package main import ( "context" "errors" "fmt" "io" "log" "net/http" "os" "time" "github.com/docker/go-connections/nat" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" ) var ( Unauthorized = errors.New("unauthorized") Unauthenticated = errors.New("unauthenticated") UnknownAuth = errors.New("unknown authentication method") ContainerNotFound = errors.New("openpod not found") USER = "DUMMY_USER" PASSWORD = "DUMMY_PASSWORD" PORT = "8081" dockerClient *client.Client baseImage = "openpod/open-pod" timeout = time.Minute ) func init() { var err error dockerClient, err = client.NewEnvClient() if err != nil { log.Fatalln(err) } user, ok := os.LookupEnv("OPENPORTAL_USER") if !ok { log.Print("WARNING: DEFAULT USER") } else { USER = user } pass, ok := os.LookupEnv("OPENPORTAL_PASS") if !ok { log.Print("WARNING: DEFAULT PASSWORD") } else { PASSWORD = pass } port, ok := os.LookupEnv("OPENPORTAL_PORT") if !ok { log.Print("WARNING: DEFAULT PORT") } else { PORT = port } } func verifyAuthentication(r *http.Request) error { user, password, ok := r.BasicAuth() if !ok { return Unauthenticated } if user != USER || password != PASSWORD { return Unauthorized } return nil } func loginMiddleware(w http.ResponseWriter, r *http.Request) error { err := verifyAuthentication(r) switch err { case Unauthorized: http.Error(w, fmt.Sprint(err), http.StatusUnauthorized) ip := r.Header.Get("X-Forwarded-For") if ip == "" { ip = r.RemoteAddr } log.Print("Failed auth from: ", ip) return Unauthorized case Unauthenticated: w.Header().Set("www-authenticate", "Basic realm=\"OPENPOD\"") http.Error(w, fmt.Sprint(err), http.StatusUnauthorized) return Unauthenticated } return nil } func manageHandler(w http.ResponseWriter, r *http.Request) { err := loginMiddleware(w, r) if err != nil { return } fmt.Fprint(w, ` OpenPOD Management

OpenPOD Management

`) } func cmdHandler(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } r.ParseForm() newVersion := r.FormValue("version_tag") if newVersion == "" { newVersion = "latest" } err := updateOpenPODVersion(newVersion, w) if err != nil { http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) return } fmt.Fprint(w, "\n\n\nSuccess") } func dockerPull(version string, out io.Writer) error { ctx := context.Background() image := fmt.Sprintf("docker.io/%s:%s", baseImage, version) log.Println("Image to be pulled:", image) reader, err := dockerClient.ImagePull(ctx, image, types.ImagePullOptions{}) if err != nil { return err } io.Copy(out, reader) return nil } func dockerStart(version string) error { ctx := context.Background() hostConfig := &container.HostConfig{ PortBindings: nat.PortMap{ "8080/tcp": []nat.PortBinding{ { HostIP: "0.0.0.0", HostPort: "8080", }, }, }, } config := &container.Config{ Image: fmt.Sprintf("docker.io/%s:%s", baseImage, version), Env: []string{ "APP_HOST=localhost", "APP_PORT=8080", "APP_SCHEME=https", "SECRET_KEY_BASE=\"SWl0xVj8AVXoc2G0eUk6VfeOd/lppjkaKbiHWs4ucxAUJ8+wzAEa4bMo0ZVjtVVk\"", }, ExposedPorts: nat.PortSet{ "8080/tcp": struct{}{}, }, } resp, err := dockerClient.ContainerCreate(ctx, config, hostConfig, nil, "openpod") if err != nil { return err } return dockerClient.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}) } func dockerStop() error { ctx := context.Background() containers, err := dockerClient.ContainerList(ctx, types.ContainerListOptions{All: true}) if err != nil { return err } for _, container := range containers { if isIn("/openpod", container.Names) { err = dockerClient.ContainerStop(ctx, container.ID, &timeout) if err != nil { return err } return dockerClient.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{Force: true}) } } return ContainerNotFound } func isIn(t string, l []string) bool { for _, el := range l { if el == t { return true } } return false } func updateOpenPODVersion(newVersion string, out io.Writer) error { var err error log.Println("New OpenPOD version:", newVersion) err = dockerPull(newVersion, out) if err != nil { log.Print("Pull failed") return err } err = dockerStop() switch err { case nil: case ContainerNotFound: log.Println("WARNING: OpenPOD was not running") default: log.Println("Stop failed") return err } err = dockerStart(newVersion) if err != nil { log.Println("Start failed:", err) } return err } func main() { http.HandleFunc("/", manageHandler) http.HandleFunc("/cmd", cmdHandler) bindAddr := fmt.Sprintf(":%s", PORT) log.Println("Starting on", bindAddr) log.Fatal(http.ListenAndServe(bindAddr, nil)) }