diff --git a/api/v1alpha1/ctlog_types.go b/api/v1alpha1/ctlog_types.go index 0767aa86e..115985126 100644 --- a/api/v1alpha1/ctlog_types.go +++ b/api/v1alpha1/ctlog_types.go @@ -9,8 +9,11 @@ import ( // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. // CTlogSpec defines the desired state of CTlog component +// +kubebuilder:validation:XValidation:rule=(!has(self.publicKeyRef) || has(self.privateKeyRef)),message=privateKeyRef cannot be empty +// +kubebuilder:validation:XValidation:rule=(!has(self.privateKeyPasswordRef) || has(self.privateKeyRef)),message=privateKeyRef cannot be empty type CTlogSpec struct { // The ID of a Trillian tree that stores the log data. + // If it is unset, the operator will create new Merkle tree in the Trillian backend //+optional TreeID *int64 `json:"treeID,omitempty"` diff --git a/api/v1alpha1/ctlog_types_test.go b/api/v1alpha1/ctlog_types_test.go new file mode 100644 index 000000000..2cf796b8e --- /dev/null +++ b/api/v1alpha1/ctlog_types_test.go @@ -0,0 +1,179 @@ +package v1alpha1 + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "golang.org/x/net/context" + _ "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var _ = Describe("CTlog", func() { + + Context("CTlogSpec", func() { + It("can be created", func() { + created := generateCTlogObject("ctlog-create") + Expect(k8sClient.Create(context.Background(), created)).To(Succeed()) + + fetched := &CTlog{} + Expect(k8sClient.Get(context.Background(), getKey(created), fetched)).To(Succeed()) + Expect(fetched).To(Equal(created)) + }) + + It("can be updated", func() { + created := generateCTlogObject("ctlog-update") + Expect(k8sClient.Create(context.Background(), created)).To(Succeed()) + + fetched := &CTlog{} + Expect(k8sClient.Get(context.Background(), getKey(created), fetched)).To(Succeed()) + Expect(fetched).To(Equal(created)) + + var id int64 = 1234567890123456789 + fetched.Spec.TreeID = &id + Expect(k8sClient.Update(context.Background(), fetched)).To(Succeed()) + }) + + It("can be deleted", func() { + created := generateCTlogObject("ctlog-delete") + Expect(k8sClient.Create(context.Background(), created)).To(Succeed()) + + Expect(k8sClient.Delete(context.Background(), created)).To(Succeed()) + Expect(k8sClient.Get(context.Background(), getKey(created), created)).ToNot(Succeed()) + }) + + Context("is validated", func() { + It("public key", func() { + invalidObject := generateCTlogObject("public-key-invalid") + invalidObject.Spec.PublicKeyRef = &SecretKeySelector{ + Key: "key", + LocalObjectReference: corev1.LocalObjectReference{Name: "name"}, + } + + Expect(apierrors.IsInvalid(k8sClient.Create(context.Background(), invalidObject))).To(BeTrue()) + Expect(k8sClient.Create(context.Background(), invalidObject)). + To(MatchError(ContainSubstring("privateKeyRef cannot be empty"))) + }) + + It("private key password", func() { + invalidObject := generateCTlogObject("private-key-password-invalid") + invalidObject.Spec.PublicKeyRef = &SecretKeySelector{ + Key: "key", + LocalObjectReference: corev1.LocalObjectReference{Name: "name"}, + } + + Expect(apierrors.IsInvalid(k8sClient.Create(context.Background(), invalidObject))).To(BeTrue()) + Expect(k8sClient.Create(context.Background(), invalidObject)). + To(MatchError(ContainSubstring("privateKeyRef cannot be empty"))) + }) + }) + + Context("Default settings", func() { + var ( + ctlogInstance CTlog + expectedCTlogInstance CTlog + ) + + BeforeEach(func() { + expectedCTlogInstance = *generateCTlogObject("foo") + }) + + When("CR spec is empty", func() { + It("creates CR with defaults", func() { + ctlogInstance = CTlog{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ctlog-defaults", + Namespace: "default", + }, + } + + Expect(k8sClient.Create(context.Background(), &ctlogInstance)).To(Succeed()) + fetched := &CTlog{} + Expect(k8sClient.Get(context.Background(), getKey(&ctlogInstance), fetched)).To(Succeed()) + Expect(fetched.Spec).To(Equal(expectedCTlogInstance.Spec)) + }) + }) + + When("CR is fully populated", func() { + It("outputs the CR", func() { + tree := int64(1269875) + + ctlogInstance = CTlog{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ctlog-full-manifest", + Namespace: "default", + }, + Spec: CTlogSpec{ + TreeID: &tree, + PublicKeyRef: &SecretKeySelector{ + Key: "key", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "name", + }, + }, + PrivateKeyRef: &SecretKeySelector{ + Key: "key", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "name", + }, + }, + PrivateKeyPasswordRef: &SecretKeySelector{ + Key: "key", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "name", + }, + }, + RootCertificates: []SecretKeySelector{ + { + Key: "key", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "name", + }, + }, + }, + }, + } + + Expect(k8sClient.Create(context.Background(), &ctlogInstance)).To(Succeed()) + fetchedCTlog := &CTlog{} + Expect(k8sClient.Get(context.Background(), getKey(&ctlogInstance), fetchedCTlog)).To(Succeed()) + Expect(fetchedCTlog.Spec).To(Equal(ctlogInstance.Spec)) + }) + }) + + When("CR is partially set", func() { + + It("sets spec.pvc.storage if spec.pvc is partially set", func() { + tree := int64(1269875) + ctlogInstance = CTlog{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ctlog-storage", + Namespace: "default", + }, + Spec: CTlogSpec{ + TreeID: &tree, + }, + } + + expectedCTlogInstance.Spec.TreeID = &tree + + Expect(k8sClient.Create(context.Background(), &ctlogInstance)).To(Succeed()) + fetchedCTlog := &CTlog{} + Expect(k8sClient.Get(context.Background(), getKey(&ctlogInstance), fetchedCTlog)).To(Succeed()) + Expect(fetchedCTlog.Spec).To(Equal(expectedCTlogInstance.Spec)) + }) + }) + }) + }) +}) + +func generateCTlogObject(name string) *CTlog { + return &CTlog{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "default", + }, + Spec: CTlogSpec{}, + } +} diff --git a/api/v1alpha1/fulcio_types.go b/api/v1alpha1/fulcio_types.go index 68de602a8..6bd5eab46 100644 --- a/api/v1alpha1/fulcio_types.go +++ b/api/v1alpha1/fulcio_types.go @@ -15,12 +15,14 @@ type FulcioSpec struct { //+required Config FulcioConfig `json:"config"` // Certificate configuration - Certificate FulcioCert `json:"certificate,omitempty"` + Certificate FulcioCert `json:"certificate"` //Enable Service monitors for fulcio Monitoring MonitoringConfig `json:"monitoring,omitempty"` } // FulcioCert defines fields for system-generated certificate +// +kubebuilder:validation:XValidation:rule=(has(self.caRef) || self.commonName != ""),message=commonName cannot be empty +// +kubebuilder:validation:XValidation:rule=(!has(self.caRef) || has(self.privateKeyRef)),message=privateKeyRef cannot be empty type FulcioCert struct { // Reference to CA private key //+optional @@ -42,7 +44,8 @@ type FulcioCert struct { } type FulcioConfig struct { - OIDCIssuers map[string]OIDCIssuer `json:"OIDCIssuers,omitempty"` + //+kubebuilder:validation:MinProperties:=1 + OIDCIssuers map[string]OIDCIssuer `json:"OIDCIssuers"` // A meta issuer has a templated URL of the form: // https://oidc.eks.*.amazonaws.com/id/* @@ -51,6 +54,7 @@ type FulcioConfig struct { // other special characters) Some examples we want to match: // * https://oidc.eks.us-west-2.amazonaws.com/id/B02C93B6A2D30341AD01E1B6D48164CB // * https://container.googleapis.com/v1/projects/mattmoor-credit/locations/us-west1-b/clusters/tenant-cluster + // +optional MetaIssuers map[string]OIDCIssuer `json:"MetaIssuers,omitempty"` } @@ -58,9 +62,11 @@ type OIDCIssuer struct { // The expected issuer of an OIDC token IssuerURL string `json:"IssuerURL,omitempty"` // The expected client ID of the OIDC token + //+required ClientID string `json:"ClientID"` // Used to determine the subject of the certificate and if additional // certificate values are needed + //+required Type string `json:"Type"` // Optional, if the issuer is in a different claim in the OIDC token IssuerClaim string `json:"IssuerClaim,omitempty"` @@ -79,7 +85,7 @@ type OIDCIssuer struct { // FulcioStatus defines the observed state of Fulcio type FulcioStatus struct { ServerConfigRef *v1.LocalObjectReference `json:"serverConfigRef,omitempty"` - Certificate FulcioCert `json:"certificate,omitempty"` + Certificate *FulcioCert `json:"certificate,omitempty"` Url string `json:"url,omitempty"` // +listType=map // +listMapKey=type diff --git a/api/v1alpha1/fulcio_types_test.go b/api/v1alpha1/fulcio_types_test.go new file mode 100644 index 000000000..ba76070cd --- /dev/null +++ b/api/v1alpha1/fulcio_types_test.go @@ -0,0 +1,237 @@ +package v1alpha1 + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "golang.org/x/net/context" + _ "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var _ = Describe("Fulcio", func() { + + Context("FulcioSpec", func() { + It("can be created", func() { + created := generateFulcioObject("fulcio-create") + Expect(k8sClient.Create(context.Background(), created)).To(Succeed()) + + fetched := &Fulcio{} + Expect(k8sClient.Get(context.Background(), getKey(created), fetched)).To(Succeed()) + Expect(fetched).To(Equal(created)) + }) + + It("can be updated", func() { + created := generateFulcioObject("fulcio-update") + Expect(k8sClient.Create(context.Background(), created)).To(Succeed()) + + fetched := &Fulcio{} + Expect(k8sClient.Get(context.Background(), getKey(created), fetched)).To(Succeed()) + Expect(fetched).To(Equal(created)) + + fetched.Spec.Config.OIDCIssuers["test"] = OIDCIssuer{ + Type: "email", + ClientID: "client", + } + Expect(k8sClient.Update(context.Background(), fetched)).To(Succeed()) + }) + + It("can be deleted", func() { + created := generateFulcioObject("fulcio-delete") + Expect(k8sClient.Create(context.Background(), created)).To(Succeed()) + + Expect(k8sClient.Delete(context.Background(), created)).To(Succeed()) + Expect(k8sClient.Get(context.Background(), getKey(created), created)).ToNot(Succeed()) + }) + + When("changing external access setting", func() { + It("enabled false->true", func() { + created := generateFulcioObject("fulcio-access-1") + created.Spec.ExternalAccess.Enabled = false + Expect(k8sClient.Create(context.Background(), created)).To(Succeed()) + + fetched := &Fulcio{} + Expect(k8sClient.Get(context.Background(), getKey(created), fetched)).To(Succeed()) + Expect(fetched).To(Equal(created)) + + fetched.Spec.ExternalAccess.Enabled = true + Expect(k8sClient.Update(context.Background(), fetched)).To(Succeed()) + }) + + It("enabled true->false", func() { + created := generateFulcioObject("fulcio-access-2") + created.Spec.ExternalAccess.Enabled = true + Expect(k8sClient.Create(context.Background(), created)).To(Succeed()) + + fetched := &Fulcio{} + Expect(k8sClient.Get(context.Background(), getKey(created), fetched)).To(Succeed()) + Expect(fetched).To(Equal(created)) + + fetched.Spec.ExternalAccess.Enabled = false + Expect(apierrors.IsInvalid(k8sClient.Update(context.Background(), fetched))).To(BeTrue()) + Expect(k8sClient.Update(context.Background(), fetched)). + To(MatchError(ContainSubstring("Feature cannot be disabled"))) + }) + }) + + When("changing monitoring", func() { + It("enabled false->true", func() { + created := generateFulcioObject("fulcio-monitoring-1") + created.Spec.Monitoring.Enabled = false + Expect(k8sClient.Create(context.Background(), created)).To(Succeed()) + + fetched := &Fulcio{} + Expect(k8sClient.Get(context.Background(), getKey(created), fetched)).To(Succeed()) + Expect(fetched).To(Equal(created)) + + fetched.Spec.Monitoring.Enabled = true + Expect(k8sClient.Update(context.Background(), fetched)).To(Succeed()) + }) + + It("enabled true->false", func() { + created := generateFulcioObject("fulcio-monitoring-2") + created.Spec.Monitoring.Enabled = true + Expect(k8sClient.Create(context.Background(), created)).To(Succeed()) + + fetched := &Fulcio{} + Expect(k8sClient.Get(context.Background(), getKey(created), fetched)).To(Succeed()) + Expect(fetched).To(Equal(created)) + + fetched.Spec.Monitoring.Enabled = false + Expect(apierrors.IsInvalid(k8sClient.Update(context.Background(), fetched))).To(BeTrue()) + Expect(k8sClient.Update(context.Background(), fetched)). + To(MatchError(ContainSubstring("Feature cannot be disabled"))) + }) + }) + + Context("is validated", func() { + It("commonName", func() { + invalidObject := generateFulcioObject("commonname-invalid") + invalidObject.Spec.Certificate.CommonName = "" + + Expect(apierrors.IsInvalid(k8sClient.Create(context.Background(), invalidObject))).To(BeTrue()) + Expect(k8sClient.Create(context.Background(), invalidObject)). + To(MatchError(ContainSubstring("commonName cannot be empty"))) + }) + + It("private key", func() { + invalidObject := generateFulcioObject("private-key-invalid") + invalidObject.Spec.Certificate.CARef = &SecretKeySelector{ + Key: "key", + LocalObjectReference: corev1.LocalObjectReference{Name: "name"}, + } + + Expect(apierrors.IsInvalid(k8sClient.Create(context.Background(), invalidObject))).To(BeTrue()) + Expect(k8sClient.Create(context.Background(), invalidObject)). + To(MatchError(ContainSubstring("privateKeyRef cannot be empty"))) + }) + + It("config is not empty", func() { + invalidObject := generateFulcioObject("config-invalid") + invalidObject.Spec.Config.OIDCIssuers = make(map[string]OIDCIssuer) + + Expect(apierrors.IsInvalid(k8sClient.Create(context.Background(), invalidObject))).To(BeTrue()) + Expect(k8sClient.Create(context.Background(), invalidObject)). + To(MatchError(ContainSubstring("in body should have at least 1 properties"))) + }) + }) + + Context("Default settings", func() { + var ( + fulcioInstance Fulcio + ) + + When("CR spec is empty", func() { + It("creates CR with defaults", func() { + fulcioInstance = Fulcio{ + ObjectMeta: metav1.ObjectMeta{ + Name: "fulcio-defaults", + Namespace: "default", + }, + } + + Expect(apierrors.IsInvalid(k8sClient.Create(context.Background(), &fulcioInstance))).To(BeTrue()) + }) + }) + + When("CR is fully populated", func() { + It("outputs the CR", func() { + fulcioInstance = Fulcio{ + ObjectMeta: metav1.ObjectMeta{ + Name: "fulcio-full-manifest", + Namespace: "default", + }, + Spec: FulcioSpec{ + Monitoring: MonitoringConfig{ + Enabled: true, + }, + ExternalAccess: ExternalAccess{ + Enabled: true, + Host: "hostname", + }, + Config: FulcioConfig{ + OIDCIssuers: map[string]OIDCIssuer{ + "oidc": { + ClientID: "client", + Type: "email", + IssuerURL: "url", + IssuerClaim: "claim", + ChallengeClaim: "challange", + SPIFFETrustDomain: "SPIFFE", + SubjectDomain: "domain", + }, + "oidc2": { + ClientID: "clien2", + Type: "email2", + IssuerURL: "url2", + IssuerClaim: "claim2", + ChallengeClaim: "challang2e", + SPIFFETrustDomain: "SPIFFE2", + SubjectDomain: "domain2", + }, + }, + }, + Certificate: FulcioCert{ + CommonName: "CommonName", + OrganizationName: "OrganizationName", + OrganizationEmail: "OrganizationEmail", + CARef: &SecretKeySelector{Key: "key", LocalObjectReference: corev1.LocalObjectReference{Name: "name"}}, + PrivateKeyRef: &SecretKeySelector{Key: "key", LocalObjectReference: corev1.LocalObjectReference{Name: "name"}}, + PrivateKeyPasswordRef: &SecretKeySelector{Key: "key", LocalObjectReference: corev1.LocalObjectReference{Name: "name"}}, + }, + }, + } + + Expect(k8sClient.Create(context.Background(), &fulcioInstance)).To(Succeed()) + fetchedFulcio := &Fulcio{} + Expect(k8sClient.Get(context.Background(), getKey(&fulcioInstance), fetchedFulcio)).To(Succeed()) + Expect(fetchedFulcio.Spec).To(Equal(fulcioInstance.Spec)) + }) + }) + }) + }) +}) + +func generateFulcioObject(name string) *Fulcio { + return &Fulcio{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "default", + }, + Spec: FulcioSpec{ + Config: FulcioConfig{ + OIDCIssuers: map[string]OIDCIssuer{ + "oidc": { + ClientID: "client", + Type: "email", + IssuerURL: "url", + }, + }, + }, + Certificate: FulcioCert{ + CommonName: "hostname", + }, + }, + } +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index c4ce61c34..c13008639 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -352,7 +352,11 @@ func (in *FulcioStatus) DeepCopyInto(out *FulcioStatus) { *out = new(v1.LocalObjectReference) **out = **in } - in.Certificate.DeepCopyInto(&out.Certificate) + if in.Certificate != nil { + in, out := &in.Certificate, &out.Certificate + *out = new(FulcioCert) + (*in).DeepCopyInto(*out) + } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions *out = make([]metav1.Condition, len(*in)) diff --git a/config/crd/bases/rhtas.redhat.com_ctlogs.yaml b/config/crd/bases/rhtas.redhat.com_ctlogs.yaml index ecdbaf390..105d491f2 100644 --- a/config/crd/bases/rhtas.redhat.com_ctlogs.yaml +++ b/config/crd/bases/rhtas.redhat.com_ctlogs.yaml @@ -125,10 +125,17 @@ spec: x-kubernetes-map-type: atomic type: array treeID: - description: The ID of a Trillian tree that stores the log data. + description: |- + The ID of a Trillian tree that stores the log data. + If it is unset, the operator will create new Merkle tree in the Trillian backend format: int64 type: integer type: object + x-kubernetes-validations: + - message: privateKeyRef cannot be empty + rule: (!has(self.publicKeyRef) || has(self.privateKeyRef)) + - message: privateKeyRef cannot be empty + rule: (!has(self.privateKeyPasswordRef) || has(self.privateKeyRef)) status: description: CTlogStatus defines the observed state of CTlog component properties: diff --git a/config/crd/bases/rhtas.redhat.com_fulcios.yaml b/config/crd/bases/rhtas.redhat.com_fulcios.yaml index 9c4b43735..c1c6da45a 100644 --- a/config/crd/bases/rhtas.redhat.com_fulcios.yaml +++ b/config/crd/bases/rhtas.redhat.com_fulcios.yaml @@ -112,6 +112,11 @@ spec: type: object x-kubernetes-map-type: atomic type: object + x-kubernetes-validations: + - message: commonName cannot be empty + rule: (has(self.caRef) || self.commonName != "") + - message: privateKeyRef cannot be empty + rule: (!has(self.caRef) || has(self.privateKeyRef)) config: properties: MetaIssuers: @@ -199,7 +204,10 @@ spec: - ClientID - Type type: object + minProperties: 1 type: object + required: + - OIDCIssuers type: object externalAccess: description: Define whether you want to export service or not @@ -233,6 +241,7 @@ spec: - enabled type: object required: + - certificate - config type: object status: @@ -302,6 +311,11 @@ spec: type: object x-kubernetes-map-type: atomic type: object + x-kubernetes-validations: + - message: commonName cannot be empty + rule: (has(self.caRef) || self.commonName != "") + - message: privateKeyRef cannot be empty + rule: (!has(self.caRef) || has(self.privateKeyRef)) conditions: items: description: "Condition contains details for one aspect of the current diff --git a/config/crd/bases/rhtas.redhat.com_securesigns.yaml b/config/crd/bases/rhtas.redhat.com_securesigns.yaml index e820a4c92..df4734737 100644 --- a/config/crd/bases/rhtas.redhat.com_securesigns.yaml +++ b/config/crd/bases/rhtas.redhat.com_securesigns.yaml @@ -140,10 +140,17 @@ spec: x-kubernetes-map-type: atomic type: array treeID: - description: The ID of a Trillian tree that stores the log data. + description: |- + The ID of a Trillian tree that stores the log data. + If it is unset, the operator will create new Merkle tree in the Trillian backend format: int64 type: integer type: object + x-kubernetes-validations: + - message: privateKeyRef cannot be empty + rule: (!has(self.publicKeyRef) || has(self.privateKeyRef)) + - message: privateKeyRef cannot be empty + rule: (!has(self.privateKeyPasswordRef) || has(self.privateKeyRef)) fulcio: description: FulcioSpec defines the desired state of Fulcio properties: @@ -211,6 +218,11 @@ spec: type: object x-kubernetes-map-type: atomic type: object + x-kubernetes-validations: + - message: commonName cannot be empty + rule: (has(self.caRef) || self.commonName != "") + - message: privateKeyRef cannot be empty + rule: (!has(self.caRef) || has(self.privateKeyRef)) config: properties: MetaIssuers: @@ -298,7 +310,10 @@ spec: - ClientID - Type type: object + minProperties: 1 type: object + required: + - OIDCIssuers type: object externalAccess: description: Define whether you want to export service or not @@ -333,6 +348,7 @@ spec: - enabled type: object required: + - certificate - config type: object rekor: diff --git a/controllers/fulcio/actions/generate_cert.go b/controllers/fulcio/actions/generate_cert.go index d3307c843..52cc11974 100644 --- a/controllers/fulcio/actions/generate_cert.go +++ b/controllers/fulcio/actions/generate_cert.go @@ -40,7 +40,8 @@ func (g handleCert) Name() string { func (g handleCert) CanHandle(_ context.Context, instance *v1alpha1.Fulcio) bool { c := meta.FindStatusCondition(instance.Status.Conditions, constants.Ready) - return (c.Reason == constants.Pending || c.Reason == constants.Ready) && !equality.Semantic.DeepDerivative(instance.Spec.Certificate, instance.Status.Certificate) + return (c.Reason == constants.Pending || c.Reason == constants.Ready) && (instance.Status.Certificate == nil || + !equality.Semantic.DeepDerivative(instance.Spec.Certificate, *instance.Status.Certificate)) } func (g handleCert) Handle(ctx context.Context, instance *v1alpha1.Fulcio) *action.Result { @@ -120,7 +121,11 @@ func (g handleCert) Handle(ctx context.Context, instance *v1alpha1.Fulcio) *acti } g.Recorder.Event(instance, v1.EventTypeNormal, "FulcioCertUpdated", "Fulcio certificate secret updated") - instance.Spec.Certificate.DeepCopyInto(&instance.Status.Certificate) + if instance.Status.Certificate == nil { + instance.Status.Certificate = new(v1alpha1.FulcioCert) + } + + instance.Spec.Certificate.DeepCopyInto(instance.Status.Certificate) if instance.Spec.Certificate.PrivateKeyRef == nil { instance.Status.Certificate.PrivateKeyRef = &v1alpha1.SecretKeySelector{ Key: "private", diff --git a/controllers/fulcio/utils/fulcio_deployment.go b/controllers/fulcio/utils/fulcio_deployment.go index e7cfb22c3..e4366910d 100644 --- a/controllers/fulcio/utils/fulcio_deployment.go +++ b/controllers/fulcio/utils/fulcio_deployment.go @@ -16,6 +16,9 @@ func CreateDeployment(instance *v1alpha1.Fulcio, deploymentName string, sa strin if instance.Status.ServerConfigRef == nil { return nil, errors.New("server config ref is not specified") } + if instance.Status.Certificate == nil { + return nil, errors.New("certificate config is not specified") + } if instance.Status.Certificate.PrivateKeyRef == nil { return nil, errors.New("private key secret is not specified") }