Skip to content

Commit 5d24f0a

Browse files
committed
fix: do not error if failed to push image (#390)
Relates to #385 This commit adds an option ENVBUILDER_EXIT_ON_PUSH_FAILURE that controls the previous behaviour of exiting with an error if ENVBUILDER_PUSH_IMAGE was set to a truthy value and we failed to push the image. - Before, Envbuilder would always exit with an error on a failed push. - Now, Envbuilder will only exit with an error if ENVBUILDER_EXIT_ON_PUSH_FAILURE is set to a truthy value. - Otherwise, Envbuilder will continue building the image and executing ENVBUILDER_INIT_SCRIPT even if push fails. (cherry picked from commit b18cd0e)
1 parent dc6aa54 commit 5d24f0a

File tree

5 files changed

+71
-6
lines changed

5 files changed

+71
-6
lines changed

docs/env-variables.md

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
| `--docker-config-base64` | `ENVBUILDER_DOCKER_CONFIG_BASE64` | | The base64 encoded Docker config file that will be used to pull images from private container registries. When this is set, Docker configuration set via the DOCKER_CONFIG environment variable is ignored. |
1919
| `--fallback-image` | `ENVBUILDER_FALLBACK_IMAGE` | | Specifies an alternative image to use when neither an image is declared in the devcontainer.json file nor a Dockerfile is present. If there's a build failure (from a faulty Dockerfile) or a misconfiguration, this image will be the substitute. Set ExitOnBuildFailure to true to halt the container if the build faces an issue. |
2020
| `--exit-on-build-failure` | `ENVBUILDER_EXIT_ON_BUILD_FAILURE` | | Terminates the container upon a build failure. This is handy when preferring the FALLBACK_IMAGE in cases where no devcontainer.json or image is provided. However, it ensures that the container stops if the build process encounters an error. |
21+
| `--exit-on-push-failure` | `ENVBUILDER_EXIT_ON_PUSH_FAILURE` | | ExitOnPushFailure terminates the container upon a push failure. This is useful if failure to push the built image should abort execution and result in an error. |
2122
| `--force-safe` | `ENVBUILDER_FORCE_SAFE` | | Ignores any filesystem safety checks. This could cause serious harm to your system! This is used in cases where bypass is needed to unblock customers. |
2223
| `--insecure` | `ENVBUILDER_INSECURE` | | Bypass TLS verification when cloning and pulling from container registries. |
2324
| `--ignore-paths` | `ENVBUILDER_IGNORE_PATHS` | | The comma separated list of paths to ignore when building the workspace. |

envbuilder.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -582,10 +582,13 @@ func run(ctx context.Context, opts options.Options, execArgs *execArgsInfo) erro
582582
endStage("🏗️ Built image!")
583583
if opts.PushImage {
584584
endStage = startStage("🏗️ Pushing image...")
585-
if err := executor.DoPush(image, kOpts); err != nil {
585+
if err := executor.DoPush(image, kOpts); err == nil {
586+
endStage("🏗️ Pushed image!")
587+
} else if !opts.ExitOnPushFailure {
588+
endStage("⚠️️ Failed to push image!")
589+
} else {
586590
return nil, xerrors.Errorf("do push: %w", err)
587591
}
588-
endStage("🏗️ Pushed image!")
589592
}
590593

591594
return image, err

integration/integration_test.go

+48-4
Original file line numberDiff line numberDiff line change
@@ -1879,9 +1879,10 @@ RUN date --utc > /root/date.txt`, testImageAlpine),
18791879
_, err = remote.Image(ref, remoteAuthOpt)
18801880
require.ErrorContains(t, err, "NAME_UNKNOWN", "expected image to not be present before build + push")
18811881

1882-
// When: we run envbuilder with PUSH_IMAGE set
1882+
// When: we run envbuilder with PUSH_IMAGE and EXIT_ON_PUSH_FAILURE set
18831883
_, err = runEnvbuilder(t, runOpts{env: append(opts,
18841884
envbuilderEnv("PUSH_IMAGE", "1"),
1885+
envbuilderEnv("EXIT_ON_PUSH_FAILURE", "1"),
18851886
)})
18861887
// Then: it should fail with an Unauthorized error
18871888
require.ErrorContains(t, err, "401 Unauthorized", "expected unauthorized error using no auth when cache repo requires it")
@@ -2074,7 +2075,7 @@ RUN date --utc > /root/date.txt`, testImageAlpine),
20742075
require.ErrorContains(t, err, "--cache-repo must be set when using --push-image")
20752076
})
20762077

