diff --git a/docker.go b/docker.go index a96227b..f74c122 100644 --- a/docker.go +++ b/docker.go @@ -16,6 +16,7 @@ import ( "strconv" "strings" "sync" + "syscall" "time" "github.com/docker/docker/api/types" @@ -26,17 +27,59 @@ import ( "github.com/docker/docker/client" "github.com/docker/go-connections/nat" "github.com/gpmd/filehelper" + "github.com/mitchellh/go-ps" ) +type Mode string + +var ModeDocker = Mode("docker") +var ModeStatic = Mode("static") + // Docker is base struct for the app type Docker struct { cli *client.Client list []types.Container + mode Mode } // Run starts the created container func (d *Docker) Run(ctx context.Context, image, imageurl string, labels map[string]string, conf map[string][]string) string { - + log.Println("Running", image, imageurl, labels, conf) + if d.mode == ModeStatic { + rpipe, _, err := os.Pipe() + E(err) + nullFile, err := os.Open(os.DevNull) + E(err) + wd, err := os.Getwd() + E(err) + attr := &os.ProcAttr{ + Dir: wd, + Env: os.Environ(), + Files: []*os.File{ + rpipe, // (0) stdin + os.Stdout, // (1) stdout + os.Stderr, // (2) stderr + nullFile, // (3) dup on fd 0 after initialization + }, + Sys: &syscall.SysProcAttr{ + //Chroot: d.Chroot, + Credential: nil, + Setsid: true, + }, + } + child, err := os.StartProcess("/usr/local/bin/"+conf["command"][0], conf["command"], attr) + E(err) + defer rpipe.Close() + log.Printf("PID: %d", child.Pid) + time.Sleep(3 * time.Second) + p, err := ps.FindProcess(child.Pid) + E(err) + if p != nil { + log.Printf("Process still there: %v", p) + return "" + } + panic("Process died") + } if imageurl != "" { reader, err := d.cli.ImagePull(ctx, imageurl, types.ImagePullOptions{}) if err != nil { @@ -152,6 +195,18 @@ func (d *Docker) Run(ctx context.Context, image, imageurl string, labels map[str // List lists all containers func (d *Docker) List() []types.Container { + if d.mode == ModeStatic { + d.list = []types.Container{} + plist, err := ps.Processes() + E(err) + for _, p := range plist { + d.list = append(d.list, types.Container{ + Names: []string{p.Executable()}, + ID: strconv.Itoa(p.Pid()), + }) + } + return d.list + } containers, err := d.cli.ContainerList(context.Background(), types.ContainerListOptions{}) E(err) d.list = []types.Container{} @@ -163,6 +218,19 @@ func (d *Docker) List() []types.Container { // StopContainer stops container by container ID func (d *Docker) StopContainer(ctx context.Context, containerID string) { + if d.mode == ModeStatic { + pid, err := strconv.Atoi(containerID) + E(err) + p, err := os.FindProcess(pid) + E(err) + err = p.Kill() + E(err) + psp, err := ps.FindProcess(pid) + if psp != nil { + log.Printf("Can't kill process") + } + return + } err := d.cli.ContainerStop(ctx, containerID, nil) E(err) } @@ -278,7 +346,11 @@ func walkFnClosure(src string, tw *tar.Writer, buf *bytes.Buffer) filepath.WalkF } // BuildDockerImage builds a docker container using `conf` -func BuildDockerImage(ctx context.Context, conf map[string]string, cli APIClient) (string, error) { +func (d *Docker) BuildDockerImage(ctx context.Context, conf map[string]string) (string, error) { + log.Println("Building", conf) + if d.mode == ModeStatic { + return "", nil + } dockerFilePath := "./Dockerfile" dockerFileReader, err := os.Open(dockerFilePath) @@ -329,8 +401,8 @@ func BuildDockerImage(ctx context.Context, conf map[string]string, cli APIClient return "", fmt.Errorf("unable to walk user provided context path %v: %v", UserProvidedContextPath, err) } dockerFileTarReader := bytes.NewReader(buf.Bytes()) - log.Printf("building '%s'...\n", imageName) - imageBuildResponse, err := cli.ImageBuild( + log.Printf("Building '%s'...\n", imageName) + imageBuildResponse, err := d.cli.ImageBuild( ctx, dockerFileTarReader, types.ImageBuildOptions{ diff --git a/go.mod b/go.mod index e08d3f9..50cda30 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/go-chi/chi v4.1.1+incompatible github.com/gpmd/filehelper v0.3.0 + github.com/mitchellh/go-ps v1.0.0 github.com/moby/term v0.0.0-20200507201656-73f35e472e8f github.com/opencontainers/go-digest v1.0.0-rc1 // indirect github.com/shoobyban/slog v0.0.0-20190209173919-7f513f7a44c1 diff --git a/go.sum b/go.sum index e36ddaf..74c159b 100644 --- a/go.sum +++ b/go.sum @@ -80,6 +80,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= +github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/term v0.0.0-20200507201656-73f35e472e8f h1:FQQ9Wo/j3IZrVSv8RkGZoeYMuec0xAoSNijF1UqEgB4= diff --git a/main.go b/main.go index fd5ad4e..3423298 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "flag" "fmt" "log" "os" @@ -26,24 +27,48 @@ func E(err error) { } func main() { - log.Println("Version 0.1.1") + log.Println("Version 0.1.2") dockerconf := map[string][]string{} conf := map[string]string{} labelconf := map[string]string{} - cli, err := client.NewEnvClient() - E(err) + + fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError) + var mode string + fs.StringVar(&mode, "mode", "docker", "Mode: docker (default), static") + err := fs.Parse(os.Args[1:]) + if err != nil { + panic(err) + } + // log.Printf("Flags: %s %v", mode, fs) + env := os.Environ() + for _, e := range env { + if strings.HasPrefix(e, "PATH=") { + log.Printf("Env: %s", e) + } + } + + var d Docker ctx := context.Background() - d := Docker{cli: cli} - if len(os.Args) > 1 { - switch os.Args[1] { - case "dashboard": - server(ctx, d, conf) + switch mode { + case "docker": + cli, err := client.NewEnvClient() + E(err) + d = Docker{mode: ModeDocker, cli: cli} + case "static": + d = Docker{mode: ModeStatic} + } + + if len(fs.Args()) > 0 { + switch fs.Args()[0] { case "start": traefik(ctx, d, dockerconf) + default: + log.Printf("can't process %v", fs.Args()) } return } + // deployment command (with docker) is default (legacy systems) log.Println("Reading configuration...") viper.SetConfigName("config") viper.AddConfigPath(".") // optionally look for config in the working directory @@ -80,7 +105,7 @@ func main() { log.Println("Creating docker container...") - name, err := BuildDockerImage(ctx, conf, d.cli) + name, err := d.BuildDockerImage(ctx, conf) E(err) d.Run(ctx, name, "", labelconf, dockerconf) diff --git a/server.go b/server.go deleted file mode 100644 index 8e21f39..0000000 --- a/server.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "net/http" - "strings" - - "github.com/go-chi/chi" - "github.com/go-chi/chi/middleware" - "github.com/shoobyban/slog" -) - -func server(ctx context.Context, d Docker, conf map[string]string) { - r := chi.NewRouter() - r.Use(middleware.RequestID) - r.Use(middleware.Logger) - r.Use(middleware.Recoverer) - - r.Get("/running", func(w http.ResponseWriter, r *http.Request) { - var list []string - for _, v := range d.List() { - list = append(list, strings.TrimPrefix(v.Names[0], "/")) - } - w.Write([]byte(strings.Join(list, "\n"))) - }) - r.Get("/details", func(w http.ResponseWriter, r *http.Request) { - b, _ := json.MarshalIndent(d.List(), "", " ") - w.Write(b) - }) - r.Handle("/", http.FileServer(http.Dir("./webroot/"))) - - slog.Infof("Listening on http://localhost:%s/", conf["port"]) - http.ListenAndServe(":"+conf["port"], r) -} diff --git a/traefik.go b/traefik.go index a9a5f64..ed6a632 100644 --- a/traefik.go +++ b/traefik.go @@ -3,12 +3,10 @@ package main import ( "context" "flag" - "io/ioutil" "log" "os" "strings" - "github.com/gpmd/filehelper" "github.com/shoobyban/slog" ) @@ -39,41 +37,38 @@ func traefik(ctx context.Context, d Docker, dockerconf map[string][]string) { panic(err) } - t, err := filehelper.ProcessTemplateFile("traefik.toml.template", dockerconf) - if err != nil { - panic(err) - } - os.MkdirAll("server", 0755) - err = ioutil.WriteFile("server/traefik.toml", t, 0755) - if err != nil { - panic(err) - } var id string for _, s := range d.List() { - if s.Image == "traefik:latest" || strings.HasPrefix(s.Names[0], "/traefik_") { - slog.Infof("names %v", s.Names) + if s.Image == "traefik:latest" || strings.HasPrefix(s.Names[0], "/traefik_") || s.Names[0] == "traefik" { + slog.Infof("found ID:%s %s", s.ID, s.Names[0]) id = s.ID break } } + if id != "" { d.StopContainer(ctx, id) } dockerconf["command"] = []string{ "traefik", - "--providers.docker=true", "--global.checknewversion=false", "--global.sendanonymoususage=false", "--log.level=DEBUG", "--api=true", "--api.insecure=true", } - dockerconf["networks"] = append(dockerconf["networks"], "traefik") - dockerconf["mounts"] = append(dockerconf["mounts"], "/var/run/docker.sock:/var/run/docker.sock") - dockerconf["ports"] = append(dockerconf["ports"], "8080:8080") - for _, p := range ports { - dockerconf["ports"] = append(dockerconf["ports"], p+":"+p) - dockerconf["command"] = append(dockerconf["command"], "--entrypoints.port"+p+".address=:"+p) + switch d.mode { + case ModeDocker: + dockerconf["command"] = append(dockerconf["command"], "--providers.docker=true") + dockerconf["mounts"] = append(dockerconf["mounts"], "/var/run/docker.sock:/var/run/docker.sock") + dockerconf["networks"] = append(dockerconf["networks"], "traefik") + dockerconf["ports"] = append(dockerconf["ports"], "8080:8080") + for _, p := range ports { + dockerconf["ports"] = append(dockerconf["ports"], p+":"+p) + dockerconf["command"] = append(dockerconf["command"], "--entrypoints.port"+p+".address=:"+p) + } + case ModeStatic: + dockerconf["command"] = append(dockerconf["command"], "--providers.redis=true") } for _, p := range tlsports { dockerconf["ports"] = append(dockerconf["ports"], p+":"+p) @@ -111,6 +106,11 @@ func traefik(ctx context.Context, d Docker, dockerconf map[string][]string) { dockerconf["command"] = append(dockerconf["command"], "--entrypoints.web.port"+parts[0]+".redirections.entrypoint.to=port"+parts[1]) dockerconf["command"] = append(dockerconf["command"], "--entrypoints.web.port"+parts[0]+".redirections.entrypoint.scheme=https") } - slog.Infof("conf: %v", dockerconf) - d.Run(ctx, "traefik:latest", "docker.io/library/traefik:latest", map[string]string{}, dockerconf) + + switch d.mode { + case ModeDocker: + d.Run(ctx, "traefik:latest", "docker.io/library/traefik:latest", map[string]string{}, dockerconf) + case ModeStatic: + d.Run(ctx, "traefik", "", map[string]string{}, dockerconf) + } } diff --git a/webroot/index.html b/webroot/index.html deleted file mode 100644 index 531b516..0000000 --- a/webroot/index.html +++ /dev/null @@ -1,78 +0,0 @@ - - - -
- -Command: {{v.Command}}
-