diff --git a/api/v1/coherence_webhook.go b/api/v1/coherence_webhook.go index 7bde9e46..1f6a7c57 100644 --- a/api/v1/coherence_webhook.go +++ b/api/v1/coherence_webhook.go @@ -8,8 +8,10 @@ package v1 import ( "fmt" + cncf "github.com/distribution/distribution/reference" "github.com/go-test/deep" "github.com/oracle/coherence-operator/pkg/operator" + "github.com/pkg/errors" appsv1 "k8s.io/api/apps/v1" apiequality "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/runtime" @@ -136,6 +138,10 @@ func (in *Coherence) ValidateCreate() error { return err } err = commonWebHook.validateNodePorts(in) + if err != nil { + return err + } + err = commonWebHook.validateImageNames(in) return err } @@ -156,6 +162,9 @@ func (in *Coherence) ValidateUpdate(previous runtime.Object) error { if err := commonWebHook.validateNodePorts(in); err != nil { return err } + if err := commonWebHook.validateImageNames(in); err != nil { + return err + } var errorList field.ErrorList sts := in.Spec.CreateStatefulSet(in) @@ -246,6 +255,27 @@ func (in *CommonWebHook) validateNodePorts(current CoherenceResource) error { return nil } +// validateImageName validates a container image name +func (in *CommonWebHook) validateImageNames(c CoherenceResource) error { + cohImage := c.GetSpec().GetCoherenceImage() + if cohImage != nil { + _, err := cncf.Parse(*cohImage) + if err != nil { + return errors.Wrap(err, "error validating Coherence image name") + } + } + + utilsImage := c.GetSpec().GetUtilsImage() + if utilsImage != "" { + _, err := cncf.Parse(utilsImage) + if err != nil { + return errors.Wrap(err, "error validating utils image name") + } + } + + return nil +} + // ValidateStatefulSetUpdate tests if required fields in the StatefulSet are set. func ValidateStatefulSetUpdate(statefulSet, oldStatefulSet *appsv1.StatefulSet) field.ErrorList { var allErrs field.ErrorList diff --git a/api/v1/coherence_webhook_test.go b/api/v1/coherence_webhook_test.go index 1ee1d14e..29a51264 100644 --- a/api/v1/coherence_webhook_test.go +++ b/api/v1/coherence_webhook_test.go @@ -755,3 +755,35 @@ func TestValidateNodePortsOnUpdateWithInvalidPort(t *testing.T) { err := current.ValidateUpdate(prev) g.Expect(err).To(HaveOccurred()) } + +func TestValidateInvalidCoherenceImage(t *testing.T) { + g := NewGomegaWithT(t) + + current := &coh.Coherence{ + Spec: coh.CoherenceStatefulSetResourceSpec{ + CoherenceResourceSpec: coh.CoherenceResourceSpec{ + Image: pointer.String("foo/BaD iMaGeNaMe:latest"), // images must be lowercase without spaces + }, + }, + } + + err := current.ValidateCreate() + g.Expect(err).To(HaveOccurred()) +} + +func TestValidateInvalidUtilsImage(t *testing.T) { + g := NewGomegaWithT(t) + + current := &coh.Coherence{ + Spec: coh.CoherenceStatefulSetResourceSpec{ + CoherenceResourceSpec: coh.CoherenceResourceSpec{ + CoherenceUtils: &coh.ImageSpec{ + Image: pointer.String("foo/BaD iMaGeNaMe:latest"), // images must be lowercase without spaces + }, + }, + }, + } + + err := current.ValidateCreate() + g.Expect(err).To(HaveOccurred()) +} diff --git a/api/v1/coherenceresourcespec_types.go b/api/v1/coherenceresourcespec_types.go index 6ecb081e..230dfc45 100644 --- a/api/v1/coherenceresourcespec_types.go +++ b/api/v1/coherenceresourcespec_types.go @@ -912,15 +912,20 @@ func (in *CoherenceResourceSpec) UpdateDefaultLivenessProbeAction(probe *corev1. return probe } -// CreateOperatorInitContainer creates the Operator init-container spec. -func (in *CoherenceResourceSpec) CreateOperatorInitContainer(deployment CoherenceResource) corev1.Container { +// GetUtilsImage returns the name of the utils image to use. +func (in *CoherenceResourceSpec) GetUtilsImage() string { var image string if in.CoherenceUtils == nil || in.CoherenceUtils.Image == nil { image = operator.GetDefaultOperatorImage() } else { image = *in.CoherenceUtils.Image } + return image +} +// CreateOperatorInitContainer creates the Operator init-container spec. +func (in *CoherenceResourceSpec) CreateOperatorInitContainer(deployment CoherenceResource) corev1.Container { + image := in.GetUtilsImage() vm := in.CreateCommonVolumeMounts() c := corev1.Container{ diff --git a/controllers/coherence_controller.go b/controllers/coherence_controller.go index 7120bc27..4aae9906 100644 --- a/controllers/coherence_controller.go +++ b/controllers/coherence_controller.go @@ -143,6 +143,13 @@ func (in *CoherenceReconciler) Reconcile(ctx context.Context, request ctrl.Reque // The request is an add or update + if err := deployment.ValidateCreate(); err != nil { + reason := "Coherence resource is invalid: " + err.Error() + in.GetEventRecorder().Event(deployment, coreV1.EventTypeWarning, "Validation", reason) + log.Info(reason) + return ctrl.Result{}, nil + } + // Ensure the hash label is present (it should have been added by the web-hook, so this should be a no-op). // The hash may not have been added if the Coherence resource was added/modified when the Operator was uninstalled. if hashApplied, err := in.ensureHashApplied(ctx, deployment); hashApplied || err != nil { diff --git a/go.mod b/go.mod index fb5baaeb..f646aec8 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.19 require ( github.com/davecgh/go-spew v1.1.1 + github.com/distribution/distribution v2.8.2+incompatible github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 github.com/go-logr/logr v1.2.3 github.com/go-test/deep v1.1.0 @@ -28,6 +29,7 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect @@ -56,6 +58,8 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect diff --git a/go.sum b/go.sum index 14c48b96..2bd04840 100644 --- a/go.sum +++ b/go.sum @@ -70,6 +70,10 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/distribution v2.8.2+incompatible h1:k9+4DKdOG+quPFZXT/mUsiQrGu9vYCp+dXpuPkuqhk8= +github.com/distribution/distribution v2.8.2+incompatible/go.mod h1:EgLm2NgWtdKgzF9NpMzUKgzmR7AMmb0VQi2B+ZzDRjc= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= @@ -262,6 +266,10 @@ github.com/onsi/ginkgo/v2 v2.8.4 h1:gf5mIQ8cLFieruNLAdgijHF1PYfLphKm2dxxcUtcqK0= github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.27.2 h1:SKU0CXeKE/WVgIV1T61kSa3+IRE8Ekrv9rdXDwwTqnY= github.com/onsi/gomega v1.27.2/go.mod h1:5mR3phAHpkAVIDkHEUBY6HGVsU+cpcEscrGPB4oPlZI= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=