diff --git a/docs/reference/dep-006.md b/docs/reference/dep-006.md index dd81824..22a92ec 100644 --- a/docs/reference/dep-006.md +++ b/docs/reference/dep-006.md @@ -27,12 +27,15 @@ The format of this file is as follows: watch-delay = 1 override-ports = ["8080:8080"] auto-connect = false + custom-tags = ["backend-dev"] [environments.staging] name = "example-go" namespace = "kube-system" build-tar = "build.tar.gz" chart-tar = "chart.tar.gz" + custom-tags = ["latest", "backend-staging"] + ``` Let's break it down by section: @@ -65,6 +68,7 @@ name at runtime using `draft up --environment=staging`. watch-delay = 2 override-ports = ["8080:8080", "9229:9229"] auto-connect = false + custom-tags = ["latest", "backend-staging"] ``` Here is a run-down on each of the fields: @@ -81,10 +85,12 @@ Here is a run-down on each of the fields: - `watch-delay`: the delay for local file changes to have stopped before deploying again (in seconds). - `override-ports`: the configuration to be passed to the `draft connect` command, in the format ":" - `auto-connect`: specifies whether Draft should automatically connect to the application after the deplyoment is successful. The local ports are configurable through the `override-ports` field. +- `custom-tags`: specifies the custom tags Draft will push to the container registry. Note that Draft will push and use the computed SHA of the application as the tag of your image for the Helm chart. +> Note: It is recommended to [avoid fixed image tags (like `latest`, `canary`, `dev`) in production](https://kubernetes.io/docs/concepts/configuration/overview#container-images), and Helm will not upgrade the release if the image tag is the same. > For more information on configuring `draft connect`, check [dep-007.md][dep007] -Note: All updates to the `draft.toml` will take effect the next time `draft up --environment=` is invoked _except_ the `namespace` key/value pair. Once a deployment has occurred in the original namespace, it won't be transferred over to another. +> Note: All updates to the `draft.toml` will take effect the next time `draft up --environment=` is invoked _except_ the `namespace` key/value pair. Once a deployment has occurred in the original namespace, it won't be transferred over to another. # Rationale diff --git a/examples/example-go/main.go b/examples/example-go/main.go index 3e93040..a047400 100644 --- a/examples/example-go/main.go +++ b/examples/example-go/main.go @@ -6,7 +6,7 @@ import ( ) func handler(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello World, I'm Golang!") + fmt.Fprintf(w, "Hello World, I'm Golang 123!") } func main() { diff --git a/pkg/builder/builder.go b/pkg/builder/builder.go index 35f0768..732714a 100644 --- a/pkg/builder/builder.go +++ b/pkg/builder/builder.go @@ -89,7 +89,7 @@ type AppContext struct { bldr *Builder ctx *Context buf *bytes.Buffer - tag string + tags []string img string log io.WriteCloser id string @@ -114,6 +114,11 @@ func newAppContext(b *Builder, buildCtx *Context) (*AppContext, error) { imageRepository := strings.TrimLeft(fmt.Sprintf("%s/%s", buildCtx.Env.Registry, buildCtx.Env.Name), "/") image := fmt.Sprintf("%s:%s", imageRepository, imgtag) + t := []string{image} + for _, tag := range buildCtx.Env.CustomTags { + t = append(t, fmt.Sprintf("%s:%s", imageRepository, tag)) + } + // inject certain values into the chart such as the registry location, // the application name, buildID and the application version. tplstr := "image.repository=%s,image.tag=%s,%s=%s,%s=%s" @@ -147,7 +152,7 @@ func newAppContext(b *Builder, buildCtx *Context) (*AppContext, error) { bldr: b, ctx: buildCtx, buf: buf, - tag: imgtag, + tags: t, img: image, log: logf, vals: vals, @@ -377,7 +382,7 @@ func (b *Builder) buildImg(ctx context.Context, app *AppContext, out chan<- *Sum msgc := make(chan string) errc := make(chan error) go func() { - buildopts := types.ImageBuildOptions{Tags: []string{app.img}} + buildopts := types.ImageBuildOptions{Tags: app.tags} resp, err := b.DockerClient.Client().ImageBuild(ctx, app.buf, buildopts) if err != nil { errc <- err @@ -436,27 +441,42 @@ func (b *Builder) pushImg(ctx context.Context, app *AppContext, out chan<- *Summ msgc := make(chan string, 1) errc := make(chan error, 1) + + var wg sync.WaitGroup + wg.Add(len(app.tags)) + go func() { registryAuth, err := command.RetrieveAuthTokenFromImage(ctx, b.DockerClient, app.img) if err != nil { errc <- err return } - resp, err := b.DockerClient.Client().ImagePush(ctx, app.img, types.ImagePushOptions{RegistryAuth: registryAuth}) - if err != nil { - errc <- err - return + + for _, img := range app.tags { + + go func(img string) { + defer wg.Done() + + resp, err := b.DockerClient.Client().ImagePush(ctx, img, types.ImagePushOptions{RegistryAuth: registryAuth}) + if err != nil { + errc <- err + return + } + + defer resp.Close() + outFd, isTerm := term.GetFdInfo(app.log) + if err := jsonmessage.DisplayJSONMessagesStream(resp, app.log, outFd, isTerm, nil); err != nil { + errc <- err + return + } + }(img) } + defer func() { - resp.Close() close(errc) close(msgc) }() - outFd, isTerm := term.GetFdInfo(app.log) - if err := jsonmessage.DisplayJSONMessagesStream(resp, app.log, outFd, isTerm, nil); err != nil { - errc <- err - return - } + }() for msgc != nil || errc != nil { select { @@ -477,6 +497,7 @@ func (b *Builder) pushImg(ctx context.Context, app *AppContext, out chan<- *Summ time.Sleep(time.Second) } } + wg.Wait() return nil } diff --git a/pkg/draft/manifest/manifest.go b/pkg/draft/manifest/manifest.go index 3730778..523fa06 100644 --- a/pkg/draft/manifest/manifest.go +++ b/pkg/draft/manifest/manifest.go @@ -36,6 +36,7 @@ type Environment struct { WatchDelay int `toml:"watch-delay,omitempty"` OverridePorts []string `toml:"override-ports,omitempty"` AutoConnect bool `toml:"auto-connect"` + CustomTags []string `toml:"custom-tags,omitempty"` } // New creates a new manifest with the Environments intialized. diff --git a/pkg/draft/manifest/manifest_test.go b/pkg/draft/manifest/manifest_test.go index 28786cd..3bf0544 100644 --- a/pkg/draft/manifest/manifest_test.go +++ b/pkg/draft/manifest/manifest_test.go @@ -8,7 +8,7 @@ import ( func TestNew(t *testing.T) { m := New() m.Environments[DefaultEnvironmentName].Name = "foobar" - expected := "&{foobar default [] false false 2 [] false}" + expected := "&{foobar default [] false false 2 [] false []}" actual := fmt.Sprintf("%v", m.Environments[DefaultEnvironmentName]) if expected != actual {