diff --git a/go.mod b/go.mod index 248db8a..ff18e3b 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,13 @@ module git.abbiamoundominio.org/blallo/openportal go 1.15 -require github.com/docker/docker v1.13.1 // indirect +require ( + github.com/Microsoft/go-winio v0.4.15 // indirect + github.com/docker/distribution v2.7.1+incompatible // indirect + github.com/docker/docker v1.13.1 // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.4.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect +) diff --git a/go.sum b/go.sum index 8a8f0bf..ba3c8a1 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,35 @@ +github.com/Microsoft/go-winio v0.4.15 h1:qkLXKzb1QoVatRyd/YlXZ/Kg0m5K3SPuoD82jjSOaBc= +github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo= github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/main.go b/main.go index 128c99b..4df5c29 100644 --- a/main.go +++ b/main.go @@ -1,23 +1,59 @@ package main import ( + "context" "errors" "fmt" - //"html/template" + "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") - USER = "DUMMY_USER" - PASSWORD = "DUMMY_PASSWORD" - //dockerClient = + 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 { @@ -84,23 +120,117 @@ func cmdHandler(w http.ResponseWriter, r *http.Request) { newVersion = "latest" } - err := updateOpenPODVersion(newVersion) + err := updateOpenPODVersion(newVersion, w) if err != nil { http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) return } - fmt.Fprint(w, "Success") + fmt.Fprint(w, "\n\n\nSuccess") } -func updateOpenPODVersion(newVersion string) error { - log.Println("New OpenPOD version:", newVersion) +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) - log.Println("Starting on :8080") - log.Fatal(http.ListenAndServe(":8080", nil)) + bindAddr := fmt.Sprintf(":%s", PORT) + + log.Println("Starting on", bindAddr) + log.Fatal(http.ListenAndServe(bindAddr, nil)) }