diff --git a/controllers/imagerepository_controller.go b/controllers/imagerepository_controller.go index a8793a51..35f28355 100644 --- a/controllers/imagerepository_controller.go +++ b/controllers/imagerepository_controller.go @@ -155,14 +155,7 @@ func (r *ImageRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Requ defer r.MetricsRecorder.RecordDuration(*objRef, reconcileStart) } - var err error - var ref name.Reference - if s := strings.Split(imageRepo.Spec.Image, "://"); len(s) > 1 { - err = fmt.Errorf(".spec.image value should not start with URL scheme; remove '%s://'", s[0]) - } else { - ref, err = name.ParseReference(imageRepo.Spec.Image) - } - + ref, err := parseImageReference(imageRepo.Spec.Image) if err != nil { imagev1.SetImageRepositoryReadiness( &imageRepo, @@ -214,6 +207,24 @@ func (r *ImageRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{RequeueAfter: when}, nil } +func parseImageReference(url string) (name.Reference, error) { + if s := strings.Split(url, "://"); len(s) > 1 { + return nil, fmt.Errorf(".spec.image value should not start with URL scheme; remove '%s://'", s[0]) + } + + ref, err := name.ParseReference(url) + if err != nil { + return nil, err + } + + imageName := strings.TrimPrefix(url, ref.Context().RegistryStr()) + if s := strings.Split(imageName, ":"); len(s) > 1 { + return nil, fmt.Errorf(".spec.image value should not contain a tag; remove ':%s'", s[1]) + } + + return ref, nil +} + // parseAwsImage returns the AWS account ID and region and `true` if // the image repository is hosted in AWS's Elastic Container Registry, // otherwise empty strings and `false`. diff --git a/controllers/scan_test.go b/controllers/scan_test.go index 6aeb838d..739d7043 100644 --- a/controllers/scan_test.go +++ b/controllers/scan_test.go @@ -342,6 +342,45 @@ func TestImageRepositoryReconciler_imageAttribute_schemePrefix(t *testing.T) { g.Expect(testEnv.Delete(ctx, &repo)).To(Succeed()) } +func TestImageRepositoryReconciler_imageAttribute_withTag(t *testing.T) { + g := NewWithT(t) + + registryServer := test.NewRegistryServer() + defer registryServer.Close() + + imgRepo, err := test.LoadImages(registryServer, "test-fetch", []string{"1.0.0"}) + g.Expect(err).ToNot(HaveOccurred()) + imgRepo = imgRepo + ":1.0.0" + + repo := imagev1.ImageRepository{ + Spec: imagev1.ImageRepositorySpec{ + Interval: metav1.Duration{Duration: reconciliationInterval}, + Image: imgRepo, + }, + } + objectName := types.NamespacedName{ + Name: "random", + Namespace: "default", + } + + repo.Name = objectName.Name + repo.Namespace = objectName.Namespace + + ctx, cancel := context.WithTimeout(context.TODO(), contextTimeout) + defer cancel() + g.Expect(testEnv.Create(ctx, &repo)).To(Succeed()) + + var ready *metav1.Condition + g.Eventually(func() bool { + _ = testEnv.Get(ctx, objectName, &repo) + ready = apimeta.FindStatusCondition(*repo.GetStatusConditions(), meta.ReadyCondition) + return ready != nil && ready.Reason == imagev1.ImageURLInvalidReason + }, timeout, interval).Should(BeTrue()) + g.Expect(ready.Message).To(ContainSubstring("should not contain a tag")) + // Cleanup. + g.Expect(testEnv.Delete(ctx, &repo)).To(Succeed()) +} + func TestImageRepositoryReconciler_imageAttribute_hostPort(t *testing.T) { g := NewWithT(t) diff --git a/docs/api/image-reflector.md b/docs/api/image-reflector.md index 4deb91f1..cf6bf1e9 100644 --- a/docs/api/image-reflector.md +++ b/docs/api/image-reflector.md @@ -490,7 +490,9 @@ It does not apply to already started scans. Defaults to false.

accessFrom
+ github.com/fluxcd/pkg/apis/acl.AccessFrom + @@ -660,7 +662,9 @@ It does not apply to already started scans. Defaults to false.

accessFrom
+ github.com/fluxcd/pkg/apis/acl.AccessFrom + diff --git a/hack/api-docs/config.json b/hack/api-docs/config.json index 295ede64..471d97bb 100644 --- a/hack/api-docs/config.json +++ b/hack/api-docs/config.json @@ -22,6 +22,10 @@ { "typeMatchPrefix": "^github.com/fluxcd/pkg/apis/meta", "docsURLTemplate": "https://godoc.org/github.com/fluxcd/pkg/apis/meta#{{ .TypeIdentifier }}" + }, + { + "typeMatchPrefix": "^github.com/fluxcd/pkg/apis/acl", + "docsURLTemplate": "https://godoc.org/github.com/fluxcd/pkg/apis/acl#{{ .TypeIdentifier }}" } ], "typeDisplayNamePrefixOverrides": {