From bb2e6c72d2fb3f1b64755bdf6d6269dbc6767f87 Mon Sep 17 00:00:00 2001 From: Josh Hawn Date: Fri, 24 Jul 2015 16:35:11 -0700 Subject: [PATCH] [api/client] Tag resolved digest from Dockerfile Builds where the base images have been resolved to trusted digest references will now be tagged with the original tag reference from the Dockerfile on a successful build. Docker-DCO-1.1-Signed-off-by: Josh Hawn (github: jlhawn) --- api/client/build.go | 50 +++++++++++++++++++----- integration-cli/docker_cli_build_test.go | 11 +++++- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/api/client/build.go b/api/client/build.go index 54e76521b7..a28b6a87f4 100644 --- a/api/client/build.go +++ b/api/client/build.go @@ -115,8 +115,9 @@ func (cli *DockerCli) CmdBuild(args ...string) error { } // Resolve the FROM lines in the Dockerfile to trusted digest references - // using Notary. - newDockerfile, err := rewriteDockerfileFrom(filepath.Join(contextDir, relDockerfile), cli.trustedReference) + // using Notary. On a successful build, we must tag the resolved digests + // to the original name specified in the Dockerfile. + newDockerfile, resolvedTags, err := rewriteDockerfileFrom(filepath.Join(contextDir, relDockerfile), cli.trustedReference) if err != nil { return fmt.Errorf("unable to process Dockerfile: %v", err) } @@ -291,7 +292,20 @@ func (cli *DockerCli) CmdBuild(args ...string) error { } return Cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code} } - return err + + if err != nil { + return err + } + + // Since the build was successful, now we must tag any of the resolved + // images from the above Dockerfile rewrite. + for _, resolved := range resolvedTags { + if err := cli.tagTrusted(resolved.repoInfo, resolved.digestRef, resolved.tagRef); err != nil { + return err + } + } + + return nil } // getDockerfileRelPath uses the given context directory for a `docker build` @@ -483,14 +497,21 @@ func (td *trustedDockerfile) Close() error { return os.Remove(td.File.Name()) } +// resolvedTag records the repository, tag, and resolved digest reference +// from a Dockerfile rewrite. +type resolvedTag struct { + repoInfo *registry.RepositoryInfo + digestRef, tagRef registry.Reference +} + // rewriteDockerfileFrom rewrites the given Dockerfile by resolving images in // "FROM " instructions to a digest reference. `translator` is a // function that takes a repository name and tag reference and returns a // trusted digest reference. -func rewriteDockerfileFrom(dockerfileName string, translator func(string, registry.Reference) (registry.Reference, error)) (newDockerfile *trustedDockerfile, err error) { +func rewriteDockerfileFrom(dockerfileName string, translator func(string, registry.Reference) (registry.Reference, error)) (newDockerfile *trustedDockerfile, resolvedTags []*resolvedTag, err error) { dockerfile, err := os.Open(dockerfileName) if err != nil { - return nil, fmt.Errorf("unable to open Dockerfile: %v", err) + return nil, nil, fmt.Errorf("unable to open Dockerfile: %v", err) } defer dockerfile.Close() @@ -499,7 +520,7 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(string, regist // Make a tempfile to store the rewritten Dockerfile. tempFile, err := ioutil.TempFile("", "trusted-dockerfile-") if err != nil { - return nil, fmt.Errorf("unable to make temporary trusted Dockerfile: %v", err) + return nil, nil, fmt.Errorf("unable to make temporary trusted Dockerfile: %v", err) } trustedFile := &trustedDockerfile{ @@ -525,21 +546,32 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(string, regist if tag == "" { tag = tags.DEFAULTTAG } + + repoInfo, err := registry.ParseRepositoryInfo(repo) + if err != nil { + return nil, nil, fmt.Errorf("unable to parse repository info: %v", err) + } + ref := registry.ParseReference(tag) if !ref.HasDigest() && isTrusted() { trustedRef, err := translator(repo, ref) if err != nil { - return nil, err + return nil, nil, err } line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", trustedRef.ImageName(repo))) + resolvedTags = append(resolvedTags, &resolvedTag{ + repoInfo: repoInfo, + digestRef: trustedRef, + tagRef: ref, + }) } } n, err := fmt.Fprintln(tempFile, line) if err != nil { - return nil, err + return nil, nil, err } trustedFile.size += int64(n) @@ -547,7 +579,7 @@ func rewriteDockerfileFrom(dockerfileName string, translator func(string, regist tempFile.Seek(0, os.SEEK_SET) - return trustedFile, scanner.Err() + return trustedFile, resolvedTags, scanner.Err() } // replaceDockerfileTarWrapper wraps the given input tar archive stream and diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 9bba032542..24009113de 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -5370,8 +5370,15 @@ func (s *DockerTrustSuite) TestTrustedBuild(c *check.C) { c.Fatalf("Unexpected output on trusted build:\n%s", out) } - // Build command does not create untrusted tag - //dockerCmd(c, "rmi", repoName) + // We should also have a tag reference for the image. + if out, exitCode := dockerCmd(c, "inspect", repoName); exitCode != 0 { + c.Fatalf("unexpected exit code inspecting image %q: %d: %s", repoName, exitCode, out) + } + + // We should now be able to remove the tag reference. + if out, exitCode := dockerCmd(c, "rmi", repoName); exitCode != 0 { + c.Fatalf("unexpected exit code inspecting image %q: %d: %s", repoName, exitCode, out) + } } func (s *DockerTrustSuite) TestTrustedBuildUntrustedTag(c *check.C) {