Skip to content

Commit 4cba91f

Browse files
authored
fix: do not error if failed to push image (#390) (#423)
1 parent 532c639 commit 4cba91f

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
@@ -581,10 +581,13 @@ func run(ctx context.Context, opts options.Options, execArgs *execArgsInfo) erro
581581
endStage("🏗️ Built image!")
582582
if opts.PushImage {
583583
endStage = startStage("🏗️ Pushing image...")
584-
if err := executor.DoPush(image, kOpts); err != nil {
584+
if err := executor.DoPush(image, kOpts); err == nil {
585+
endStage("🏗️ Pushed image!")
586+
} else if !opts.ExitOnPushFailure {
587+
endStage("⚠️️ Failed to push image!")
588+
} else {
585589
return nil, xerrors.Errorf("do push: %w", err)
586590
}
587-
endStage("🏗️ Pushed image!")
588591
}
589592

590593
return image, err

integration/integration_test.go

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

1885-
// When: we run envbuilder with PUSH_IMAGE set
1885+
// When: we run envbuilder with PUSH_IMAGE and EXIT_ON_PUSH_FAILURE set
18861886
_, err = runEnvbuilder(t, runOpts{env: append(opts,
18871887
envbuilderEnv("PUSH_IMAGE", "1"),
1888+
envbuilderEnv("EXIT_ON_PUSH_FAILURE", "1"),
18881889
)})
18891890
// Then: it should fail with an Unauthorized error
18901891
require.ErrorContains(t, err, "401 Unauthorized", "expected unauthorized error using no auth when cache repo requires it")
@@ -2077,7 +2078,7 @@ RUN date --utc > /root/date.txt`, testImageAlpine),
20772078
require.ErrorContains(t, err, "--cache-repo must be set when using --push-image")
20782079
})
20792080

2080-
t.Run("PushErr", func(t *testing.T) {
2081+
t.Run("PushErr/ExitOnPushFail", func(t *testing.T) {
20812082
t.Parallel()
20822083

20832084
srv := gittest.CreateGitServer(t, gittest.Options{
@@ -2107,12 +2108,50 @@ RUN date --utc > /root/date.txt`, testImageAlpine),
21072108
envbuilderEnv("GIT_URL", srv.URL),
21082109
envbuilderEnv("CACHE_REPO", notRegURL),
21092110
envbuilderEnv("PUSH_IMAGE", "1"),
2111+
envbuilderEnv("EXIT_ON_PUSH_FAILURE", "1"),
21102112
}})
21112113

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

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

@@ -2354,8 +2393,13 @@ func pushImage(t *testing.T, ref name.Reference, remoteOpt remote.Option, env ..
23542393
if remoteOpt != nil {
23552394
remoteOpts = append(remoteOpts, remoteOpt)
23562395
}
2357-
2358-
_, err := runEnvbuilder(t, runOpts{env: append(env, envbuilderEnv("PUSH_IMAGE", "1"))})
2396+
opts := runOpts{
2397+
env: append(env,
2398+
envbuilderEnv("PUSH_IMAGE", "1"),
2399+
envbuilderEnv("EXIT_ON_PUSH_FAILURE", "1"),
2400+
),
2401+
}
2402+
_, err := runEnvbuilder(t, opts)
23592403
require.NoError(t, err, "envbuilder push image failed")
23602404

23612405
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)