Skip to content
This repository was archived by the owner on Mar 16, 2024. It is now read-only.

Commit bc2a23d

Browse files
authored
Prevent implicit Docker Hub resolution when auto-upgrade is turned on (#1845)
Signed-off-by: Grant Linville <[email protected]>
1 parent 7de6a4f commit bc2a23d

File tree

23 files changed

+137
-53
lines changed

23 files changed

+137
-53
lines changed

integration/build/build_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ func TestBuildNestedAcornWithLocalImage(t *testing.T) {
241241
}
242242

243243
// build the Nginx image
244-
source := imagesource.NewImageSource("./testdata/nested/nginx.Acornfile", []string{}, []string{}, []string{})
244+
source := imagesource.NewImageSource("./testdata/nested/nginx.Acornfile", []string{}, []string{}, []string{}, false)
245245
image, _, err := source.GetImageAndDeployArgs(helper.GetCTX(t), c)
246246
if err != nil {
247247
t.Fatal(err)

integration/dev/dev_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func TestDev(t *testing.T) {
6060
eg := errgroup.Group{}
6161
eg.Go(func() error {
6262
return dev.Dev(subCtx, helper.BuilderClient(t, ns.Name), &dev.Options{
63-
ImageSource: imagesource.NewImageSource(acornCueFile, []string{tmp}, nil, nil),
63+
ImageSource: imagesource.NewImageSource(acornCueFile, []string{tmp}, nil, nil, false),
6464
Run: client.AppRunOptions{
6565
Name: "test-app",
6666
},

integration/run/run_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/acorn-io/runtime/pkg/appdefinition"
2020
"github.com/acorn-io/runtime/pkg/client"
2121
"github.com/acorn-io/runtime/pkg/config"
22+
"github.com/acorn-io/runtime/pkg/imagesource"
2223
kclient "github.com/acorn-io/runtime/pkg/k8sclient"
2324
"github.com/acorn-io/runtime/pkg/labels"
2425
"github.com/acorn-io/runtime/pkg/run"
@@ -1698,3 +1699,55 @@ func TestAutoUpgradeImageValidation(t *testing.T) {
16981699
}
16991700
assert.ErrorContains(t, err, "could not find local image for myimage:latest - if you are trying to use a remote image, specify the full registry")
17001701
}
1702+
1703+
func TestAutoUpgradeLocalImage(t *testing.T) {
1704+
ctx := helper.GetCTX(t)
1705+
1706+
helper.StartController(t)
1707+
restConfig, err := restconfig.New(scheme.Scheme)
1708+
if err != nil {
1709+
t.Fatal("error while getting rest config:", err)
1710+
}
1711+
kclient := helper.MustReturn(kclient.Default)
1712+
project := helper.TempProject(t, kclient)
1713+
1714+
c, err := client.New(restConfig, project.Name, project.Name)
1715+
if err != nil {
1716+
t.Fatal(err)
1717+
}
1718+
1719+
// Attempt to run an auto-upgrade app with a non-existent local image. Should get an error.
1720+
_, err = c.AppRun(ctx, "mylocalimage", &client.AppRunOptions{
1721+
AutoUpgrade: &[]bool{true}[0],
1722+
})
1723+
if err == nil {
1724+
t.Fatalf("expected to get a not found error, instead got %v", err)
1725+
}
1726+
assert.ErrorContains(t, err, "could not find local image for mylocalimage - if you are trying to use a remote image, specify the full registry")
1727+
1728+
// Next, build the local image
1729+
image, err := c.AcornImageBuild(ctx, "./testdata/named/Acornfile", &client.AcornImageBuildOptions{})
1730+
if err != nil {
1731+
t.Fatal(err)
1732+
}
1733+
1734+
// Tag the image
1735+
err = c.ImageTag(ctx, image.ID, "mylocalimage")
1736+
if err != nil {
1737+
t.Fatal(err)
1738+
}
1739+
1740+
// Deploy the app
1741+
imageSource := imagesource.NewImageSource("", []string{"mylocalimage"}, []string{}, nil, true)
1742+
appImage, _, err := imageSource.GetImageAndDeployArgs(ctx, c)
1743+
if err != nil {
1744+
t.Fatal(err)
1745+
}
1746+
1747+
_, err = c.AppRun(ctx, appImage, &client.AppRunOptions{
1748+
AutoUpgrade: &[]bool{true}[0],
1749+
})
1750+
if err != nil {
1751+
t.Fatal(err)
1752+
}
1753+
}

pkg/apis/api.acorn.io/v1/types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ type ImageDetails struct {
208208
DeployArgs v1.GenericMap `json:"deployArgs,omitempty"`
209209
Profiles []string `json:"profiles,omitempty"`
210210
Auth *RegistryAuth `json:"auth,omitempty"`
211+
// NoDefaultRegistry - if true, do not assume a default registry on the image if none is specified
212+
NoDefaultRegistry bool `json:"noDefaultRegistry,omitempty"`
211213

212214
// Output Params
213215
AppImage v1.AppImage `json:"appImage,omitempty"`

pkg/apis/internal.acorn.io/v1/appinstance.go

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -172,22 +172,20 @@ func (a AppInstanceStatus) GetDevMode() bool {
172172
}
173173

174174
type AppInstanceStatus struct {
175-
DevSession *DevSessionInstanceSpec `json:"devSession,omitempty"`
176-
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
177-
ObservedImageDigest string `json:"observedImageDigest,omitempty"`
178-
Columns AppColumns `json:"columns,omitempty"`
179-
Ready bool `json:"ready,omitempty"`
180-
Namespace string `json:"namespace,omitempty"`
181-
AppImage AppImage `json:"appImage,omitempty"`
182-
AvailableAppImage string `json:"availableAppImage,omitempty"`
183-
AvailableAppImageRemote bool `json:"availableAppImageRemote,omitempty"`
184-
ConfirmUpgradeAppImage string `json:"confirmUpgradeAppImage,omitempty"`
185-
ConfirmUpgradeAppImageRemote bool `json:"confirmUpgradeAppImageRemote,omitempty"`
186-
AppSpec AppSpec `json:"appSpec,omitempty"`
187-
AppStatus AppStatus `json:"appStatus,omitempty"`
188-
Scheduling map[string]Scheduling `json:"scheduling,omitempty"`
189-
Conditions []Condition `json:"conditions,omitempty"`
190-
Defaults Defaults `json:"defaults,omitempty"`
175+
DevSession *DevSessionInstanceSpec `json:"devSession,omitempty"`
176+
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
177+
ObservedImageDigest string `json:"observedImageDigest,omitempty"`
178+
Columns AppColumns `json:"columns,omitempty"`
179+
Ready bool `json:"ready,omitempty"`
180+
Namespace string `json:"namespace,omitempty"`
181+
AppImage AppImage `json:"appImage,omitempty"`
182+
AvailableAppImage string `json:"availableAppImage,omitempty"`
183+
ConfirmUpgradeAppImage string `json:"confirmUpgradeAppImage,omitempty"`
184+
AppSpec AppSpec `json:"appSpec,omitempty"`
185+
AppStatus AppStatus `json:"appStatus,omitempty"`
186+
Scheduling map[string]Scheduling `json:"scheduling,omitempty"`
187+
Conditions []Condition `json:"conditions,omitempty"`
188+
Defaults Defaults `json:"defaults,omitempty"`
191189
}
192190

193191
type Defaults struct {

pkg/autoupgrade/daemon.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,6 @@ func (d *daemon) refreshImages(ctx context.Context, apps map[kclient.ObjectKey]v
209209
// This satisfies the usecase of autoUpgrade with an app's tag is something static, like "latest"
210210
// However, if the tag is a pattern and the current image has no tag, we don't want to check for a digest because this would
211211
// result in a digest upgrade even though no tag matched.
212-
var remote bool
213212
if !updated && (!isPattern || current.Identifier() != "") {
214213
nextAppImage = imageKey.image
215214
var pullErr error
@@ -222,8 +221,6 @@ func (d *daemon) refreshImages(ctx context.Context, apps map[kclient.ObjectKey]v
222221
if localDigest, ok, _ := d.client.resolveLocalTag(ctx, app.Namespace, imageKey.image); ok && localDigest != "" {
223222
digest = localDigest
224223
}
225-
} else {
226-
remote = true
227224
}
228225

229226
if digest == "" && pullErr != nil {
@@ -252,18 +249,14 @@ func (d *daemon) refreshImages(ctx context.Context, apps map[kclient.ObjectKey]v
252249
continue
253250
}
254251
app.Status.AvailableAppImage = nextAppImage
255-
app.Status.AvailableAppImageRemote = remote
256252
app.Status.ConfirmUpgradeAppImage = ""
257-
app.Status.ConfirmUpgradeAppImageRemote = false
258253
case "notify":
259254
if app.Status.ConfirmUpgradeAppImage == nextAppImage {
260255
d.appKeysPrevCheck[appKey] = updateTime
261256
continue
262257
}
263258
app.Status.ConfirmUpgradeAppImage = nextAppImage
264-
app.Status.ConfirmUpgradeAppImageRemote = remote
265259
app.Status.AvailableAppImage = ""
266-
app.Status.AvailableAppImageRemote = false
267260
default:
268261
logrus.Warnf("Unrecognized auto-upgrade mode %v for %v", mode, app.Name)
269262
continue

pkg/cli/build.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func (s *Build) Run(cmd *cobra.Command, args []string) error {
4444
return err
4545
}
4646

47-
helper := imagesource.NewImageSource(s.File, args, s.Profile, s.Platform)
47+
helper := imagesource.NewImageSource(s.File, args, s.Profile, s.Platform, false)
4848
image, _, err := helper.GetImageAndDeployArgs(cmd.Context(), c)
4949
if err != nil {
5050
return err

pkg/cli/dev.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func (s *Dev) Run(cmd *cobra.Command, args []string) error {
5757
return err
5858
}
5959

60-
imageSource := imagesource.NewImageSource(s.File, args, s.Profile, nil)
60+
imageSource := imagesource.NewImageSource(s.File, args, s.Profile, nil, s.AutoUpgrade != nil && *s.AutoUpgrade)
6161

6262
opts, err := s.ToOpts()
6363
if err != nil {

pkg/cli/render.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func (s *Render) Run(cmd *cobra.Command, args []string) error {
3232
return err
3333
}
3434

35-
imageAndArgs := imagesource.NewImageSource(s.File, args, s.Profile, nil)
35+
imageAndArgs := imagesource.NewImageSource(s.File, args, s.Profile, nil, false)
3636

3737
appDef, _, err := imageAndArgs.GetAppDefinition(cmd.Context(), c)
3838
if err != nil {

pkg/cli/run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ func (s *Run) Run(cmd *cobra.Command, args []string) (err error) {
229229
}
230230

231231
var (
232-
imageSource = imagesource.NewImageSource(s.File, args, s.Profile, nil)
232+
imageSource = imagesource.NewImageSource(s.File, args, s.Profile, nil, s.AutoUpgrade != nil && *s.AutoUpgrade)
233233
app *apiv1.App
234234
updated bool
235235
)

pkg/client/client.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,8 @@ type ImageDetailsOptions struct {
309309
Profiles []string
310310
DeployArgs map[string]any
311311
Auth *apiv1.RegistryAuth
312+
// NoDefaultRegistry - if true, indicates that no default container registry should be assumed when getting image details
313+
NoDefaultRegistry bool
312314
}
313315

314316
type ImageDeleteOptions struct {

pkg/client/image.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ func (c *DefaultClient) ImageDetails(ctx context.Context, imageName string, opts
4242
detailsResult.Profiles = opts.Profiles
4343
detailsResult.NestedDigest = opts.NestedDigest
4444
detailsResult.Auth = opts.Auth
45+
detailsResult.NoDefaultRegistry = opts.NoDefaultRegistry
4546
}
4647

4748
err := c.RESTClient.Post().

pkg/controller/appdefinition/pullappimage.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,14 @@ func pullAppImage(transport http.RoundTripper, client pullClient) router.Handler
5555
return nil
5656
}
5757

58-
// Skip the attempt to locally resolve if we already know that the image will be remote
5958
var (
6059
_, autoUpgradeOn = autoupgrade.Mode(appInstance.Spec)
6160
resolved string
6261
err error
6362
isLocal bool
6463
)
65-
if !appInstance.Status.AvailableAppImageRemote {
64+
// Only attempt to resolve locally if auto-upgrade is not on, or if auto-upgrade is on but we know the image is not remote.
65+
if !autoUpgradeOn || !images.IsImageRemote(target, true, remote.WithTransport(transport)) {
6666
resolved, isLocal, err = client.resolve(req.Ctx, req.Client, appInstance.Namespace, target)
6767
if err != nil {
6868
cond.Error(err)

pkg/controller/appdefinition/pullappimage_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ func testRecordPullEvent(t *testing.T, testName string, appInstance *v1.AppInsta
247247
return nil
248248
}
249249

250-
handler := pullAppImage(nil, pullClient{
250+
handler := pullAppImage(mockRoundTripper{}, pullClient{
251251
recorder: event.RecorderFunc(fakeRecorder),
252252
resolve: resolve,
253253
pull: pull,

pkg/imagedetails/imagedetails.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
kclient "sigs.k8s.io/controller-runtime/pkg/client"
1818
)
1919

20-
func GetImageDetails(ctx context.Context, c kclient.Client, namespace, imageName string, profiles []string, deployArgs map[string]any, nested string, opts ...remote.Option) (*apiv1.ImageDetails, error) {
20+
func GetImageDetails(ctx context.Context, c kclient.Client, namespace, imageName string, profiles []string, deployArgs map[string]any, nested string, noDefaultReg bool, opts ...remote.Option) (*apiv1.ImageDetails, error) {
2121
imageName = strings.ReplaceAll(imageName, "+", "/")
2222
name := strings.ReplaceAll(imageName, "/", "+")
2323

@@ -43,7 +43,7 @@ func GetImageDetails(ctx context.Context, c kclient.Client, namespace, imageName
4343
err := c.Get(ctx, router.Key(namespace, name), image)
4444
if err != nil && !apierror.IsNotFound(err) {
4545
return nil, err
46-
} else if err != nil && apierror.IsNotFound(err) && tags.IsLocalReference(name) {
46+
} else if err != nil && apierror.IsNotFound(err) && (tags.IsLocalReference(name) || (noDefaultReg && tags.HasNoSpecifiedRegistry(imageName))) {
4747
return nil, err
4848
} else if err == nil {
4949
namespace = image.Namespace

pkg/images/operations.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,29 @@ func ResolveTag(tag imagename.Reference, image string) string {
116116
return image
117117
}
118118

119+
// IsImageRemote checks the remote registry to see if the given image name exists.
120+
// If noDefaultRegistry is true, and the image does not have a specified registry, this function will return false
121+
// without attempting to check any remote registries.
122+
func IsImageRemote(image string, noDefaultRegistry bool, opts ...remote.Option) bool {
123+
var (
124+
ref imagename.Reference
125+
err error
126+
)
127+
if noDefaultRegistry {
128+
ref, err = imagename.ParseReference(image, imagename.WithDefaultRegistry(NoDefaultRegistry))
129+
} else {
130+
ref, err = imagename.ParseReference(image)
131+
}
132+
133+
if err != nil || ref.Context().RegistryStr() == NoDefaultRegistry {
134+
return false
135+
}
136+
137+
_, err = remote.Index(ref, opts...)
138+
139+
return err == nil
140+
}
141+
119142
func pullIndex(tag imagename.Reference, opts []remote.Option) (*v1.AppImage, error) {
120143
img, err := remote.Index(tag, opts...)
121144
if err != nil {

pkg/imagesource/helper.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strings"
99

1010
"github.com/acorn-io/runtime/pkg/appdefinition"
11+
"github.com/acorn-io/runtime/pkg/autoupgrade"
1112
"github.com/acorn-io/runtime/pkg/build"
1213
"github.com/acorn-io/runtime/pkg/client"
1314
"github.com/acorn-io/runtime/pkg/config"
@@ -21,13 +22,20 @@ type ImageSource struct {
2122
Args []string
2223
Profiles []string
2324
Platforms []string
25+
// NoDefaultRegistry - if true, indicates that no container registry should be assumed for the Image.
26+
// This is used if the ImageSource is for an app with auto-upgrade enabled.
27+
NoDefaultRegistry bool
2428
}
2529

26-
func NewImageSource(file string, args, profiles, platforms []string) (result ImageSource) {
30+
func NewImageSource(file string, args, profiles, platforms []string, noDefaultReg bool) (result ImageSource) {
2731
result.File = file
2832
result.Image, result.Args = splitImageAndArgs(args)
2933
result.Profiles = profiles
3034
result.Platforms = platforms
35+
36+
// If the image is a pattern, auto-upgrade is on, so assume no default registry
37+
_, isPattern := autoupgrade.AutoUpgradePattern(result.Image)
38+
result.NoDefaultRegistry = noDefaultReg || isPattern
3139
return
3240
}
3341

@@ -64,7 +72,7 @@ func (i ImageSource) GetAppDefinition(ctx context.Context, c client.Client) (*ap
6472
)
6573
if file == "" {
6674
sourceName = image
67-
imageDetails, err := c.ImageDetails(ctx, image, nil)
75+
imageDetails, err := c.ImageDetails(ctx, image, &client.ImageDetailsOptions{NoDefaultRegistry: i.NoDefaultRegistry})
6876
if err != nil {
6977
return nil, nil, err
7078
}

pkg/imagesource/platforms_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func TestParamsHelp(t *testing.T) {
2424
"--i-default=3",
2525
"--complex",
2626
"@testdata/params/test.cue",
27-
}, nil, nil).GetAppDefinition(context.Background(), nil)
27+
}, nil, nil, false).GetAppDefinition(context.Background(), nil)
2828
assert.Equal(t, pflag.ErrHelp, err)
2929
}
3030

@@ -42,7 +42,7 @@ func TestParams(t *testing.T) {
4242
"--i-default=3",
4343
"--complex",
4444
"@testdata/params/test.cue",
45-
}, nil, nil).GetAppDefinition(context.Background(), nil)
45+
}, nil, nil, false).GetAppDefinition(context.Background(), nil)
4646
if err != nil {
4747
t.Fatal(err)
4848
}

pkg/openapi/generated/openapi_generated.go

Lines changed: 7 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/server/registry/apigroups/acorn/apps/confirmupgrade.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ func (s *ConfirmUpgradeStrategy) Create(ctx context.Context, obj types.Object) (
3939
return nil, err
4040
}
4141
app.Status.AvailableAppImage = app.Status.ConfirmUpgradeAppImage
42-
app.Status.AvailableAppImageRemote = app.Status.ConfirmUpgradeAppImageRemote
4342

4443
err = s.client.Status().Update(ctx, app)
4544
if err != nil {

0 commit comments

Comments
 (0)