diff --git a/api/v1beta2/imagerepository_types.go b/api/v1beta2/imagerepository_types.go index eaee2c14..5b770a1b 100644 --- a/api/v1beta2/imagerepository_types.go +++ b/api/v1beta2/imagerepository_types.go @@ -104,6 +104,11 @@ type ImageRepositorySpec struct { // +kubebuilder:default:=generic // +optional Provider string `json:"provider,omitempty"` + + // Insecure, if set to true indicates that the image registry is hosted at an + // HTTP endpoint. + // +optional + Insecure bool `json:"insecure,omitempty"` } type ScanResult struct { diff --git a/config/crd/bases/image.toolkit.fluxcd.io_imagerepositories.yaml b/config/crd/bases/image.toolkit.fluxcd.io_imagerepositories.yaml index 40075d79..44dec07f 100644 --- a/config/crd/bases/image.toolkit.fluxcd.io_imagerepositories.yaml +++ b/config/crd/bases/image.toolkit.fluxcd.io_imagerepositories.yaml @@ -315,6 +315,10 @@ spec: image: description: Image is the name of the image repository type: string + insecure: + description: Insecure, if set to true indicates that the image registry + is hosted at an HTTP endpoint. + type: boolean interval: description: Interval is the length of time to wait between scans of the image repository. diff --git a/docs/api/v1beta2/image-reflector.md b/docs/api/v1beta2/image-reflector.md index 576479fa..b215c913 100644 --- a/docs/api/v1beta2/image-reflector.md +++ b/docs/api/v1beta2/image-reflector.md @@ -543,6 +543,19 @@ string When not specified, defaults to ‘generic’.

+ + +insecure
+ +bool + + + +(Optional) +

Insecure, if set to true indicates that the image registry is hosted at an +HTTP endpoint.

+ + @@ -731,6 +744,19 @@ string When not specified, defaults to ‘generic’.

+ + +insecure
+ +bool + + + +(Optional) +

Insecure, if set to true indicates that the image registry is hosted at an +HTTP endpoint.

+ + diff --git a/docs/spec/v1beta2/imagerepositories.md b/docs/spec/v1beta2/imagerepositories.md index 12986aaa..768e878a 100644 --- a/docs/spec/v1beta2/imagerepositories.md +++ b/docs/spec/v1beta2/imagerepositories.md @@ -318,6 +318,12 @@ spec: - "1.1.1|1.0.0" ``` +### Insecure + +`.spec.insecure` is an optional field to specify that the image registry is +hosted at a non-TLS endpoint and thus the controller should use plain HTTP +requests to communicate with the registry. + ### Provider `.spec.provider` is an optional field that allows specifying an OIDC provider diff --git a/internal/controller/imagerepository_controller.go b/internal/controller/imagerepository_controller.go index 0ae58872..1e60b1cb 100644 --- a/internal/controller/imagerepository_controller.go +++ b/internal/controller/imagerepository_controller.go @@ -249,7 +249,7 @@ func (r *ImageRepositoryReconciler) reconcile(ctx context.Context, sp *patch.Ser } // Parse image reference. - ref, err := parseImageReference(obj.Spec.Image) + ref, err := parseImageReference(obj.Spec.Image, obj.Spec.Insecure) if err != nil { conditions.MarkStalled(obj, imagev1.ImageURLInvalidReason, err.Error()) result, retErr = ctrl.Result{}, nil @@ -468,7 +468,7 @@ func (r *ImageRepositoryReconciler) shouldScan(obj imagev1.ImageRepository, now // If the canonical image name of the image is different from the last // observed name, scan now. - ref, err := parseImageReference(obj.Spec.Image) + ref, err := parseImageReference(obj.Spec.Image, obj.Spec.Insecure) if err != nil { return false, scanInterval, "", err } @@ -570,13 +570,19 @@ func eventLogf(ctx context.Context, r kuberecorder.EventRecorder, obj runtime.Ob } // parseImageReference parses the given URL into a container registry repository -// reference. -func parseImageReference(url string) (name.Reference, error) { +// reference. If insecure is set to true, then the registry is deemed to be +// located at an HTTP endpoint. +func parseImageReference(url string, insecure bool) (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) + var opts []name.Option + if insecure { + opts = append(opts, name.Insecure) + } + + ref, err := name.ParseReference(url, opts...) if err != nil { return nil, err } diff --git a/internal/controller/imagerepository_controller_test.go b/internal/controller/imagerepository_controller_test.go index df25ac43..141adfbd 100644 --- a/internal/controller/imagerepository_controller_test.go +++ b/internal/controller/imagerepository_controller_test.go @@ -580,7 +580,7 @@ func TestImageRepositoryReconciler_scan(t *testing.T) { repo.SetAnnotations(map[string]string{meta.ReconcileRequestAnnotation: tt.annotation}) } - ref, err := parseImageReference(imgRepo) + ref, err := parseImageReference(imgRepo, false) g.Expect(err).ToNot(HaveOccurred()) opts := []remote.Option{} @@ -656,12 +656,13 @@ func TestGetLatestTags(t *testing.T) { } } -func TestParseImageReference(t *testing.T) { +func Test_parseImageReference(t *testing.T) { tests := []struct { - name string - url string - wantErr bool - wantRef string + name string + url string + insecure bool + wantErr bool + wantRef string }{ { name: "simple valid url", @@ -684,16 +685,27 @@ func TestParseImageReference(t *testing.T) { wantErr: false, wantRef: "example.com:9999/foo/bar", }, + { + name: "with insecure registry", + url: "example.com/foo/bar", + insecure: true, + wantRef: "example.com/foo/bar", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) - ref, err := parseImageReference(tt.url) - g.Expect(err != nil).To(Equal(tt.wantErr)) - if err == nil { + ref, err := parseImageReference(tt.url, tt.insecure) + if tt.wantErr { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).ToNot(HaveOccurred()) g.Expect(ref.String()).To(Equal(tt.wantRef)) + if tt.insecure { + g.Expect(ref.Context().Registry.Scheme()).To(Equal("http")) + } } }) }