From 81c6de6cb3678dbae61f50583b0896cba38dc460 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Thu, 22 Dec 2016 18:10:45 +0100 Subject: [PATCH 1/4] Ability to dockerize a Beego application This introduces a new command "dockerize" which will generate a Dockerfile to allow a Beego Web Application to run inside Docker. --- bee.go | 1 + dockerize.go | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 dockerize.go diff --git a/bee.go b/bee.go index ca1e25e..21fa1d0 100644 --- a/bee.go +++ b/bee.go @@ -126,6 +126,7 @@ var availableCommands = []*Command{ //cmdRundocs, cmdMigrate, cmdFix, + cmdDockerize, } var logger = GetBeeLogger(os.Stdout) diff --git a/dockerize.go b/dockerize.go new file mode 100644 index 0000000..6043648 --- /dev/null +++ b/dockerize.go @@ -0,0 +1,112 @@ +// Copyright 2016 bee authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package main + +import ( + "flag" + "os" + "path" + "path/filepath" + "strings" + "text/template" +) + +var cmdDockerize = &Command{ + CustomFlags: true, + UsageLine: "dockerize", + Short: "Generates a Dockerfile for your Beego application", + Long: `Dockerize generates a Dockerfile for your Beego Web Application. + The Dockerfile will compile, get the dependencies with {{"godep"|bold}}, and set the entrypoint. + `, + PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, + Run: dockerizeApp, +} + +const dockerBuildTemplate = `FROM library/golang:latest + +# Get Beego Web Framework +RUN go get github.com/astaxie/beego + +# Godep for vendoring +RUN go get github.com/tools/godep + +# Recompile the standard library without CGO +RUN CGO_ENABLED=0 go install -a std + +ENV APP_DIR $GOPATH{{.Appdir}} +RUN mkdir -p $APP_DIR + +# Set the entrypoint +ENTRYPOINT $APP_DIR/{{.Entrypoint}} +ADD . $APP_DIR + +# Compile the binary and statically link +RUN cd $APP_DIR +RUN CGO_ENABLED=0 godep go build -ldflags '-d -w -s' + +EXPOSE {{.Expose}} +` + +type Dockerfile struct { + Appdir string + Entrypoint string + Expose string +} + +var ( + expose string +) + +func init() { + fs := flag.NewFlagSet("dockerize", flag.ContinueOnError) + fs.StringVar(&expose, "expose", "8080", "Port to expose in the Docker container.") + cmdDockerize.Flag = *fs +} + +func dockerizeApp(cmd *Command, args []string) int { + cmd.Flag.Parse(args) + + logger.Info("Generating Dockerfile...") + + gopath := os.Getenv("GOPATH") + dir, err := filepath.Abs(".") + MustCheck(err) + + appdir := strings.Replace(dir, gopath, "", 1) + + _, entrypoint := path.Split(appdir) + dockerfile := Dockerfile{ + Appdir: appdir, + Entrypoint: entrypoint, + Expose: expose, + } + + generateDockerfile(dockerfile) + return 0 +} + +func generateDockerfile(df Dockerfile) { + t := template.Must(template.New("dockerBuildTemplate").Parse(dockerBuildTemplate)).Funcs(BeeFuncMap()) + + f, err := os.Create("Dockerfile") + if err != nil { + logger.Fatalf("Error writing Dockerfile: %v", err.Error()) + } + defer CloseFile(f) + + t.Execute(f, df) + + logger.Success("Dockerfile generated.") +} From 699d76bc95eb9cecc71b950672862b64396c7d6c Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Thu, 22 Dec 2016 18:29:13 +0100 Subject: [PATCH 2/4] Ability to specify the base image of the Docker container --- dockerize.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/dockerize.go b/dockerize.go index 6043648..5336de9 100644 --- a/dockerize.go +++ b/dockerize.go @@ -34,10 +34,7 @@ var cmdDockerize = &Command{ Run: dockerizeApp, } -const dockerBuildTemplate = `FROM library/golang:latest - -# Get Beego Web Framework -RUN go get github.com/astaxie/beego +const dockerBuildTemplate = `FROM {{.BaseImage}} # Godep for vendoring RUN go get github.com/tools/godep @@ -60,17 +57,20 @@ EXPOSE {{.Expose}} ` type Dockerfile struct { + BaseImage string Appdir string Entrypoint string Expose string } var ( - expose string + expose string + baseImage string ) func init() { fs := flag.NewFlagSet("dockerize", flag.ContinueOnError) + fs.StringVar(&baseImage, "image", "library/golang", "Sets the base image of the Docker container.") fs.StringVar(&expose, "expose", "8080", "Port to expose in the Docker container.") cmdDockerize.Flag = *fs } @@ -86,8 +86,10 @@ func dockerizeApp(cmd *Command, args []string) int { appdir := strings.Replace(dir, gopath, "", 1) + // TODO: Check if the base image exists in Docker Hub _, entrypoint := path.Split(appdir) dockerfile := Dockerfile{ + BaseImage: baseImage, Appdir: appdir, Entrypoint: entrypoint, Expose: expose, From 4ea1715df60961bddbc9a43a28822a91f2d69748 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Thu, 22 Dec 2016 22:53:45 +0100 Subject: [PATCH 3/4] Expose more than one port This adds the ability to expose more than one port inside the Docker container. --- dockerize.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/dockerize.go b/dockerize.go index 5336de9..6aee753 100644 --- a/dockerize.go +++ b/dockerize.go @@ -29,6 +29,9 @@ var cmdDockerize = &Command{ Short: "Generates a Dockerfile for your Beego application", Long: `Dockerize generates a Dockerfile for your Beego Web Application. The Dockerfile will compile, get the dependencies with {{"godep"|bold}}, and set the entrypoint. + + {{"Example:"|bold}} + $ bee dockerize -expose="3000,80,25" `, PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() }, Run: dockerizeApp, @@ -56,6 +59,7 @@ RUN CGO_ENABLED=0 godep go build -ldflags '-d -w -s' EXPOSE {{.Expose}} ` +// Dockerfile holds the information about the Docker container. type Dockerfile struct { BaseImage string Appdir string @@ -70,13 +74,15 @@ var ( func init() { fs := flag.NewFlagSet("dockerize", flag.ContinueOnError) - fs.StringVar(&baseImage, "image", "library/golang", "Sets the base image of the Docker container.") - fs.StringVar(&expose, "expose", "8080", "Port to expose in the Docker container.") + fs.StringVar(&baseImage, "image", "", "Sets the base image of the Docker container.") + fs.StringVar(&expose, "expose", "8080", "Port(s) to expose in the Docker container.") cmdDockerize.Flag = *fs } func dockerizeApp(cmd *Command, args []string) int { - cmd.Flag.Parse(args) + if err := cmd.Flag.Parse(args); err != nil { + logger.Fatalf("Error parsing flags: %v", err.Error()) + } logger.Info("Generating Dockerfile...") @@ -84,9 +90,19 @@ func dockerizeApp(cmd *Command, args []string) int { dir, err := filepath.Abs(".") MustCheck(err) + if len(baseImage) == 0 { + baseImage = "library/golang:latest" + } + appdir := strings.Replace(dir, gopath, "", 1) - // TODO: Check if the base image exists in Docker Hub + // In case of multiple ports to expose inside the container, + // replace all the commas with whitespaces. + // See the verb EXPOSE in the Docker documentation. + if strings.Contains(expose, ",") { + expose = strings.Replace(expose, ",", " ", -1) + } + _, entrypoint := path.Split(appdir) dockerfile := Dockerfile{ BaseImage: baseImage, From 040b160ecd16ee46361864f9594c5b85f8109720 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Fri, 23 Dec 2016 11:53:02 +0100 Subject: [PATCH 4/4] Added default base image + New layout for displaying options --- bee.go | 11 ++++------- dockerize.go | 6 +----- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/bee.go b/bee.go index 21fa1d0..268166e 100644 --- a/bee.go +++ b/bee.go @@ -99,12 +99,7 @@ func (c *Command) Options() map[string]string { c.Flag.VisitAll(func(f *flag.Flag) { defaultVal := f.DefValue if len(defaultVal) > 0 { - if strings.Contains(defaultVal, ":") { - // Truncate the flag's default value by appending '...' at the end - options[f.Name+"="+strings.Split(defaultVal, ":")[0]+":..."] = f.Usage - } else { - options[f.Name+"="+defaultVal] = f.Usage - } + options[f.Name+"="+defaultVal] = f.Usage } else { options[f.Name] = f.Usage } @@ -210,7 +205,9 @@ Use {{"bee help [topic]" | bold}} for more information about that topic. var helpTemplate = `{{"USAGE" | headline}} {{.UsageLine | printf "bee %s" | bold}} {{if .Options}}{{endline}}{{"OPTIONS" | headline}}{{range $k,$v := .Options}} - {{$k | printf "-%-12s" | bold}} {{$v}}{{end}}{{endline}}{{end}} + {{$k | printf "-%s" | bold}} + {{$v}} + {{end}}{{end}} {{"DESCRIPTION" | headline}} {{tmpltostr .Long . | trim}} ` diff --git a/dockerize.go b/dockerize.go index 6aee753..827fc8e 100644 --- a/dockerize.go +++ b/dockerize.go @@ -74,7 +74,7 @@ var ( func init() { fs := flag.NewFlagSet("dockerize", flag.ContinueOnError) - fs.StringVar(&baseImage, "image", "", "Sets the base image of the Docker container.") + fs.StringVar(&baseImage, "image", "library/golang", "Set the base image of the Docker container.") fs.StringVar(&expose, "expose", "8080", "Port(s) to expose in the Docker container.") cmdDockerize.Flag = *fs } @@ -90,10 +90,6 @@ func dockerizeApp(cmd *Command, args []string) int { dir, err := filepath.Abs(".") MustCheck(err) - if len(baseImage) == 0 { - baseImage = "library/golang:latest" - } - appdir := strings.Replace(dir, gopath, "", 1) // In case of multiple ports to expose inside the container,