diff --git a/envbuilder.go b/envbuilder.go index c65ae62f..6bebd2e4 100644 --- a/envbuilder.go +++ b/envbuilder.go @@ -1061,6 +1061,26 @@ func RunCacheProbe(ctx context.Context, opts options.Options) (v1.Image, error) }) } + // We expect an image built and pushed by envbuilder to have the envbuilder + // binary present at a predefined path. In order to correctly replicate the + // build via executor.RunCacheProbe we need to have the *exact* copy of the + // envbuilder binary available used to build the image. + exePath := opts.BinaryPath + // Add an exception for the current running binary in kaniko ignore list + if err := util.AddAllowedPathToDefaultIgnoreList(exePath); err != nil { + return nil, xerrors.Errorf("add exe path to ignore list: %w", err) + } + // Copy the envbuilder binary into the build context. + buildParams.DockerfileContent += fmt.Sprintf(` +COPY --chmod=0755 %s %s +USER root +WORKDIR / +ENTRYPOINT [%q]`, exePath, exePath, exePath) + dst := filepath.Join(buildParams.BuildContext, exePath) + if err := copyFile(exePath, dst); err != nil { + return nil, xerrors.Errorf("copy envbuilder binary to build context: %w", err) + } + stdoutWriter, closeStdout := log.Writer(opts.Logger) defer closeStdout() stderrWriter, closeStderr := log.Writer(opts.Logger) diff --git a/integration/integration_test.go b/integration/integration_test.go index b3fe7bef..297e86a1 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -1114,13 +1114,14 @@ RUN date --utc > /root/date.txt`, testImageAlpine), _, err = remote.Image(ref) require.ErrorContains(t, err, "MANIFEST_UNKNOWN", "expected image to not be present before build + push") - // Then: re-running envbuilder with GET_CACHED_IMAGE should succeed + // Then: re-running envbuilder with GET_CACHED_IMAGE should not succeed, as + // the envbuilder binary is not present in the pushed image. _, err = runEnvbuilder(t, runOpts{env: []string{ envbuilderEnv("GIT_URL", srv.URL), envbuilderEnv("CACHE_REPO", testRepo), envbuilderEnv("GET_CACHED_IMAGE", "1"), }}) - require.NoError(t, err) + require.ErrorContains(t, err, "uncached COPY command is not supported in cache probe mode") }) t.Run("CacheAndPush", func(t *testing.T) { diff --git a/options/defaults.go b/options/defaults.go index 18bf12a8..42e48063 100644 --- a/options/defaults.go +++ b/options/defaults.go @@ -55,4 +55,7 @@ func (o *Options) SetDefaults() { if o.WorkspaceFolder == "" { o.WorkspaceFolder = DefaultWorkspaceFolder(o.GitURL) } + if o.BinaryPath == "" { + o.BinaryPath = "/.envbuilder/bin/envbuilder" + } } diff --git a/options/defaults_test.go b/options/defaults_test.go index 156ae16b..48783585 100644 --- a/options/defaults_test.go +++ b/options/defaults_test.go @@ -85,6 +85,7 @@ func TestOptions_SetDefaults(t *testing.T) { Filesystem: chmodfs.New(osfs.New("/")), GitURL: "", WorkspaceFolder: constants.EmptyWorkspaceDir, + BinaryPath: "/.envbuilder/bin/envbuilder", } var actual options.Options diff --git a/options/options.go b/options/options.go index 3432cee5..ff6a8be3 100644 --- a/options/options.go +++ b/options/options.go @@ -156,6 +156,11 @@ type Options struct { // used to improving cache utilization when multiple users are building // working on the same repository. RemoteRepoBuildMode bool + + // BinaryPath is the path to the local envbuilder binary when + // attempting to probe the build cache. This is only relevant when + // GetCachedImage is true. + BinaryPath string } const envPrefix = "ENVBUILDER_" @@ -427,6 +432,13 @@ func (o *Options) CLI() serpent.OptionSet { Description: "Print the digest of the cached image, if available. " + "Exits with an error if not found.", }, + { + Flag: "binary-path", + Env: WithEnvPrefix("BINARY_PATH"), + Value: serpent.StringOf(&o.BinaryPath), + Hidden: true, + Description: "Specify the path to an Envbuilder binary for use when probing the build cache.", + }, { Flag: "remote-repo-build-mode", Env: WithEnvPrefix("REMOTE_REPO_BUILD_MODE"), @@ -485,6 +497,9 @@ func (o *Options) Markdown() string { _, _ = sb.WriteString("| - | - | - | - |\n") for _, opt := range cliOptions { + if opt.Hidden { + continue + } d := opt.Default if d != "" { d = "`" + d + "`"