1
0
mirror of https://github.com/docker/cli.git synced 2026-01-13 18:22:35 +03:00

Merge pull request #6195 from thaJeztah/build_no_dct

build: remove DCT support for classic builder
This commit is contained in:
Sebastiaan van Stijn
2025-07-21 22:03:46 +02:00
committed by GitHub
9 changed files with 2 additions and 222 deletions

View File

@@ -1,8 +1,6 @@
package image
import (
"archive/tar"
"bufio"
"bytes"
"context"
"encoding/json"
@@ -20,9 +18,7 @@ import (
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/command/image/build"
"github.com/docker/cli/cli/streams"
"github.com/docker/cli/cli/trust"
"github.com/docker/cli/internal/jsonstream"
"github.com/docker/cli/internal/lazyregexp"
"github.com/docker/cli/opts"
buildtypes "github.com/docker/docker/api/types/build"
"github.com/docker/docker/api/types/container"
@@ -65,7 +61,6 @@ type buildOptions struct {
target string
imageIDFile string
platform string
untrusted bool
}
// dockerfileFromStdin returns true when the user specified that the Dockerfile
@@ -144,7 +139,8 @@ func NewBuildCommand(dockerCli command.Cli) *cobra.Command {
flags.SetAnnotation("target", annotation.ExternalURL, []string{"https://docs.docker.com/reference/cli/docker/buildx/build/#target"})
flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file")
command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled())
flags.Bool("disable-content-trust", dockerCli.ContentTrustEnabled(), "Skip image verification (deprecated)")
_ = flags.MarkHidden("disable-content-trust")
flags.StringVar(&options.platform, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable")
flags.SetAnnotation("platform", "version", []string{"1.38"})
@@ -286,26 +282,6 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
var resolvedTags []*resolvedTag
if !options.untrusted {
translator := func(ctx context.Context, ref reference.NamedTagged) (reference.Canonical, error) {
return TrustedReference(ctx, dockerCli, ref)
}
// if there is a tar wrapper, the dockerfile needs to be replaced inside it
if buildCtx != nil {
// Wrap the tar archive to replace the Dockerfile entry with the rewritten
// Dockerfile which uses trusted pulls.
buildCtx = replaceDockerfileForContentTrust(ctx, buildCtx, relDockerfile, translator, &resolvedTags)
} else if dockerfileCtx != nil {
// if there was not archive context still do the possible replacements in Dockerfile
newDockerfile, _, err := rewriteDockerfileFromForContentTrust(ctx, dockerfileCtx, translator)
if err != nil {
return err
}
dockerfileCtx = io.NopCloser(bytes.NewBuffer(newDockerfile))
}
}
if options.compress {
buildCtx, err = build.Compress(buildCtx)
if err != nil {
@@ -402,21 +378,10 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
return err
}
}
if !options.untrusted {
// 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 := trust.TagTrusted(ctx, dockerCli.Client(), dockerCli.Err(), resolved.digestRef, resolved.tagRef); err != nil {
return err
}
}
}
return nil
}
type translatorFunc func(context.Context, reference.NamedTagged) (reference.Canonical, error)
// validateTag checks if the given image name can be resolved.
func validateTag(rawRepo string) (string, error) {
_, err := reference.ParseNormalizedNamed(rawRepo)
@@ -427,118 +392,6 @@ func validateTag(rawRepo string) (string, error) {
return rawRepo, nil
}
var dockerfileFromLinePattern = lazyregexp.New(`(?i)^[\s]*FROM[ \f\r\t\v]+(?P<image>[^ \f\r\t\v\n#]+)`)
// resolvedTag records the repository, tag, and resolved digest reference
// from a Dockerfile rewrite.
type resolvedTag struct {
digestRef reference.Canonical
tagRef reference.NamedTagged
}
// noBaseImageSpecifier is the symbol used by the FROM
// command to specify that no base image is to be used.
const noBaseImageSpecifier = "scratch"
// rewriteDockerfileFromForContentTrust rewrites the given Dockerfile by resolving images in
// "FROM <image>" instructions to a digest reference. `translator` is a
// function that takes a repository name and tag reference and returns a
// trusted digest reference.
// This should be called *only* when content trust is enabled
func rewriteDockerfileFromForContentTrust(ctx context.Context, dockerfile io.Reader, translator translatorFunc) (newDockerfile []byte, resolvedTags []*resolvedTag, err error) {
scanner := bufio.NewScanner(dockerfile)
buf := bytes.NewBuffer(nil)
// Scan the lines of the Dockerfile, looking for a "FROM" line.
for scanner.Scan() {
line := scanner.Text()
matches := dockerfileFromLinePattern.FindStringSubmatch(line)
if matches != nil && matches[1] != noBaseImageSpecifier {
// Replace the line with a resolved "FROM repo@digest"
var ref reference.Named
ref, err = reference.ParseNormalizedNamed(matches[1])
if err != nil {
return nil, nil, err
}
ref = reference.TagNameOnly(ref)
if ref, ok := ref.(reference.NamedTagged); ok {
trustedRef, err := translator(ctx, ref)
if err != nil {
return nil, nil, err
}
line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, "FROM "+reference.FamiliarString(trustedRef))
resolvedTags = append(resolvedTags, &resolvedTag{
digestRef: trustedRef,
tagRef: ref,
})
}
}
_, err := fmt.Fprintln(buf, line)
if err != nil {
return nil, nil, err
}
}
return buf.Bytes(), resolvedTags, scanner.Err()
}
// replaceDockerfileForContentTrust wraps the given input tar archive stream and
// uses the translator to replace the Dockerfile which uses a trusted reference.
// Returns a new tar archive stream with the replaced Dockerfile.
func replaceDockerfileForContentTrust(ctx context.Context, inputTarStream io.ReadCloser, dockerfileName string, translator translatorFunc, resolvedTags *[]*resolvedTag) io.ReadCloser {
pipeReader, pipeWriter := io.Pipe()
go func() {
tarReader := tar.NewReader(inputTarStream)
tarWriter := tar.NewWriter(pipeWriter)
defer inputTarStream.Close()
for {
hdr, err := tarReader.Next()
if err == io.EOF {
// Signals end of archive.
_ = tarWriter.Close()
_ = pipeWriter.Close()
return
}
if err != nil {
_ = pipeWriter.CloseWithError(err)
return
}
content := io.Reader(tarReader)
if hdr.Name == dockerfileName {
// This entry is the Dockerfile. Since the tar archive was
// generated from a directory on the local filesystem, the
// Dockerfile will only appear once in the archive.
var newDockerfile []byte
newDockerfile, *resolvedTags, err = rewriteDockerfileFromForContentTrust(ctx, content, translator)
if err != nil {
_ = pipeWriter.CloseWithError(err)
return
}
hdr.Size = int64(len(newDockerfile))
content = bytes.NewBuffer(newDockerfile)
}
if err := tarWriter.WriteHeader(hdr); err != nil {
_ = pipeWriter.CloseWithError(err)
return
}
if _, err := io.Copy(tarWriter, content); err != nil {
_ = pipeWriter.CloseWithError(err)
return
}
}
}()
return pipeReader
}
func imageBuildOptions(dockerCli command.Cli, options buildOptions) buildtypes.ImageBuildOptions {
configFile := dockerCli.ConfigFile()
return buildtypes.ImageBuildOptions{

View File

@@ -47,7 +47,6 @@ func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) {
options.compress = true
options.dockerfileName = "-"
options.context = dir.Path()
options.untrusted = true
assert.NilError(t, runBuild(context.TODO(), cli, options))
expected := []string{fakeBuild.options.Dockerfile, ".dockerignore", "foo"}
@@ -74,7 +73,6 @@ func TestRunBuildResetsUidAndGidInContext(t *testing.T) {
options := newBuildOptions()
options.context = dir.Path()
options.untrusted = true
assert.NilError(t, runBuild(context.TODO(), cli, options))
headers := fakeBuild.headers(t)
@@ -109,7 +107,6 @@ COPY data /data
options := newBuildOptions()
options.context = dir.Path()
options.dockerfileName = df.Path()
options.untrusted = true
assert.NilError(t, runBuild(context.TODO(), cli, options))
expected := []string{fakeBuild.options.Dockerfile, ".dockerignore", "data"}
@@ -170,7 +167,6 @@ RUN echo hello world
cli := test.NewFakeCli(&fakeClient{imageBuildFunc: fakeBuild.build})
options := newBuildOptions()
options.context = tmpDir.Join("context-link")
options.untrusted = true
assert.NilError(t, runBuild(context.TODO(), cli, options))
assert.DeepEqual(t, fakeBuild.filenames(t), []string{"Dockerfile"})

View File

@@ -2803,7 +2803,6 @@ _docker_image_build() {
"
local boolean_options="
--disable-content-trust=false
--force-rm
--help
--no-cache

View File

@@ -139,7 +139,6 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l cpu-quota -d
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -s c -l cpu-shares -d 'CPU shares (relative weight)'
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l cpuset-cpus -d 'CPUs in which to allow execution (0-3, 0,1)'
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l cpuset-mems -d 'MEMs in which to allow execution (0-3, 0,1)'
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l disable-content-trust -d 'Skip image verification'
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -s f -l file -d "Name of the Dockerfile (Default is PATH/Dockerfile)"
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l force-rm -d 'Always remove intermediate containers'
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l help -d 'Print usage'

View File

@@ -1005,7 +1005,6 @@ __docker_image_subcommand() {
"($help)--cpu-rt-runtime=[Limit the CPU real-time runtime]:CPU real-time runtime in microseconds: " \
"($help)--cpuset-cpus=[CPUs in which to allow execution]:CPUs: " \
"($help)--cpuset-mems=[MEMs in which to allow execution]:MEMs: " \
"($help)--disable-content-trust[Skip image verification]" \
"($help -f --file)"{-f=,--file=}"[Name of the Dockerfile]:Dockerfile:_files" \
"($help)--force-rm[Always remove intermediate containers]" \
"($help)--isolation=[Container isolation technology]:isolation:(default hyperv process)" \

View File

@@ -21,7 +21,6 @@ Build an image from a Dockerfile
| `-c`, `--cpu-shares` | `int64` | `0` | CPU shares (relative weight) |
| `--cpuset-cpus` | `string` | | CPUs in which to allow execution (0-3, 0,1) |
| `--cpuset-mems` | `string` | | MEMs in which to allow execution (0-3, 0,1) |
| `--disable-content-trust` | `bool` | `true` | Skip image verification |
| [`-f`](https://docs.docker.com/reference/cli/docker/buildx/build/#file), [`--file`](https://docs.docker.com/reference/cli/docker/buildx/build/#file) | `string` | | Name of the Dockerfile (Default is `PATH/Dockerfile`) |
| `--force-rm` | `bool` | | Always remove intermediate containers |
| `--iidfile` | `string` | | Write the image ID to the file |

View File

@@ -21,7 +21,6 @@ Build an image from a Dockerfile
| `-c`, `--cpu-shares` | `int64` | `0` | CPU shares (relative weight) |
| `--cpuset-cpus` | `string` | | CPUs in which to allow execution (0-3, 0,1) |
| `--cpuset-mems` | `string` | | MEMs in which to allow execution (0-3, 0,1) |
| `--disable-content-trust` | `bool` | `true` | Skip image verification |
| [`-f`](https://docs.docker.com/reference/cli/docker/buildx/build/#file), [`--file`](https://docs.docker.com/reference/cli/docker/buildx/build/#file) | `string` | | Name of the Dockerfile (Default is `PATH/Dockerfile`) |
| `--force-rm` | `bool` | | Always remove intermediate containers |
| `--iidfile` | `string` | | Write the image ID to the file |

View File

@@ -21,7 +21,6 @@ Build an image from a Dockerfile
| `-c`, `--cpu-shares` | `int64` | `0` | CPU shares (relative weight) |
| `--cpuset-cpus` | `string` | | CPUs in which to allow execution (0-3, 0,1) |
| `--cpuset-mems` | `string` | | MEMs in which to allow execution (0-3, 0,1) |
| `--disable-content-trust` | `bool` | `true` | Skip image verification |
| [`-f`](https://docs.docker.com/reference/cli/docker/buildx/build/#file), [`--file`](https://docs.docker.com/reference/cli/docker/buildx/build/#file) | `string` | | Name of the Dockerfile (Default is `PATH/Dockerfile`) |
| `--force-rm` | `bool` | | Always remove intermediate containers |
| `--iidfile` | `string` | | Write the image ID to the file |

View File

@@ -14,7 +14,6 @@ import (
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/fs"
"gotest.tools/v3/icmd"
"gotest.tools/v3/skip"
)
func TestBuildFromContextDirectoryWithTag(t *testing.T) {
@@ -62,68 +61,6 @@ func TestBuildFromContextDirectoryWithTag(t *testing.T) {
})
}
func TestTrustedBuild(t *testing.T) {
skip.If(t, environment.RemoteDaemon())
t.Setenv("DOCKER_BUILDKIT", "0")
dir := fixtures.SetupConfigFile(t)
defer dir.Remove()
image1 := fixtures.CreateMaskedTrustedRemoteImage(t, registryPrefix, "trust-build1", "latest")
image2 := fixtures.CreateMaskedTrustedRemoteImage(t, registryPrefix, "trust-build2", "latest")
buildDir := fs.NewDir(t, "test-trusted-build-context-dir",
fs.WithFile("Dockerfile", fmt.Sprintf(`
FROM %s as build-base
RUN echo ok > /foo
FROM %s
COPY --from=build-base foo bar
`, image1, image2)))
defer buildDir.Remove()
result := icmd.RunCmd(
icmd.Command("docker", "build", "-t", "myimage", "."),
withWorkingDir(buildDir),
fixtures.WithConfig(dir.Path()),
fixtures.WithTrust,
fixtures.WithNotary,
)
result.Assert(t, icmd.Expected{
Out: fmt.Sprintf("FROM %s@sha", image1[:len(image1)-7]),
Err: fmt.Sprintf("Tagging %s@sha", image1[:len(image1)-7]),
})
result.Assert(t, icmd.Expected{
Out: fmt.Sprintf("FROM %s@sha", image2[:len(image2)-7]),
})
}
func TestTrustedBuildUntrustedImage(t *testing.T) {
skip.If(t, environment.RemoteDaemon())
t.Setenv("DOCKER_BUILDKIT", "0")
dir := fixtures.SetupConfigFile(t)
defer dir.Remove()
buildDir := fs.NewDir(t, "test-trusted-build-context-dir",
fs.WithFile("Dockerfile", fmt.Sprintf(`
FROM %s
RUN []
`, fixtures.AlpineImage)))
defer buildDir.Remove()
result := icmd.RunCmd(
icmd.Command("docker", "build", "-t", "myimage", "."),
withWorkingDir(buildDir),
fixtures.WithConfig(dir.Path()),
fixtures.WithTrust,
fixtures.WithNotary,
)
result.Assert(t, icmd.Expected{
ExitCode: 1,
Err: "does not have trust data for",
})
}
func TestBuildIidFileSquash(t *testing.T) {
environment.SkipIfNotExperimentalDaemon(t)
t.Setenv("DOCKER_BUILDKIT", "0")