2077-
t.Run("PushErr", func(t *testing.T) {
2078+
t.Run("PushErr/ExitOnPushFail", func(t *testing.T) {
20782079
t.Parallel()
20792080

20802081
srv := gittest.CreateGitServer(t, gittest.Options{
@@ -2104,12 +2105,50 @@ RUN date --utc > /root/date.txt`, testImageAlpine),
21042105
envbuilderEnv("GIT_URL", srv.URL),
21052106
envbuilderEnv("CACHE_REPO", notRegURL),
21062107
envbuilderEnv("PUSH_IMAGE", "1"),
2108+
envbuilderEnv("EXIT_ON_PUSH_FAILURE", "1"),
21072109
}})
21082110

21092111
// Then: envbuilder should fail with a descriptive error
21102112
require.ErrorContains(t, err, "failed to push to destination")
21112113
})
21122114

2115+
t.Run("PushErr/NoExitOnPushFail", func(t *testing.T) {
2116+
t.Parallel()
2117+
2118+
srv := gittest.CreateGitServer(t, gittest.Options{
2119+
Files: map[string]string{
2120+
".devcontainer/Dockerfile": fmt.Sprintf(`FROM %s
2121+
USER root
2122+
ARG WORKDIR=/
2123+
WORKDIR $WORKDIR
2124+
ENV FOO=bar
2125+
RUN echo $FOO > /root/foo.txt
2126+
RUN date --utc > /root/date.txt`, testImageAlpine),
2127+
".devcontainer/devcontainer.json": `{
2128+
"name": "Test",
2129+
"build": {
2130+
"dockerfile": "Dockerfile"
2131+
},
2132+
}`,
2133+
},
2134+
})
2135+
2136+
// Given: registry is not set up (in this case, not a registry)
2137+
notRegSrv := httptest.NewServer(http.NotFoundHandler())
2138+
notRegURL := strings.TrimPrefix(notRegSrv.URL, "http://") + "/test"
2139+
2140+
// When: we run envbuilder with PUSH_IMAGE set
2141+
_, err := runEnvbuilder(t, runOpts{env: []string{
2142+
envbuilderEnv("GIT_URL", srv.URL),
2143+
envbuilderEnv("CACHE_REPO", notRegURL),
2144+
envbuilderEnv("PUSH_IMAGE", "1"),
2145+
envbuilderEnv("EXIT_ON_PUSH_FAILURE", "0"),
2146+
}})
2147+
2148+
// Then: envbuilder should not fail
2149+
require.NoError(t, err)
2150+
})
2151+
21132152
t.Run("CacheAndPushDevcontainerFeatures", func(t *testing.T) {
21142153
t.Parallel()
21152154

@@ -2351,8 +2390,13 @@ func pushImage(t *testing.T, ref name.Reference, remoteOpt remote.Option, env ..
23512390
if remoteOpt != nil {
23522391
remoteOpts = append(remoteOpts, remoteOpt)
23532392
}
2354-
2355-
_, err := runEnvbuilder(t, runOpts{env: append(env, envbuilderEnv("PUSH_IMAGE", "1"))})
2393+
opts := runOpts{
2394+
env: append(env,
2395+
envbuilderEnv("PUSH_IMAGE", "1"),
2396+
envbuilderEnv("EXIT_ON_PUSH_FAILURE", "1"),
2397+
),
2398+
}
2399+
_, err := runEnvbuilder(t, opts)
23562400
require.NoError(t, err, "envbuilder push image failed")
23572401

23582402
img, err := remote.Image(ref, remoteOpts...)

options/options.go

+12
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ type Options struct {
7878
// devcontainer.json or image is provided. However, it ensures that the
7979
// container stops if the build process encounters an error.
8080
ExitOnBuildFailure bool
81+
// ExitOnPushFailure terminates the container upon a push failure. This is
82+
// useful if failure to push the built image should abort execution
83+
// and result in an error.
84+
ExitOnPushFailure bool
8185
// ForceSafe ignores any filesystem safety checks. This could cause serious
8286
// harm to your system! This is used in cases where bypass is needed to
8387
// unblock customers.
@@ -301,6 +305,14 @@ func (o *Options) CLI() serpent.OptionSet {
301305
"no devcontainer.json or image is provided. However, it ensures " +
302306
"that the container stops if the build process encounters an error.",
303307
},
308+
{
309+
Flag: "exit-on-push-failure",
310+
Env: WithEnvPrefix("EXIT_ON_PUSH_FAILURE"),
311+
Value: serpent.BoolOf(&o.ExitOnPushFailure),
312+
Description: "ExitOnPushFailure terminates the container upon a push failure. " +
313+
"This is useful if failure to push the built image should abort execution " +
314+
"and result in an error.",
315+
},
304316
{
305317
Flag: "force-safe",
306318
Env: WithEnvPrefix("FORCE_SAFE"),

options/testdata/options.golden

+5
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ OPTIONS:
6262
image is provided. However, it ensures that the container stops if the
6363
build process encounters an error.
6464

65+
--exit-on-push-failure bool, $ENVBUILDER_EXIT_ON_PUSH_FAILURE
66+
ExitOnPushFailure terminates the container upon a push failure. This
67+
is useful if failure to push the built image should abort execution
68+
and result in an error.
69+
6570
--export-env-file string, $ENVBUILDER_EXPORT_ENV_FILE
6671
Optional file path to a .env file where envbuilder will dump
6772
environment variables from devcontainer.json and the built container

0 commit comments

Comments
 (0)