diff --git a/apis/supervisor/config/v1alpha1/types_federationdomain.go.tmpl b/apis/supervisor/config/v1alpha1/types_federationdomain.go.tmpl index 2048b4e77..0290c618b 100644 --- a/apis/supervisor/config/v1alpha1/types_federationdomain.go.tmpl +++ b/apis/supervisor/config/v1alpha1/types_federationdomain.go.tmpl @@ -8,14 +8,17 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// +kubebuilder:validation:Enum=Success;Duplicate;Invalid;SameIssuerHostMustUseSameSecret -type FederationDomainStatusCondition string +type FederationDomainPhase string const ( - SuccessFederationDomainStatusCondition = FederationDomainStatusCondition("Success") - DuplicateFederationDomainStatusCondition = FederationDomainStatusCondition("Duplicate") - SameIssuerHostMustUseSameSecretFederationDomainStatusCondition = FederationDomainStatusCondition("SameIssuerHostMustUseSameSecret") - InvalidFederationDomainStatusCondition = FederationDomainStatusCondition("Invalid") + // FederationDomainPhasePending is the default phase for newly-created FederationDomain resources. + FederationDomainPhasePending FederationDomainPhase = "Pending" + + // FederationDomainPhaseReady is the phase for an FederationDomain resource in a healthy state. + FederationDomainPhaseReady FederationDomainPhase = "Ready" + + // FederationDomainPhaseError is the phase for an FederationDomain in an unhealthy state. + FederationDomainPhaseError FederationDomainPhase = "Error" ) // FederationDomainTLSSpec is a struct that describes the TLS configuration for an OIDC Provider. @@ -263,20 +266,17 @@ type FederationDomainSecrets struct { // FederationDomainStatus is a struct that describes the actual state of an OIDC Provider. type FederationDomainStatus struct { - // Status holds an enum that describes the state of this OIDC Provider. Note that this Status can - // represent success or failure. - // +optional - Status FederationDomainStatusCondition `json:"status,omitempty"` + // Phase summarizes the overall status of the FederationDomain. + // +kubebuilder:default=Pending + // +kubebuilder:validation:Enum=Pending;Ready;Error + Phase FederationDomainPhase `json:"phase,omitempty"` - // Message provides human-readable details about the Status. - // +optional - Message string `json:"message,omitempty"` - - // LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get - // around some undesirable behavior with respect to the empty metav1.Time value (see - // https://github.com/kubernetes/kubernetes/issues/86811). - // +optional - LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` + // Conditions represent the observations of an FederationDomain's current state. + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` // Secrets contains information about this OIDC Provider's secrets. // +optional @@ -288,7 +288,7 @@ type FederationDomainStatus struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` -// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status` +// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type FederationDomain struct { diff --git a/apis/supervisor/config/v1alpha1/types_oidcclient.go.tmpl b/apis/supervisor/config/v1alpha1/types_oidcclient.go.tmpl index 48f5de378..61106fdba 100644 --- a/apis/supervisor/config/v1alpha1/types_oidcclient.go.tmpl +++ b/apis/supervisor/config/v1alpha1/types_oidcclient.go.tmpl @@ -8,14 +8,14 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" type OIDCClientPhase string const ( - // PhasePending is the default phase for newly-created OIDCClient resources. - PhasePending OIDCClientPhase = "Pending" + // OIDCClientPhasePending is the default phase for newly-created OIDCClient resources. + OIDCClientPhasePending OIDCClientPhase = "Pending" - // PhaseReady is the phase for an OIDCClient resource in a healthy state. - PhaseReady OIDCClientPhase = "Ready" + // OIDCClientPhaseReady is the phase for an OIDCClient resource in a healthy state. + OIDCClientPhaseReady OIDCClientPhase = "Ready" - // PhaseError is the phase for an OIDCClient in an unhealthy state. - PhaseError OIDCClientPhase = "Error" + // OIDCClientPhaseError is the phase for an OIDCClient in an unhealthy state. + OIDCClientPhaseError OIDCClientPhase = "Error" ) // +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/` diff --git a/deploy/supervisor/config.supervisor.pinniped.dev_federationdomains.yaml b/deploy/supervisor/config.supervisor.pinniped.dev_federationdomains.yaml index 396d25511..cb7e1a519 100644 --- a/deploy/supervisor/config.supervisor.pinniped.dev_federationdomains.yaml +++ b/deploy/supervisor/config.supervisor.pinniped.dev_federationdomains.yaml @@ -21,8 +21,8 @@ spec: - jsonPath: .spec.issuer name: Issuer type: string - - jsonPath: .status.status - name: Status + - jsonPath: .status.phase + name: Phase type: string - jsonPath: .metadata.creationTimestamp name: Age @@ -348,14 +348,80 @@ spec: status: description: Status of the OIDC provider. properties: - lastUpdateTime: - description: LastUpdateTime holds the time at which the Status was - last updated. It is a pointer to get around some undesirable behavior - with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). - format: date-time - type: string - message: - description: Message provides human-readable details about the Status. + conditions: + description: Conditions represent the observations of an FederationDomain's + current state. + items: + description: Condition status of a resource (mirrored from the metav1.Condition + type added in Kubernetes 1.19). In a future API version we can + switch to using the upstream type. See https://github.com/kubernetes/apimachinery/blob/v0.19.0/pkg/apis/meta/v1/types.go#L1353-L1413. + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + phase: + default: Pending + description: Phase summarizes the overall status of the FederationDomain. + enum: + - Pending + - Ready + - Error type: string secrets: description: Secrets contains information about this OIDC Provider's @@ -402,15 +468,6 @@ spec: type: string type: object type: object - status: - description: Status holds an enum that describes the state of this - OIDC Provider. Note that this Status can represent success or failure. - enum: - - Success - - Duplicate - - Invalid - - SameIssuerHostMustUseSameSecret - type: string type: object required: - spec diff --git a/generated/1.21/README.adoc b/generated/1.21/README.adoc index 9a5fcd685..4710a9b7f 100644 --- a/generated/1.21/README.adoc +++ b/generated/1.21/README.adoc @@ -728,9 +728,8 @@ FederationDomainStatus is a struct that describes the actual state of an OIDC Pr [cols="25a,75a", options="header"] |=== | Field | Description -| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-21-apis-supervisor-config-v1alpha1-federationdomainstatuscondition[$$FederationDomainStatusCondition$$]__ | Status holds an enum that describes the state of this OIDC Provider. Note that this Status can represent success or failure. -| *`message`* __string__ | Message provides human-readable details about the Status. -| *`lastUpdateTime`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#time-v1-meta[$$Time$$]__ | LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get around some undesirable behavior with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). +| *`phase`* __FederationDomainPhase__ | Phase summarizes the overall status of the FederationDomain. +| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-21-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | Conditions represent the observations of an FederationDomain's current state. | *`secrets`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-21-apis-supervisor-config-v1alpha1-federationdomainsecrets[$$FederationDomainSecrets$$]__ | Secrets contains information about this OIDC Provider's secrets. |=== diff --git a/generated/1.21/apis/supervisor/config/v1alpha1/types_federationdomain.go b/generated/1.21/apis/supervisor/config/v1alpha1/types_federationdomain.go index 2048b4e77..0290c618b 100644 --- a/generated/1.21/apis/supervisor/config/v1alpha1/types_federationdomain.go +++ b/generated/1.21/apis/supervisor/config/v1alpha1/types_federationdomain.go @@ -8,14 +8,17 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// +kubebuilder:validation:Enum=Success;Duplicate;Invalid;SameIssuerHostMustUseSameSecret -type FederationDomainStatusCondition string +type FederationDomainPhase string const ( - SuccessFederationDomainStatusCondition = FederationDomainStatusCondition("Success") - DuplicateFederationDomainStatusCondition = FederationDomainStatusCondition("Duplicate") - SameIssuerHostMustUseSameSecretFederationDomainStatusCondition = FederationDomainStatusCondition("SameIssuerHostMustUseSameSecret") - InvalidFederationDomainStatusCondition = FederationDomainStatusCondition("Invalid") + // FederationDomainPhasePending is the default phase for newly-created FederationDomain resources. + FederationDomainPhasePending FederationDomainPhase = "Pending" + + // FederationDomainPhaseReady is the phase for an FederationDomain resource in a healthy state. + FederationDomainPhaseReady FederationDomainPhase = "Ready" + + // FederationDomainPhaseError is the phase for an FederationDomain in an unhealthy state. + FederationDomainPhaseError FederationDomainPhase = "Error" ) // FederationDomainTLSSpec is a struct that describes the TLS configuration for an OIDC Provider. @@ -263,20 +266,17 @@ type FederationDomainSecrets struct { // FederationDomainStatus is a struct that describes the actual state of an OIDC Provider. type FederationDomainStatus struct { - // Status holds an enum that describes the state of this OIDC Provider. Note that this Status can - // represent success or failure. - // +optional - Status FederationDomainStatusCondition `json:"status,omitempty"` + // Phase summarizes the overall status of the FederationDomain. + // +kubebuilder:default=Pending + // +kubebuilder:validation:Enum=Pending;Ready;Error + Phase FederationDomainPhase `json:"phase,omitempty"` - // Message provides human-readable details about the Status. - // +optional - Message string `json:"message,omitempty"` - - // LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get - // around some undesirable behavior with respect to the empty metav1.Time value (see - // https://github.com/kubernetes/kubernetes/issues/86811). - // +optional - LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` + // Conditions represent the observations of an FederationDomain's current state. + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` // Secrets contains information about this OIDC Provider's secrets. // +optional @@ -288,7 +288,7 @@ type FederationDomainStatus struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` -// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status` +// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type FederationDomain struct { diff --git a/generated/1.21/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/1.21/apis/supervisor/config/v1alpha1/types_oidcclient.go index 48f5de378..61106fdba 100644 --- a/generated/1.21/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/1.21/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -8,14 +8,14 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" type OIDCClientPhase string const ( - // PhasePending is the default phase for newly-created OIDCClient resources. - PhasePending OIDCClientPhase = "Pending" + // OIDCClientPhasePending is the default phase for newly-created OIDCClient resources. + OIDCClientPhasePending OIDCClientPhase = "Pending" - // PhaseReady is the phase for an OIDCClient resource in a healthy state. - PhaseReady OIDCClientPhase = "Ready" + // OIDCClientPhaseReady is the phase for an OIDCClient resource in a healthy state. + OIDCClientPhaseReady OIDCClientPhase = "Ready" - // PhaseError is the phase for an OIDCClient in an unhealthy state. - PhaseError OIDCClientPhase = "Error" + // OIDCClientPhaseError is the phase for an OIDCClient in an unhealthy state. + OIDCClientPhaseError OIDCClientPhase = "Error" ) // +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/` diff --git a/generated/1.21/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go b/generated/1.21/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go index 241b1aa96..0800faf56 100644 --- a/generated/1.21/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.21/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go @@ -143,9 +143,12 @@ func (in *FederationDomainSpec) DeepCopy() *FederationDomainSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FederationDomainStatus) DeepCopyInto(out *FederationDomainStatus) { *out = *in - if in.LastUpdateTime != nil { - in, out := &in.LastUpdateTime, &out.LastUpdateTime - *out = (*in).DeepCopy() + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } out.Secrets = in.Secrets return diff --git a/generated/1.21/crds/config.supervisor.pinniped.dev_federationdomains.yaml b/generated/1.21/crds/config.supervisor.pinniped.dev_federationdomains.yaml index 396d25511..cb7e1a519 100644 --- a/generated/1.21/crds/config.supervisor.pinniped.dev_federationdomains.yaml +++ b/generated/1.21/crds/config.supervisor.pinniped.dev_federationdomains.yaml @@ -21,8 +21,8 @@ spec: - jsonPath: .spec.issuer name: Issuer type: string - - jsonPath: .status.status - name: Status + - jsonPath: .status.phase + name: Phase type: string - jsonPath: .metadata.creationTimestamp name: Age @@ -348,14 +348,80 @@ spec: status: description: Status of the OIDC provider. properties: - lastUpdateTime: - description: LastUpdateTime holds the time at which the Status was - last updated. It is a pointer to get around some undesirable behavior - with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). - format: date-time - type: string - message: - description: Message provides human-readable details about the Status. + conditions: + description: Conditions represent the observations of an FederationDomain's + current state. + items: + description: Condition status of a resource (mirrored from the metav1.Condition + type added in Kubernetes 1.19). In a future API version we can + switch to using the upstream type. See https://github.com/kubernetes/apimachinery/blob/v0.19.0/pkg/apis/meta/v1/types.go#L1353-L1413. + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + phase: + default: Pending + description: Phase summarizes the overall status of the FederationDomain. + enum: + - Pending + - Ready + - Error type: string secrets: description: Secrets contains information about this OIDC Provider's @@ -402,15 +468,6 @@ spec: type: string type: object type: object - status: - description: Status holds an enum that describes the state of this - OIDC Provider. Note that this Status can represent success or failure. - enum: - - Success - - Duplicate - - Invalid - - SameIssuerHostMustUseSameSecret - type: string type: object required: - spec diff --git a/generated/1.22/README.adoc b/generated/1.22/README.adoc index 58e9b9353..178c5bfcd 100644 --- a/generated/1.22/README.adoc +++ b/generated/1.22/README.adoc @@ -728,9 +728,8 @@ FederationDomainStatus is a struct that describes the actual state of an OIDC Pr [cols="25a,75a", options="header"] |=== | Field | Description -| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-22-apis-supervisor-config-v1alpha1-federationdomainstatuscondition[$$FederationDomainStatusCondition$$]__ | Status holds an enum that describes the state of this OIDC Provider. Note that this Status can represent success or failure. -| *`message`* __string__ | Message provides human-readable details about the Status. -| *`lastUpdateTime`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#time-v1-meta[$$Time$$]__ | LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get around some undesirable behavior with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). +| *`phase`* __FederationDomainPhase__ | Phase summarizes the overall status of the FederationDomain. +| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-22-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | Conditions represent the observations of an FederationDomain's current state. | *`secrets`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-22-apis-supervisor-config-v1alpha1-federationdomainsecrets[$$FederationDomainSecrets$$]__ | Secrets contains information about this OIDC Provider's secrets. |=== diff --git a/generated/1.22/apis/supervisor/config/v1alpha1/types_federationdomain.go b/generated/1.22/apis/supervisor/config/v1alpha1/types_federationdomain.go index 2048b4e77..0290c618b 100644 --- a/generated/1.22/apis/supervisor/config/v1alpha1/types_federationdomain.go +++ b/generated/1.22/apis/supervisor/config/v1alpha1/types_federationdomain.go @@ -8,14 +8,17 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// +kubebuilder:validation:Enum=Success;Duplicate;Invalid;SameIssuerHostMustUseSameSecret -type FederationDomainStatusCondition string +type FederationDomainPhase string const ( - SuccessFederationDomainStatusCondition = FederationDomainStatusCondition("Success") - DuplicateFederationDomainStatusCondition = FederationDomainStatusCondition("Duplicate") - SameIssuerHostMustUseSameSecretFederationDomainStatusCondition = FederationDomainStatusCondition("SameIssuerHostMustUseSameSecret") - InvalidFederationDomainStatusCondition = FederationDomainStatusCondition("Invalid") + // FederationDomainPhasePending is the default phase for newly-created FederationDomain resources. + FederationDomainPhasePending FederationDomainPhase = "Pending" + + // FederationDomainPhaseReady is the phase for an FederationDomain resource in a healthy state. + FederationDomainPhaseReady FederationDomainPhase = "Ready" + + // FederationDomainPhaseError is the phase for an FederationDomain in an unhealthy state. + FederationDomainPhaseError FederationDomainPhase = "Error" ) // FederationDomainTLSSpec is a struct that describes the TLS configuration for an OIDC Provider. @@ -263,20 +266,17 @@ type FederationDomainSecrets struct { // FederationDomainStatus is a struct that describes the actual state of an OIDC Provider. type FederationDomainStatus struct { - // Status holds an enum that describes the state of this OIDC Provider. Note that this Status can - // represent success or failure. - // +optional - Status FederationDomainStatusCondition `json:"status,omitempty"` + // Phase summarizes the overall status of the FederationDomain. + // +kubebuilder:default=Pending + // +kubebuilder:validation:Enum=Pending;Ready;Error + Phase FederationDomainPhase `json:"phase,omitempty"` - // Message provides human-readable details about the Status. - // +optional - Message string `json:"message,omitempty"` - - // LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get - // around some undesirable behavior with respect to the empty metav1.Time value (see - // https://github.com/kubernetes/kubernetes/issues/86811). - // +optional - LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` + // Conditions represent the observations of an FederationDomain's current state. + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` // Secrets contains information about this OIDC Provider's secrets. // +optional @@ -288,7 +288,7 @@ type FederationDomainStatus struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` -// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status` +// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type FederationDomain struct { diff --git a/generated/1.22/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/1.22/apis/supervisor/config/v1alpha1/types_oidcclient.go index 48f5de378..61106fdba 100644 --- a/generated/1.22/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/1.22/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -8,14 +8,14 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" type OIDCClientPhase string const ( - // PhasePending is the default phase for newly-created OIDCClient resources. - PhasePending OIDCClientPhase = "Pending" + // OIDCClientPhasePending is the default phase for newly-created OIDCClient resources. + OIDCClientPhasePending OIDCClientPhase = "Pending" - // PhaseReady is the phase for an OIDCClient resource in a healthy state. - PhaseReady OIDCClientPhase = "Ready" + // OIDCClientPhaseReady is the phase for an OIDCClient resource in a healthy state. + OIDCClientPhaseReady OIDCClientPhase = "Ready" - // PhaseError is the phase for an OIDCClient in an unhealthy state. - PhaseError OIDCClientPhase = "Error" + // OIDCClientPhaseError is the phase for an OIDCClient in an unhealthy state. + OIDCClientPhaseError OIDCClientPhase = "Error" ) // +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/` diff --git a/generated/1.22/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go b/generated/1.22/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go index 241b1aa96..0800faf56 100644 --- a/generated/1.22/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.22/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go @@ -143,9 +143,12 @@ func (in *FederationDomainSpec) DeepCopy() *FederationDomainSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FederationDomainStatus) DeepCopyInto(out *FederationDomainStatus) { *out = *in - if in.LastUpdateTime != nil { - in, out := &in.LastUpdateTime, &out.LastUpdateTime - *out = (*in).DeepCopy() + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } out.Secrets = in.Secrets return diff --git a/generated/1.22/crds/config.supervisor.pinniped.dev_federationdomains.yaml b/generated/1.22/crds/config.supervisor.pinniped.dev_federationdomains.yaml index 396d25511..cb7e1a519 100644 --- a/generated/1.22/crds/config.supervisor.pinniped.dev_federationdomains.yaml +++ b/generated/1.22/crds/config.supervisor.pinniped.dev_federationdomains.yaml @@ -21,8 +21,8 @@ spec: - jsonPath: .spec.issuer name: Issuer type: string - - jsonPath: .status.status - name: Status + - jsonPath: .status.phase + name: Phase type: string - jsonPath: .metadata.creationTimestamp name: Age @@ -348,14 +348,80 @@ spec: status: description: Status of the OIDC provider. properties: - lastUpdateTime: - description: LastUpdateTime holds the time at which the Status was - last updated. It is a pointer to get around some undesirable behavior - with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). - format: date-time - type: string - message: - description: Message provides human-readable details about the Status. + conditions: + description: Conditions represent the observations of an FederationDomain's + current state. + items: + description: Condition status of a resource (mirrored from the metav1.Condition + type added in Kubernetes 1.19). In a future API version we can + switch to using the upstream type. See https://github.com/kubernetes/apimachinery/blob/v0.19.0/pkg/apis/meta/v1/types.go#L1353-L1413. + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + phase: + default: Pending + description: Phase summarizes the overall status of the FederationDomain. + enum: + - Pending + - Ready + - Error type: string secrets: description: Secrets contains information about this OIDC Provider's @@ -402,15 +468,6 @@ spec: type: string type: object type: object - status: - description: Status holds an enum that describes the state of this - OIDC Provider. Note that this Status can represent success or failure. - enum: - - Success - - Duplicate - - Invalid - - SameIssuerHostMustUseSameSecret - type: string type: object required: - spec diff --git a/generated/1.23/README.adoc b/generated/1.23/README.adoc index dfd2ef936..7044b5f46 100644 --- a/generated/1.23/README.adoc +++ b/generated/1.23/README.adoc @@ -728,9 +728,8 @@ FederationDomainStatus is a struct that describes the actual state of an OIDC Pr [cols="25a,75a", options="header"] |=== | Field | Description -| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-23-apis-supervisor-config-v1alpha1-federationdomainstatuscondition[$$FederationDomainStatusCondition$$]__ | Status holds an enum that describes the state of this OIDC Provider. Note that this Status can represent success or failure. -| *`message`* __string__ | Message provides human-readable details about the Status. -| *`lastUpdateTime`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#time-v1-meta[$$Time$$]__ | LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get around some undesirable behavior with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). +| *`phase`* __FederationDomainPhase__ | Phase summarizes the overall status of the FederationDomain. +| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-23-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | Conditions represent the observations of an FederationDomain's current state. | *`secrets`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-23-apis-supervisor-config-v1alpha1-federationdomainsecrets[$$FederationDomainSecrets$$]__ | Secrets contains information about this OIDC Provider's secrets. |=== diff --git a/generated/1.23/apis/supervisor/config/v1alpha1/types_federationdomain.go b/generated/1.23/apis/supervisor/config/v1alpha1/types_federationdomain.go index 2048b4e77..0290c618b 100644 --- a/generated/1.23/apis/supervisor/config/v1alpha1/types_federationdomain.go +++ b/generated/1.23/apis/supervisor/config/v1alpha1/types_federationdomain.go @@ -8,14 +8,17 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// +kubebuilder:validation:Enum=Success;Duplicate;Invalid;SameIssuerHostMustUseSameSecret -type FederationDomainStatusCondition string +type FederationDomainPhase string const ( - SuccessFederationDomainStatusCondition = FederationDomainStatusCondition("Success") - DuplicateFederationDomainStatusCondition = FederationDomainStatusCondition("Duplicate") - SameIssuerHostMustUseSameSecretFederationDomainStatusCondition = FederationDomainStatusCondition("SameIssuerHostMustUseSameSecret") - InvalidFederationDomainStatusCondition = FederationDomainStatusCondition("Invalid") + // FederationDomainPhasePending is the default phase for newly-created FederationDomain resources. + FederationDomainPhasePending FederationDomainPhase = "Pending" + + // FederationDomainPhaseReady is the phase for an FederationDomain resource in a healthy state. + FederationDomainPhaseReady FederationDomainPhase = "Ready" + + // FederationDomainPhaseError is the phase for an FederationDomain in an unhealthy state. + FederationDomainPhaseError FederationDomainPhase = "Error" ) // FederationDomainTLSSpec is a struct that describes the TLS configuration for an OIDC Provider. @@ -263,20 +266,17 @@ type FederationDomainSecrets struct { // FederationDomainStatus is a struct that describes the actual state of an OIDC Provider. type FederationDomainStatus struct { - // Status holds an enum that describes the state of this OIDC Provider. Note that this Status can - // represent success or failure. - // +optional - Status FederationDomainStatusCondition `json:"status,omitempty"` + // Phase summarizes the overall status of the FederationDomain. + // +kubebuilder:default=Pending + // +kubebuilder:validation:Enum=Pending;Ready;Error + Phase FederationDomainPhase `json:"phase,omitempty"` - // Message provides human-readable details about the Status. - // +optional - Message string `json:"message,omitempty"` - - // LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get - // around some undesirable behavior with respect to the empty metav1.Time value (see - // https://github.com/kubernetes/kubernetes/issues/86811). - // +optional - LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` + // Conditions represent the observations of an FederationDomain's current state. + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` // Secrets contains information about this OIDC Provider's secrets. // +optional @@ -288,7 +288,7 @@ type FederationDomainStatus struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` -// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status` +// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type FederationDomain struct { diff --git a/generated/1.23/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/1.23/apis/supervisor/config/v1alpha1/types_oidcclient.go index 48f5de378..61106fdba 100644 --- a/generated/1.23/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/1.23/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -8,14 +8,14 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" type OIDCClientPhase string const ( - // PhasePending is the default phase for newly-created OIDCClient resources. - PhasePending OIDCClientPhase = "Pending" + // OIDCClientPhasePending is the default phase for newly-created OIDCClient resources. + OIDCClientPhasePending OIDCClientPhase = "Pending" - // PhaseReady is the phase for an OIDCClient resource in a healthy state. - PhaseReady OIDCClientPhase = "Ready" + // OIDCClientPhaseReady is the phase for an OIDCClient resource in a healthy state. + OIDCClientPhaseReady OIDCClientPhase = "Ready" - // PhaseError is the phase for an OIDCClient in an unhealthy state. - PhaseError OIDCClientPhase = "Error" + // OIDCClientPhaseError is the phase for an OIDCClient in an unhealthy state. + OIDCClientPhaseError OIDCClientPhase = "Error" ) // +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/` diff --git a/generated/1.23/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go b/generated/1.23/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go index 241b1aa96..0800faf56 100644 --- a/generated/1.23/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.23/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go @@ -143,9 +143,12 @@ func (in *FederationDomainSpec) DeepCopy() *FederationDomainSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FederationDomainStatus) DeepCopyInto(out *FederationDomainStatus) { *out = *in - if in.LastUpdateTime != nil { - in, out := &in.LastUpdateTime, &out.LastUpdateTime - *out = (*in).DeepCopy() + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } out.Secrets = in.Secrets return diff --git a/generated/1.23/crds/config.supervisor.pinniped.dev_federationdomains.yaml b/generated/1.23/crds/config.supervisor.pinniped.dev_federationdomains.yaml index 396d25511..cb7e1a519 100644 --- a/generated/1.23/crds/config.supervisor.pinniped.dev_federationdomains.yaml +++ b/generated/1.23/crds/config.supervisor.pinniped.dev_federationdomains.yaml @@ -21,8 +21,8 @@ spec: - jsonPath: .spec.issuer name: Issuer type: string - - jsonPath: .status.status - name: Status + - jsonPath: .status.phase + name: Phase type: string - jsonPath: .metadata.creationTimestamp name: Age @@ -348,14 +348,80 @@ spec: status: description: Status of the OIDC provider. properties: - lastUpdateTime: - description: LastUpdateTime holds the time at which the Status was - last updated. It is a pointer to get around some undesirable behavior - with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). - format: date-time - type: string - message: - description: Message provides human-readable details about the Status. + conditions: + description: Conditions represent the observations of an FederationDomain's + current state. + items: + description: Condition status of a resource (mirrored from the metav1.Condition + type added in Kubernetes 1.19). In a future API version we can + switch to using the upstream type. See https://github.com/kubernetes/apimachinery/blob/v0.19.0/pkg/apis/meta/v1/types.go#L1353-L1413. + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + phase: + default: Pending + description: Phase summarizes the overall status of the FederationDomain. + enum: + - Pending + - Ready + - Error type: string secrets: description: Secrets contains information about this OIDC Provider's @@ -402,15 +468,6 @@ spec: type: string type: object type: object - status: - description: Status holds an enum that describes the state of this - OIDC Provider. Note that this Status can represent success or failure. - enum: - - Success - - Duplicate - - Invalid - - SameIssuerHostMustUseSameSecret - type: string type: object required: - spec diff --git a/generated/1.24/README.adoc b/generated/1.24/README.adoc index c68bd1371..5d92b7054 100644 --- a/generated/1.24/README.adoc +++ b/generated/1.24/README.adoc @@ -728,9 +728,8 @@ FederationDomainStatus is a struct that describes the actual state of an OIDC Pr [cols="25a,75a", options="header"] |=== | Field | Description -| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-config-v1alpha1-federationdomainstatuscondition[$$FederationDomainStatusCondition$$]__ | Status holds an enum that describes the state of this OIDC Provider. Note that this Status can represent success or failure. -| *`message`* __string__ | Message provides human-readable details about the Status. -| *`lastUpdateTime`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#time-v1-meta[$$Time$$]__ | LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get around some undesirable behavior with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). +| *`phase`* __FederationDomainPhase__ | Phase summarizes the overall status of the FederationDomain. +| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | Conditions represent the observations of an FederationDomain's current state. | *`secrets`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-24-apis-supervisor-config-v1alpha1-federationdomainsecrets[$$FederationDomainSecrets$$]__ | Secrets contains information about this OIDC Provider's secrets. |=== diff --git a/generated/1.24/apis/supervisor/config/v1alpha1/types_federationdomain.go b/generated/1.24/apis/supervisor/config/v1alpha1/types_federationdomain.go index 2048b4e77..0290c618b 100644 --- a/generated/1.24/apis/supervisor/config/v1alpha1/types_federationdomain.go +++ b/generated/1.24/apis/supervisor/config/v1alpha1/types_federationdomain.go @@ -8,14 +8,17 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// +kubebuilder:validation:Enum=Success;Duplicate;Invalid;SameIssuerHostMustUseSameSecret -type FederationDomainStatusCondition string +type FederationDomainPhase string const ( - SuccessFederationDomainStatusCondition = FederationDomainStatusCondition("Success") - DuplicateFederationDomainStatusCondition = FederationDomainStatusCondition("Duplicate") - SameIssuerHostMustUseSameSecretFederationDomainStatusCondition = FederationDomainStatusCondition("SameIssuerHostMustUseSameSecret") - InvalidFederationDomainStatusCondition = FederationDomainStatusCondition("Invalid") + // FederationDomainPhasePending is the default phase for newly-created FederationDomain resources. + FederationDomainPhasePending FederationDomainPhase = "Pending" + + // FederationDomainPhaseReady is the phase for an FederationDomain resource in a healthy state. + FederationDomainPhaseReady FederationDomainPhase = "Ready" + + // FederationDomainPhaseError is the phase for an FederationDomain in an unhealthy state. + FederationDomainPhaseError FederationDomainPhase = "Error" ) // FederationDomainTLSSpec is a struct that describes the TLS configuration for an OIDC Provider. @@ -263,20 +266,17 @@ type FederationDomainSecrets struct { // FederationDomainStatus is a struct that describes the actual state of an OIDC Provider. type FederationDomainStatus struct { - // Status holds an enum that describes the state of this OIDC Provider. Note that this Status can - // represent success or failure. - // +optional - Status FederationDomainStatusCondition `json:"status,omitempty"` + // Phase summarizes the overall status of the FederationDomain. + // +kubebuilder:default=Pending + // +kubebuilder:validation:Enum=Pending;Ready;Error + Phase FederationDomainPhase `json:"phase,omitempty"` - // Message provides human-readable details about the Status. - // +optional - Message string `json:"message,omitempty"` - - // LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get - // around some undesirable behavior with respect to the empty metav1.Time value (see - // https://github.com/kubernetes/kubernetes/issues/86811). - // +optional - LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` + // Conditions represent the observations of an FederationDomain's current state. + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` // Secrets contains information about this OIDC Provider's secrets. // +optional @@ -288,7 +288,7 @@ type FederationDomainStatus struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` -// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status` +// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type FederationDomain struct { diff --git a/generated/1.24/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/1.24/apis/supervisor/config/v1alpha1/types_oidcclient.go index 48f5de378..61106fdba 100644 --- a/generated/1.24/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/1.24/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -8,14 +8,14 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" type OIDCClientPhase string const ( - // PhasePending is the default phase for newly-created OIDCClient resources. - PhasePending OIDCClientPhase = "Pending" + // OIDCClientPhasePending is the default phase for newly-created OIDCClient resources. + OIDCClientPhasePending OIDCClientPhase = "Pending" - // PhaseReady is the phase for an OIDCClient resource in a healthy state. - PhaseReady OIDCClientPhase = "Ready" + // OIDCClientPhaseReady is the phase for an OIDCClient resource in a healthy state. + OIDCClientPhaseReady OIDCClientPhase = "Ready" - // PhaseError is the phase for an OIDCClient in an unhealthy state. - PhaseError OIDCClientPhase = "Error" + // OIDCClientPhaseError is the phase for an OIDCClient in an unhealthy state. + OIDCClientPhaseError OIDCClientPhase = "Error" ) // +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/` diff --git a/generated/1.24/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go b/generated/1.24/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go index 241b1aa96..0800faf56 100644 --- a/generated/1.24/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.24/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go @@ -143,9 +143,12 @@ func (in *FederationDomainSpec) DeepCopy() *FederationDomainSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FederationDomainStatus) DeepCopyInto(out *FederationDomainStatus) { *out = *in - if in.LastUpdateTime != nil { - in, out := &in.LastUpdateTime, &out.LastUpdateTime - *out = (*in).DeepCopy() + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } out.Secrets = in.Secrets return diff --git a/generated/1.24/crds/config.supervisor.pinniped.dev_federationdomains.yaml b/generated/1.24/crds/config.supervisor.pinniped.dev_federationdomains.yaml index 396d25511..cb7e1a519 100644 --- a/generated/1.24/crds/config.supervisor.pinniped.dev_federationdomains.yaml +++ b/generated/1.24/crds/config.supervisor.pinniped.dev_federationdomains.yaml @@ -21,8 +21,8 @@ spec: - jsonPath: .spec.issuer name: Issuer type: string - - jsonPath: .status.status - name: Status + - jsonPath: .status.phase + name: Phase type: string - jsonPath: .metadata.creationTimestamp name: Age @@ -348,14 +348,80 @@ spec: status: description: Status of the OIDC provider. properties: - lastUpdateTime: - description: LastUpdateTime holds the time at which the Status was - last updated. It is a pointer to get around some undesirable behavior - with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). - format: date-time - type: string - message: - description: Message provides human-readable details about the Status. + conditions: + description: Conditions represent the observations of an FederationDomain's + current state. + items: + description: Condition status of a resource (mirrored from the metav1.Condition + type added in Kubernetes 1.19). In a future API version we can + switch to using the upstream type. See https://github.com/kubernetes/apimachinery/blob/v0.19.0/pkg/apis/meta/v1/types.go#L1353-L1413. + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + phase: + default: Pending + description: Phase summarizes the overall status of the FederationDomain. + enum: + - Pending + - Ready + - Error type: string secrets: description: Secrets contains information about this OIDC Provider's @@ -402,15 +468,6 @@ spec: type: string type: object type: object - status: - description: Status holds an enum that describes the state of this - OIDC Provider. Note that this Status can represent success or failure. - enum: - - Success - - Duplicate - - Invalid - - SameIssuerHostMustUseSameSecret - type: string type: object required: - spec diff --git a/generated/1.25/README.adoc b/generated/1.25/README.adoc index 30e9a6860..eab5d0d14 100644 --- a/generated/1.25/README.adoc +++ b/generated/1.25/README.adoc @@ -726,9 +726,8 @@ FederationDomainStatus is a struct that describes the actual state of an OIDC Pr [cols="25a,75a", options="header"] |=== | Field | Description -| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-config-v1alpha1-federationdomainstatuscondition[$$FederationDomainStatusCondition$$]__ | Status holds an enum that describes the state of this OIDC Provider. Note that this Status can represent success or failure. -| *`message`* __string__ | Message provides human-readable details about the Status. -| *`lastUpdateTime`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#time-v1-meta[$$Time$$]__ | LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get around some undesirable behavior with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). +| *`phase`* __FederationDomainPhase__ | Phase summarizes the overall status of the FederationDomain. +| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | Conditions represent the observations of an FederationDomain's current state. | *`secrets`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-25-apis-supervisor-config-v1alpha1-federationdomainsecrets[$$FederationDomainSecrets$$]__ | Secrets contains information about this OIDC Provider's secrets. |=== diff --git a/generated/1.25/apis/supervisor/config/v1alpha1/types_federationdomain.go b/generated/1.25/apis/supervisor/config/v1alpha1/types_federationdomain.go index 2048b4e77..0290c618b 100644 --- a/generated/1.25/apis/supervisor/config/v1alpha1/types_federationdomain.go +++ b/generated/1.25/apis/supervisor/config/v1alpha1/types_federationdomain.go @@ -8,14 +8,17 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// +kubebuilder:validation:Enum=Success;Duplicate;Invalid;SameIssuerHostMustUseSameSecret -type FederationDomainStatusCondition string +type FederationDomainPhase string const ( - SuccessFederationDomainStatusCondition = FederationDomainStatusCondition("Success") - DuplicateFederationDomainStatusCondition = FederationDomainStatusCondition("Duplicate") - SameIssuerHostMustUseSameSecretFederationDomainStatusCondition = FederationDomainStatusCondition("SameIssuerHostMustUseSameSecret") - InvalidFederationDomainStatusCondition = FederationDomainStatusCondition("Invalid") + // FederationDomainPhasePending is the default phase for newly-created FederationDomain resources. + FederationDomainPhasePending FederationDomainPhase = "Pending" + + // FederationDomainPhaseReady is the phase for an FederationDomain resource in a healthy state. + FederationDomainPhaseReady FederationDomainPhase = "Ready" + + // FederationDomainPhaseError is the phase for an FederationDomain in an unhealthy state. + FederationDomainPhaseError FederationDomainPhase = "Error" ) // FederationDomainTLSSpec is a struct that describes the TLS configuration for an OIDC Provider. @@ -263,20 +266,17 @@ type FederationDomainSecrets struct { // FederationDomainStatus is a struct that describes the actual state of an OIDC Provider. type FederationDomainStatus struct { - // Status holds an enum that describes the state of this OIDC Provider. Note that this Status can - // represent success or failure. - // +optional - Status FederationDomainStatusCondition `json:"status,omitempty"` + // Phase summarizes the overall status of the FederationDomain. + // +kubebuilder:default=Pending + // +kubebuilder:validation:Enum=Pending;Ready;Error + Phase FederationDomainPhase `json:"phase,omitempty"` - // Message provides human-readable details about the Status. - // +optional - Message string `json:"message,omitempty"` - - // LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get - // around some undesirable behavior with respect to the empty metav1.Time value (see - // https://github.com/kubernetes/kubernetes/issues/86811). - // +optional - LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` + // Conditions represent the observations of an FederationDomain's current state. + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` // Secrets contains information about this OIDC Provider's secrets. // +optional @@ -288,7 +288,7 @@ type FederationDomainStatus struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` -// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status` +// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type FederationDomain struct { diff --git a/generated/1.25/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/1.25/apis/supervisor/config/v1alpha1/types_oidcclient.go index 48f5de378..61106fdba 100644 --- a/generated/1.25/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/1.25/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -8,14 +8,14 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" type OIDCClientPhase string const ( - // PhasePending is the default phase for newly-created OIDCClient resources. - PhasePending OIDCClientPhase = "Pending" + // OIDCClientPhasePending is the default phase for newly-created OIDCClient resources. + OIDCClientPhasePending OIDCClientPhase = "Pending" - // PhaseReady is the phase for an OIDCClient resource in a healthy state. - PhaseReady OIDCClientPhase = "Ready" + // OIDCClientPhaseReady is the phase for an OIDCClient resource in a healthy state. + OIDCClientPhaseReady OIDCClientPhase = "Ready" - // PhaseError is the phase for an OIDCClient in an unhealthy state. - PhaseError OIDCClientPhase = "Error" + // OIDCClientPhaseError is the phase for an OIDCClient in an unhealthy state. + OIDCClientPhaseError OIDCClientPhase = "Error" ) // +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/` diff --git a/generated/1.25/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go b/generated/1.25/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go index 241b1aa96..0800faf56 100644 --- a/generated/1.25/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.25/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go @@ -143,9 +143,12 @@ func (in *FederationDomainSpec) DeepCopy() *FederationDomainSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FederationDomainStatus) DeepCopyInto(out *FederationDomainStatus) { *out = *in - if in.LastUpdateTime != nil { - in, out := &in.LastUpdateTime, &out.LastUpdateTime - *out = (*in).DeepCopy() + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } out.Secrets = in.Secrets return diff --git a/generated/1.25/crds/config.supervisor.pinniped.dev_federationdomains.yaml b/generated/1.25/crds/config.supervisor.pinniped.dev_federationdomains.yaml index 396d25511..cb7e1a519 100644 --- a/generated/1.25/crds/config.supervisor.pinniped.dev_federationdomains.yaml +++ b/generated/1.25/crds/config.supervisor.pinniped.dev_federationdomains.yaml @@ -21,8 +21,8 @@ spec: - jsonPath: .spec.issuer name: Issuer type: string - - jsonPath: .status.status - name: Status + - jsonPath: .status.phase + name: Phase type: string - jsonPath: .metadata.creationTimestamp name: Age @@ -348,14 +348,80 @@ spec: status: description: Status of the OIDC provider. properties: - lastUpdateTime: - description: LastUpdateTime holds the time at which the Status was - last updated. It is a pointer to get around some undesirable behavior - with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). - format: date-time - type: string - message: - description: Message provides human-readable details about the Status. + conditions: + description: Conditions represent the observations of an FederationDomain's + current state. + items: + description: Condition status of a resource (mirrored from the metav1.Condition + type added in Kubernetes 1.19). In a future API version we can + switch to using the upstream type. See https://github.com/kubernetes/apimachinery/blob/v0.19.0/pkg/apis/meta/v1/types.go#L1353-L1413. + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + phase: + default: Pending + description: Phase summarizes the overall status of the FederationDomain. + enum: + - Pending + - Ready + - Error type: string secrets: description: Secrets contains information about this OIDC Provider's @@ -402,15 +468,6 @@ spec: type: string type: object type: object - status: - description: Status holds an enum that describes the state of this - OIDC Provider. Note that this Status can represent success or failure. - enum: - - Success - - Duplicate - - Invalid - - SameIssuerHostMustUseSameSecret - type: string type: object required: - spec diff --git a/generated/1.26/README.adoc b/generated/1.26/README.adoc index a8aa6a916..22bbcf2ac 100644 --- a/generated/1.26/README.adoc +++ b/generated/1.26/README.adoc @@ -726,9 +726,8 @@ FederationDomainStatus is a struct that describes the actual state of an OIDC Pr [cols="25a,75a", options="header"] |=== | Field | Description -| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-config-v1alpha1-federationdomainstatuscondition[$$FederationDomainStatusCondition$$]__ | Status holds an enum that describes the state of this OIDC Provider. Note that this Status can represent success or failure. -| *`message`* __string__ | Message provides human-readable details about the Status. -| *`lastUpdateTime`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#time-v1-meta[$$Time$$]__ | LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get around some undesirable behavior with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). +| *`phase`* __FederationDomainPhase__ | Phase summarizes the overall status of the FederationDomain. +| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | Conditions represent the observations of an FederationDomain's current state. | *`secrets`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-26-apis-supervisor-config-v1alpha1-federationdomainsecrets[$$FederationDomainSecrets$$]__ | Secrets contains information about this OIDC Provider's secrets. |=== diff --git a/generated/1.26/apis/supervisor/config/v1alpha1/types_federationdomain.go b/generated/1.26/apis/supervisor/config/v1alpha1/types_federationdomain.go index 2048b4e77..0290c618b 100644 --- a/generated/1.26/apis/supervisor/config/v1alpha1/types_federationdomain.go +++ b/generated/1.26/apis/supervisor/config/v1alpha1/types_federationdomain.go @@ -8,14 +8,17 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// +kubebuilder:validation:Enum=Success;Duplicate;Invalid;SameIssuerHostMustUseSameSecret -type FederationDomainStatusCondition string +type FederationDomainPhase string const ( - SuccessFederationDomainStatusCondition = FederationDomainStatusCondition("Success") - DuplicateFederationDomainStatusCondition = FederationDomainStatusCondition("Duplicate") - SameIssuerHostMustUseSameSecretFederationDomainStatusCondition = FederationDomainStatusCondition("SameIssuerHostMustUseSameSecret") - InvalidFederationDomainStatusCondition = FederationDomainStatusCondition("Invalid") + // FederationDomainPhasePending is the default phase for newly-created FederationDomain resources. + FederationDomainPhasePending FederationDomainPhase = "Pending" + + // FederationDomainPhaseReady is the phase for an FederationDomain resource in a healthy state. + FederationDomainPhaseReady FederationDomainPhase = "Ready" + + // FederationDomainPhaseError is the phase for an FederationDomain in an unhealthy state. + FederationDomainPhaseError FederationDomainPhase = "Error" ) // FederationDomainTLSSpec is a struct that describes the TLS configuration for an OIDC Provider. @@ -263,20 +266,17 @@ type FederationDomainSecrets struct { // FederationDomainStatus is a struct that describes the actual state of an OIDC Provider. type FederationDomainStatus struct { - // Status holds an enum that describes the state of this OIDC Provider. Note that this Status can - // represent success or failure. - // +optional - Status FederationDomainStatusCondition `json:"status,omitempty"` + // Phase summarizes the overall status of the FederationDomain. + // +kubebuilder:default=Pending + // +kubebuilder:validation:Enum=Pending;Ready;Error + Phase FederationDomainPhase `json:"phase,omitempty"` - // Message provides human-readable details about the Status. - // +optional - Message string `json:"message,omitempty"` - - // LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get - // around some undesirable behavior with respect to the empty metav1.Time value (see - // https://github.com/kubernetes/kubernetes/issues/86811). - // +optional - LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` + // Conditions represent the observations of an FederationDomain's current state. + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` // Secrets contains information about this OIDC Provider's secrets. // +optional @@ -288,7 +288,7 @@ type FederationDomainStatus struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` -// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status` +// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type FederationDomain struct { diff --git a/generated/1.26/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/1.26/apis/supervisor/config/v1alpha1/types_oidcclient.go index 48f5de378..61106fdba 100644 --- a/generated/1.26/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/1.26/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -8,14 +8,14 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" type OIDCClientPhase string const ( - // PhasePending is the default phase for newly-created OIDCClient resources. - PhasePending OIDCClientPhase = "Pending" + // OIDCClientPhasePending is the default phase for newly-created OIDCClient resources. + OIDCClientPhasePending OIDCClientPhase = "Pending" - // PhaseReady is the phase for an OIDCClient resource in a healthy state. - PhaseReady OIDCClientPhase = "Ready" + // OIDCClientPhaseReady is the phase for an OIDCClient resource in a healthy state. + OIDCClientPhaseReady OIDCClientPhase = "Ready" - // PhaseError is the phase for an OIDCClient in an unhealthy state. - PhaseError OIDCClientPhase = "Error" + // OIDCClientPhaseError is the phase for an OIDCClient in an unhealthy state. + OIDCClientPhaseError OIDCClientPhase = "Error" ) // +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/` diff --git a/generated/1.26/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go b/generated/1.26/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go index 241b1aa96..0800faf56 100644 --- a/generated/1.26/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.26/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go @@ -143,9 +143,12 @@ func (in *FederationDomainSpec) DeepCopy() *FederationDomainSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FederationDomainStatus) DeepCopyInto(out *FederationDomainStatus) { *out = *in - if in.LastUpdateTime != nil { - in, out := &in.LastUpdateTime, &out.LastUpdateTime - *out = (*in).DeepCopy() + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } out.Secrets = in.Secrets return diff --git a/generated/1.26/crds/config.supervisor.pinniped.dev_federationdomains.yaml b/generated/1.26/crds/config.supervisor.pinniped.dev_federationdomains.yaml index 396d25511..cb7e1a519 100644 --- a/generated/1.26/crds/config.supervisor.pinniped.dev_federationdomains.yaml +++ b/generated/1.26/crds/config.supervisor.pinniped.dev_federationdomains.yaml @@ -21,8 +21,8 @@ spec: - jsonPath: .spec.issuer name: Issuer type: string - - jsonPath: .status.status - name: Status + - jsonPath: .status.phase + name: Phase type: string - jsonPath: .metadata.creationTimestamp name: Age @@ -348,14 +348,80 @@ spec: status: description: Status of the OIDC provider. properties: - lastUpdateTime: - description: LastUpdateTime holds the time at which the Status was - last updated. It is a pointer to get around some undesirable behavior - with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). - format: date-time - type: string - message: - description: Message provides human-readable details about the Status. + conditions: + description: Conditions represent the observations of an FederationDomain's + current state. + items: + description: Condition status of a resource (mirrored from the metav1.Condition + type added in Kubernetes 1.19). In a future API version we can + switch to using the upstream type. See https://github.com/kubernetes/apimachinery/blob/v0.19.0/pkg/apis/meta/v1/types.go#L1353-L1413. + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + phase: + default: Pending + description: Phase summarizes the overall status of the FederationDomain. + enum: + - Pending + - Ready + - Error type: string secrets: description: Secrets contains information about this OIDC Provider's @@ -402,15 +468,6 @@ spec: type: string type: object type: object - status: - description: Status holds an enum that describes the state of this - OIDC Provider. Note that this Status can represent success or failure. - enum: - - Success - - Duplicate - - Invalid - - SameIssuerHostMustUseSameSecret - type: string type: object required: - spec diff --git a/generated/1.27/README.adoc b/generated/1.27/README.adoc index 463f3eb91..1f0e01aeb 100644 --- a/generated/1.27/README.adoc +++ b/generated/1.27/README.adoc @@ -726,9 +726,8 @@ FederationDomainStatus is a struct that describes the actual state of an OIDC Pr [cols="25a,75a", options="header"] |=== | Field | Description -| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-config-v1alpha1-federationdomainstatuscondition[$$FederationDomainStatusCondition$$]__ | Status holds an enum that describes the state of this OIDC Provider. Note that this Status can represent success or failure. -| *`message`* __string__ | Message provides human-readable details about the Status. -| *`lastUpdateTime`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#time-v1-meta[$$Time$$]__ | LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get around some undesirable behavior with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). +| *`phase`* __FederationDomainPhase__ | Phase summarizes the overall status of the FederationDomain. +| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | Conditions represent the observations of an FederationDomain's current state. | *`secrets`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-27-apis-supervisor-config-v1alpha1-federationdomainsecrets[$$FederationDomainSecrets$$]__ | Secrets contains information about this OIDC Provider's secrets. |=== diff --git a/generated/1.27/apis/supervisor/config/v1alpha1/types_federationdomain.go b/generated/1.27/apis/supervisor/config/v1alpha1/types_federationdomain.go index 2048b4e77..0290c618b 100644 --- a/generated/1.27/apis/supervisor/config/v1alpha1/types_federationdomain.go +++ b/generated/1.27/apis/supervisor/config/v1alpha1/types_federationdomain.go @@ -8,14 +8,17 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// +kubebuilder:validation:Enum=Success;Duplicate;Invalid;SameIssuerHostMustUseSameSecret -type FederationDomainStatusCondition string +type FederationDomainPhase string const ( - SuccessFederationDomainStatusCondition = FederationDomainStatusCondition("Success") - DuplicateFederationDomainStatusCondition = FederationDomainStatusCondition("Duplicate") - SameIssuerHostMustUseSameSecretFederationDomainStatusCondition = FederationDomainStatusCondition("SameIssuerHostMustUseSameSecret") - InvalidFederationDomainStatusCondition = FederationDomainStatusCondition("Invalid") + // FederationDomainPhasePending is the default phase for newly-created FederationDomain resources. + FederationDomainPhasePending FederationDomainPhase = "Pending" + + // FederationDomainPhaseReady is the phase for an FederationDomain resource in a healthy state. + FederationDomainPhaseReady FederationDomainPhase = "Ready" + + // FederationDomainPhaseError is the phase for an FederationDomain in an unhealthy state. + FederationDomainPhaseError FederationDomainPhase = "Error" ) // FederationDomainTLSSpec is a struct that describes the TLS configuration for an OIDC Provider. @@ -263,20 +266,17 @@ type FederationDomainSecrets struct { // FederationDomainStatus is a struct that describes the actual state of an OIDC Provider. type FederationDomainStatus struct { - // Status holds an enum that describes the state of this OIDC Provider. Note that this Status can - // represent success or failure. - // +optional - Status FederationDomainStatusCondition `json:"status,omitempty"` + // Phase summarizes the overall status of the FederationDomain. + // +kubebuilder:default=Pending + // +kubebuilder:validation:Enum=Pending;Ready;Error + Phase FederationDomainPhase `json:"phase,omitempty"` - // Message provides human-readable details about the Status. - // +optional - Message string `json:"message,omitempty"` - - // LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get - // around some undesirable behavior with respect to the empty metav1.Time value (see - // https://github.com/kubernetes/kubernetes/issues/86811). - // +optional - LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` + // Conditions represent the observations of an FederationDomain's current state. + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` // Secrets contains information about this OIDC Provider's secrets. // +optional @@ -288,7 +288,7 @@ type FederationDomainStatus struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` -// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status` +// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type FederationDomain struct { diff --git a/generated/1.27/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/1.27/apis/supervisor/config/v1alpha1/types_oidcclient.go index 48f5de378..61106fdba 100644 --- a/generated/1.27/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/1.27/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -8,14 +8,14 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" type OIDCClientPhase string const ( - // PhasePending is the default phase for newly-created OIDCClient resources. - PhasePending OIDCClientPhase = "Pending" + // OIDCClientPhasePending is the default phase for newly-created OIDCClient resources. + OIDCClientPhasePending OIDCClientPhase = "Pending" - // PhaseReady is the phase for an OIDCClient resource in a healthy state. - PhaseReady OIDCClientPhase = "Ready" + // OIDCClientPhaseReady is the phase for an OIDCClient resource in a healthy state. + OIDCClientPhaseReady OIDCClientPhase = "Ready" - // PhaseError is the phase for an OIDCClient in an unhealthy state. - PhaseError OIDCClientPhase = "Error" + // OIDCClientPhaseError is the phase for an OIDCClient in an unhealthy state. + OIDCClientPhaseError OIDCClientPhase = "Error" ) // +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/` diff --git a/generated/1.27/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go b/generated/1.27/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go index 241b1aa96..0800faf56 100644 --- a/generated/1.27/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.27/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go @@ -143,9 +143,12 @@ func (in *FederationDomainSpec) DeepCopy() *FederationDomainSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FederationDomainStatus) DeepCopyInto(out *FederationDomainStatus) { *out = *in - if in.LastUpdateTime != nil { - in, out := &in.LastUpdateTime, &out.LastUpdateTime - *out = (*in).DeepCopy() + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } out.Secrets = in.Secrets return diff --git a/generated/1.27/crds/config.supervisor.pinniped.dev_federationdomains.yaml b/generated/1.27/crds/config.supervisor.pinniped.dev_federationdomains.yaml index 396d25511..cb7e1a519 100644 --- a/generated/1.27/crds/config.supervisor.pinniped.dev_federationdomains.yaml +++ b/generated/1.27/crds/config.supervisor.pinniped.dev_federationdomains.yaml @@ -21,8 +21,8 @@ spec: - jsonPath: .spec.issuer name: Issuer type: string - - jsonPath: .status.status - name: Status + - jsonPath: .status.phase + name: Phase type: string - jsonPath: .metadata.creationTimestamp name: Age @@ -348,14 +348,80 @@ spec: status: description: Status of the OIDC provider. properties: - lastUpdateTime: - description: LastUpdateTime holds the time at which the Status was - last updated. It is a pointer to get around some undesirable behavior - with respect to the empty metav1.Time value (see https://github.com/kubernetes/kubernetes/issues/86811). - format: date-time - type: string - message: - description: Message provides human-readable details about the Status. + conditions: + description: Conditions represent the observations of an FederationDomain's + current state. + items: + description: Condition status of a resource (mirrored from the metav1.Condition + type added in Kubernetes 1.19). In a future API version we can + switch to using the upstream type. See https://github.com/kubernetes/apimachinery/blob/v0.19.0/pkg/apis/meta/v1/types.go#L1353-L1413. + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + phase: + default: Pending + description: Phase summarizes the overall status of the FederationDomain. + enum: + - Pending + - Ready + - Error type: string secrets: description: Secrets contains information about this OIDC Provider's @@ -402,15 +468,6 @@ spec: type: string type: object type: object - status: - description: Status holds an enum that describes the state of this - OIDC Provider. Note that this Status can represent success or failure. - enum: - - Success - - Duplicate - - Invalid - - SameIssuerHostMustUseSameSecret - type: string type: object required: - spec diff --git a/generated/latest/apis/supervisor/config/v1alpha1/types_federationdomain.go b/generated/latest/apis/supervisor/config/v1alpha1/types_federationdomain.go index 2048b4e77..0290c618b 100644 --- a/generated/latest/apis/supervisor/config/v1alpha1/types_federationdomain.go +++ b/generated/latest/apis/supervisor/config/v1alpha1/types_federationdomain.go @@ -8,14 +8,17 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// +kubebuilder:validation:Enum=Success;Duplicate;Invalid;SameIssuerHostMustUseSameSecret -type FederationDomainStatusCondition string +type FederationDomainPhase string const ( - SuccessFederationDomainStatusCondition = FederationDomainStatusCondition("Success") - DuplicateFederationDomainStatusCondition = FederationDomainStatusCondition("Duplicate") - SameIssuerHostMustUseSameSecretFederationDomainStatusCondition = FederationDomainStatusCondition("SameIssuerHostMustUseSameSecret") - InvalidFederationDomainStatusCondition = FederationDomainStatusCondition("Invalid") + // FederationDomainPhasePending is the default phase for newly-created FederationDomain resources. + FederationDomainPhasePending FederationDomainPhase = "Pending" + + // FederationDomainPhaseReady is the phase for an FederationDomain resource in a healthy state. + FederationDomainPhaseReady FederationDomainPhase = "Ready" + + // FederationDomainPhaseError is the phase for an FederationDomain in an unhealthy state. + FederationDomainPhaseError FederationDomainPhase = "Error" ) // FederationDomainTLSSpec is a struct that describes the TLS configuration for an OIDC Provider. @@ -263,20 +266,17 @@ type FederationDomainSecrets struct { // FederationDomainStatus is a struct that describes the actual state of an OIDC Provider. type FederationDomainStatus struct { - // Status holds an enum that describes the state of this OIDC Provider. Note that this Status can - // represent success or failure. - // +optional - Status FederationDomainStatusCondition `json:"status,omitempty"` + // Phase summarizes the overall status of the FederationDomain. + // +kubebuilder:default=Pending + // +kubebuilder:validation:Enum=Pending;Ready;Error + Phase FederationDomainPhase `json:"phase,omitempty"` - // Message provides human-readable details about the Status. - // +optional - Message string `json:"message,omitempty"` - - // LastUpdateTime holds the time at which the Status was last updated. It is a pointer to get - // around some undesirable behavior with respect to the empty metav1.Time value (see - // https://github.com/kubernetes/kubernetes/issues/86811). - // +optional - LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"` + // Conditions represent the observations of an FederationDomain's current state. + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` // Secrets contains information about this OIDC Provider's secrets. // +optional @@ -288,7 +288,7 @@ type FederationDomainStatus struct { // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories=pinniped // +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer` -// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status` +// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type FederationDomain struct { diff --git a/generated/latest/apis/supervisor/config/v1alpha1/types_oidcclient.go b/generated/latest/apis/supervisor/config/v1alpha1/types_oidcclient.go index 48f5de378..61106fdba 100644 --- a/generated/latest/apis/supervisor/config/v1alpha1/types_oidcclient.go +++ b/generated/latest/apis/supervisor/config/v1alpha1/types_oidcclient.go @@ -8,14 +8,14 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" type OIDCClientPhase string const ( - // PhasePending is the default phase for newly-created OIDCClient resources. - PhasePending OIDCClientPhase = "Pending" + // OIDCClientPhasePending is the default phase for newly-created OIDCClient resources. + OIDCClientPhasePending OIDCClientPhase = "Pending" - // PhaseReady is the phase for an OIDCClient resource in a healthy state. - PhaseReady OIDCClientPhase = "Ready" + // OIDCClientPhaseReady is the phase for an OIDCClient resource in a healthy state. + OIDCClientPhaseReady OIDCClientPhase = "Ready" - // PhaseError is the phase for an OIDCClient in an unhealthy state. - PhaseError OIDCClientPhase = "Error" + // OIDCClientPhaseError is the phase for an OIDCClient in an unhealthy state. + OIDCClientPhaseError OIDCClientPhase = "Error" ) // +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/` diff --git a/generated/latest/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go b/generated/latest/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go index 539158fac..e1af0fd0a 100644 --- a/generated/latest/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go +++ b/generated/latest/apis/supervisor/config/v1alpha1/zz_generated.deepcopy.go @@ -142,9 +142,12 @@ func (in *FederationDomainSpec) DeepCopy() *FederationDomainSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FederationDomainStatus) DeepCopyInto(out *FederationDomainStatus) { *out = *in - if in.LastUpdateTime != nil { - in, out := &in.LastUpdateTime, &out.LastUpdateTime - *out = (*in).DeepCopy() + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } out.Secrets = in.Secrets return diff --git a/internal/controller/conditionsutil/conditions_util.go b/internal/controller/conditionsutil/conditions_util.go index 2521a3a6c..0bbe3603a 100644 --- a/internal/controller/conditionsutil/conditions_util.go +++ b/internal/controller/conditionsutil/conditions_util.go @@ -67,11 +67,11 @@ func mergeIDPCondition(existing *[]v1.Condition, new *v1.Condition) bool { } // MergeConfigConditions merges conditions into conditionsToUpdate. If returns true if it merged any error conditions. -func MergeConfigConditions(conditions []*v1.Condition, observedGeneration int64, conditionsToUpdate *[]v1.Condition, log plog.MinLogger) bool { +func MergeConfigConditions(conditions []*v1.Condition, observedGeneration int64, conditionsToUpdate *[]v1.Condition, log plog.MinLogger, now v1.Time) bool { hadErrorCondition := false for i := range conditions { cond := conditions[i].DeepCopy() - cond.LastTransitionTime = v1.Now() + cond.LastTransitionTime = now cond.ObservedGeneration = observedGeneration if mergeConfigCondition(conditionsToUpdate, cond) { log.Info("updated condition", "type", cond.Type, "status", cond.Status, "reason", cond.Reason, "message", cond.Message) diff --git a/internal/controller/supervisorconfig/federation_domain_watcher.go b/internal/controller/supervisorconfig/federation_domain_watcher.go index 3923b498c..c18b9fb13 100644 --- a/internal/controller/supervisorconfig/federation_domain_watcher.go +++ b/internal/controller/supervisorconfig/federation_domain_watcher.go @@ -10,12 +10,11 @@ import ( "strings" "time" + "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/client-go/util/retry" - "k8s.io/klog/v2" "k8s.io/utils/clock" configv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1" @@ -24,12 +23,29 @@ import ( idpinformers "go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions/idp/v1alpha1" "go.pinniped.dev/internal/celtransformer" pinnipedcontroller "go.pinniped.dev/internal/controller" + "go.pinniped.dev/internal/controller/conditionsutil" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/federationdomain/federationdomainproviders" "go.pinniped.dev/internal/idtransform" "go.pinniped.dev/internal/plog" ) +const ( + typeReady = "Ready" + typeIssuerURLValid = "IssuerURLValid" + typeOneTLSSecretPerIssuerHostname = "OneTLSSecretPerIssuerHostname" + typeIssuerIsUnique = "IssuerIsUnique" + + reasonSuccess = "Success" + reasonNotReady = "NotReady" + reasonUnableToValidate = "UnableToValidate" + reasonInvalidIssuerURL = "InvalidIssuerURL" + reasonDuplicateIssuer = "DuplicateIssuer" + reasonDifferentSecretRefsFound = "DifferentSecretRefsFound" + + celTransformerMaxExpressionRuntime = 5 * time.Second +) + // FederationDomainsSetter can be notified of all known valid providers with its SetIssuer function. // If there are no longer any valid issuers, then it can be called with no arguments. // Implementations of this type should be thread-safe to support calls from multiple goroutines. @@ -109,75 +125,14 @@ func (c *federationDomainWatcherController) Sync(ctx controllerlib.Context) erro return err } - // Make a map of issuer strings -> count of how many times we saw that issuer string. - // This will help us complain when there are duplicate issuer strings. - // Also make a helper function for forming keys into this map. - issuerCounts := make(map[string]int) - issuerURLToIssuerKey := func(issuerURL *url.URL) string { - return fmt.Sprintf("%s://%s%s", issuerURL.Scheme, strings.ToLower(issuerURL.Host), issuerURL.Path) - } - - // Make a map of issuer hostnames -> set of unique secret names. This will help us complain when - // multiple FederationDomains have the same issuer hostname (excluding port) but specify - // different TLS serving Secrets. Doesn't make sense to have the one address use more than one - // TLS cert. Ignore ports because SNI information on the incoming requests is not going to include - // port numbers. Also make a helper function for forming keys into this map. - uniqueSecretNamesPerIssuerAddress := make(map[string]map[string]bool) - issuerURLToHostnameKey := lowercaseHostWithoutPort - - for _, federationDomain := range federationDomains { - issuerURL, err := url.Parse(federationDomain.Spec.Issuer) - if err != nil { - continue // Skip url parse errors because they will be validated again below. - } - - issuerCounts[issuerURLToIssuerKey(issuerURL)]++ - - setOfSecretNames := uniqueSecretNamesPerIssuerAddress[issuerURLToHostnameKey(issuerURL)] - if setOfSecretNames == nil { - setOfSecretNames = make(map[string]bool) - uniqueSecretNamesPerIssuerAddress[issuerURLToHostnameKey(issuerURL)] = setOfSecretNames - } - if federationDomain.Spec.TLS != nil { - setOfSecretNames[federationDomain.Spec.TLS.SecretName] = true - } - } - var errs []error - federationDomainIssuers := make([]*federationdomainproviders.FederationDomainIssuer, 0) + crossDomainConfigValidator := newCrossFederationDomainConfigValidator(federationDomains) + for _, federationDomain := range federationDomains { - issuerURL, urlParseErr := url.Parse(federationDomain.Spec.Issuer) + conditions := make([]*configv1alpha1.Condition, 0, 4) - // Skip url parse errors because they will be validated below. - if urlParseErr == nil { - if issuerCount := issuerCounts[issuerURLToIssuerKey(issuerURL)]; issuerCount > 1 { - if err := c.updateStatus( - ctx.Context, - federationDomain.Namespace, - federationDomain.Name, - configv1alpha1.DuplicateFederationDomainStatusCondition, - "Duplicate issuer: "+federationDomain.Spec.Issuer, - ); err != nil { - errs = append(errs, fmt.Errorf("could not update status: %w", err)) - } - continue - } - } - - // Skip url parse errors because they will be validated below. - if urlParseErr == nil && len(uniqueSecretNamesPerIssuerAddress[issuerURLToHostnameKey(issuerURL)]) > 1 { - if err := c.updateStatus( - ctx.Context, - federationDomain.Namespace, - federationDomain.Name, - configv1alpha1.SameIssuerHostMustUseSameSecretFederationDomainStatusCondition, - "Issuers with the same DNS hostname (address not including port) must use the same secretName: "+issuerURLToHostnameKey(issuerURL), - ); err != nil { - errs = append(errs, fmt.Errorf("could not update status: %w", err)) - } - continue - } + conditions = crossDomainConfigValidator.Validate(federationDomain, conditions) // TODO: Move all this identity provider stuff into helper functions. This is just a sketch of how the code would // work in the sense that this is not doing error handling, is not validating everything that it should, and @@ -232,7 +187,7 @@ func (c *federationDomainWatcherController) Sync(ctx controllerlib.Context) erro } // If there is an explicit list of IDPs on the FederationDomain, then process the list. - celTransformer, _ := celtransformer.NewCELTransformer(time.Second) // TODO: what is a good duration limit here? + celTransformer, _ := celtransformer.NewCELTransformer(celTransformerMaxExpressionRuntime) // TODO: what is a good duration limit here? // TODO: handle err for _, idp := range federationDomain.Spec.IdentityProviders { var idpResourceUID types.UID @@ -375,7 +330,7 @@ func (c *federationDomainWatcherController) Sync(ctx controllerlib.Context) erro } if !stringSlicesEqual(e.Expects.Groups, result.Groups) { // TODO: Do we need to make this insensitive to ordering, or should the transformations evaluator be changed to always return sorted group names at the end of the pipeline? - // TODO: What happens if the user did not write any group expectation? Treat it like expecting any empty list of groups? + // TODO: What happens if the user did not write any group expectation? Treat it like expecting an empty list of groups? // TODO: handle this failed example plog.Warning("FederationDomain identity provider transformations example failed: expected a different transformed groups list", "federationDomain", federationDomain.Name, @@ -402,7 +357,6 @@ func (c *federationDomainWatcherController) Sync(ctx controllerlib.Context) erro // Now that we have the list of IDPs for this FederationDomain, create the issuer. var federationDomainIssuer *federationdomainproviders.FederationDomainIssuer - err = nil if defaultFederationDomainIdentityProvider != nil { // This is the constructor for the backwards compatibility mode. federationDomainIssuer, err = federationdomainproviders.NewFederationDomainIssuerWithDefaultIDP(federationDomain.Spec.Issuer, defaultFederationDomainIdentityProvider) @@ -411,31 +365,32 @@ func (c *federationDomainWatcherController) Sync(ctx controllerlib.Context) erro federationDomainIssuer, err = federationdomainproviders.NewFederationDomainIssuer(federationDomain.Spec.Issuer, federationDomainIdentityProviders) } if err != nil { - // Note that the FederationDomainIssuer constructors validate the Issuer URL. - if err := c.updateStatus( - ctx.Context, - federationDomain.Namespace, - federationDomain.Name, - configv1alpha1.InvalidFederationDomainStatusCondition, - "Invalid: "+err.Error(), - ); err != nil { - errs = append(errs, fmt.Errorf("could not update status: %w", err)) - } - continue + // Note that the FederationDomainIssuer constructors only validate the Issuer URL, + // so these are always issuer URL validation errors. + conditions = append(conditions, &configv1alpha1.Condition{ + Type: typeIssuerURLValid, + Status: configv1alpha1.ConditionFalse, + Reason: reasonInvalidIssuerURL, + Message: err.Error(), + }) + } else { + conditions = append(conditions, &configv1alpha1.Condition{ + Type: typeIssuerURLValid, + Status: configv1alpha1.ConditionTrue, + Reason: reasonSuccess, + Message: "spec.issuer is a valid URL", + }) } - if err := c.updateStatus( - ctx.Context, - federationDomain.Namespace, - federationDomain.Name, - configv1alpha1.SuccessFederationDomainStatusCondition, - "Provider successfully created", - ); err != nil { + if err = c.updateStatus(ctx.Context, federationDomain, conditions); err != nil { errs = append(errs, fmt.Errorf("could not update status: %w", err)) continue } - federationDomainIssuers = append(federationDomainIssuers, federationDomainIssuer) + if !hadErrorCondition(conditions) { + // Successfully validated the FederationDomain, so allow it to be loaded. + federationDomainIssuers = append(federationDomainIssuers, federationDomainIssuer) + } } c.federationDomainsSetter.SetFederationDomains(federationDomainIssuers...) @@ -443,6 +398,160 @@ func (c *federationDomainWatcherController) Sync(ctx controllerlib.Context) erro return errors.NewAggregate(errs) } +func (c *federationDomainWatcherController) updateStatus( + ctx context.Context, + federationDomain *configv1alpha1.FederationDomain, + conditions []*configv1alpha1.Condition, +) error { + updated := federationDomain.DeepCopy() + + if hadErrorCondition(conditions) { + updated.Status.Phase = configv1alpha1.FederationDomainPhaseError + conditions = append(conditions, &configv1alpha1.Condition{ + Type: typeReady, + Status: configv1alpha1.ConditionFalse, + Reason: reasonNotReady, + Message: "the FederationDomain is not ready: see other conditions for details", + }) + } else { + updated.Status.Phase = configv1alpha1.FederationDomainPhaseReady + conditions = append(conditions, &configv1alpha1.Condition{ + Type: typeReady, + Status: configv1alpha1.ConditionTrue, + Reason: reasonSuccess, + Message: fmt.Sprintf("the FederationDomain is ready and its endpoints are available: "+ + "the discovery endpoint is %s/.well-known/openid-configuration", federationDomain.Spec.Issuer), + }) + } + + _ = conditionsutil.MergeConfigConditions(conditions, + federationDomain.Generation, &updated.Status.Conditions, plog.New(), metav1.NewTime(c.clock.Now())) + + if equality.Semantic.DeepEqual(federationDomain, updated) { + return nil + } + + _, err := c.client. + ConfigV1alpha1(). + FederationDomains(federationDomain.Namespace). + UpdateStatus(ctx, updated, metav1.UpdateOptions{}) + return err +} + +type crossFederationDomainConfigValidator struct { + issuerCounts map[string]int + uniqueSecretNamesPerIssuerAddress map[string]map[string]bool +} + +func issuerURLToHostnameKey(issuerURL *url.URL) string { + return lowercaseHostWithoutPort(issuerURL) +} + +func issuerURLToIssuerKey(issuerURL *url.URL) string { + return fmt.Sprintf("%s://%s%s", issuerURL.Scheme, strings.ToLower(issuerURL.Host), issuerURL.Path) +} + +func (v *crossFederationDomainConfigValidator) Validate(federationDomain *configv1alpha1.FederationDomain, conditions []*configv1alpha1.Condition) []*configv1alpha1.Condition { + issuerURL, urlParseErr := url.Parse(federationDomain.Spec.Issuer) + + if urlParseErr != nil { + // Don't write a condition about the issuer URL being invalid because that is added elsewhere in the controller. + conditions = append(conditions, &configv1alpha1.Condition{ + Type: typeIssuerIsUnique, + Status: configv1alpha1.ConditionUnknown, + Reason: reasonUnableToValidate, + Message: "unable to check if spec.issuer is unique among all FederationDomains because URL cannot be parsed", + }) + conditions = append(conditions, &configv1alpha1.Condition{ + Type: typeOneTLSSecretPerIssuerHostname, + Status: configv1alpha1.ConditionUnknown, + Reason: reasonUnableToValidate, + Message: "unable to check if all FederationDomains are using the same TLS secret when using the same hostname in the spec.issuer URL because URL cannot be parsed", + }) + return conditions + } + + if issuerCount := v.issuerCounts[issuerURLToIssuerKey(issuerURL)]; issuerCount > 1 { + conditions = append(conditions, &configv1alpha1.Condition{ + Type: typeIssuerIsUnique, + Status: configv1alpha1.ConditionFalse, + Reason: reasonDuplicateIssuer, + Message: "multiple FederationDomains have the same spec.issuer URL: these URLs must be unique (can use different hosts or paths)", + }) + } else { + conditions = append(conditions, &configv1alpha1.Condition{ + Type: typeIssuerIsUnique, + Status: configv1alpha1.ConditionTrue, + Reason: reasonSuccess, + Message: "spec.issuer is unique among all FederationDomains", + }) + } + + if len(v.uniqueSecretNamesPerIssuerAddress[issuerURLToHostnameKey(issuerURL)]) > 1 { + conditions = append(conditions, &configv1alpha1.Condition{ + Type: typeOneTLSSecretPerIssuerHostname, + Status: configv1alpha1.ConditionFalse, + Reason: reasonDifferentSecretRefsFound, + Message: "when different FederationDomains are using the same hostname in the spec.issuer URL then they must also use the same TLS secretRef: different secretRefs found", + }) + } else { + conditions = append(conditions, &configv1alpha1.Condition{ + Type: typeOneTLSSecretPerIssuerHostname, + Status: configv1alpha1.ConditionTrue, + Reason: reasonSuccess, + Message: "all FederationDomains are using the same TLS secret when using the same hostname in the spec.issuer URL", + }) + } + + return conditions +} + +func newCrossFederationDomainConfigValidator(federationDomains []*configv1alpha1.FederationDomain) *crossFederationDomainConfigValidator { + // Make a map of issuer strings -> count of how many times we saw that issuer string. + // This will help us complain when there are duplicate issuer strings. + // Also make a helper function for forming keys into this map. + issuerCounts := make(map[string]int) + + // Make a map of issuer hostnames -> set of unique secret names. This will help us complain when + // multiple FederationDomains have the same issuer hostname (excluding port) but specify + // different TLS serving Secrets. Doesn't make sense to have the one address use more than one + // TLS cert. Ignore ports because SNI information on the incoming requests is not going to include + // port numbers. Also make a helper function for forming keys into this map. + uniqueSecretNamesPerIssuerAddress := make(map[string]map[string]bool) + + for _, federationDomain := range federationDomains { + issuerURL, err := url.Parse(federationDomain.Spec.Issuer) + if err != nil { + continue // Skip url parse errors because they will be handled in the Validate function. + } + + issuerCounts[issuerURLToIssuerKey(issuerURL)]++ + + setOfSecretNames := uniqueSecretNamesPerIssuerAddress[issuerURLToHostnameKey(issuerURL)] + if setOfSecretNames == nil { + setOfSecretNames = make(map[string]bool) + uniqueSecretNamesPerIssuerAddress[issuerURLToHostnameKey(issuerURL)] = setOfSecretNames + } + if federationDomain.Spec.TLS != nil { + setOfSecretNames[federationDomain.Spec.TLS.SecretName] = true + } + } + + return &crossFederationDomainConfigValidator{ + issuerCounts: issuerCounts, + uniqueSecretNamesPerIssuerAddress: uniqueSecretNamesPerIssuerAddress, + } +} + +func hadErrorCondition(conditions []*configv1alpha1.Condition) bool { + for _, c := range conditions { + if c.Status != configv1alpha1.ConditionTrue { + return true + } + } + return false +} + func stringSlicesEqual(a []string, b []string) bool { if len(a) != len(b) { return false @@ -454,38 +563,3 @@ func stringSlicesEqual(a []string, b []string) bool { } return true } - -func (c *federationDomainWatcherController) updateStatus( - ctx context.Context, - namespace, name string, - status configv1alpha1.FederationDomainStatusCondition, - message string, -) error { - return retry.RetryOnConflict(retry.DefaultRetry, func() error { - federationDomain, err := c.client.ConfigV1alpha1().FederationDomains(namespace).Get(ctx, name, metav1.GetOptions{}) - if err != nil { - return fmt.Errorf("get failed: %w", err) - } - - if federationDomain.Status.Status == status && federationDomain.Status.Message == message { - return nil - } - - plog.Debug( - "attempting status update", - "federationdomain", - klog.KRef(namespace, name), - "status", - status, - "message", - message, - ) - federationDomain.Status.Status = status - federationDomain.Status.Message = message - federationDomain.Status.LastUpdateTime = timePtr(metav1.NewTime(c.clock.Now())) - _, err = c.client.ConfigV1alpha1().FederationDomains(namespace).UpdateStatus(ctx, federationDomain, metav1.UpdateOptions{}) - return err - }) -} - -func timePtr(t metav1.Time) *metav1.Time { return &t } diff --git a/internal/controller/supervisorconfig/federation_domain_watcher_test.go b/internal/controller/supervisorconfig/federation_domain_watcher_test.go index 0e75cdd34..f274424a2 100644 --- a/internal/controller/supervisorconfig/federation_domain_watcher_test.go +++ b/internal/controller/supervisorconfig/federation_domain_watcher_test.go @@ -16,7 +16,6 @@ import ( "github.com/sclevine/spec" "github.com/sclevine/spec/report" "github.com/stretchr/testify/require" - k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -28,7 +27,6 @@ import ( pinnipedinformers "go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions" "go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/federationdomain/federationdomainproviders" - "go.pinniped.dev/internal/here" "go.pinniped.dev/internal/testutil" ) @@ -113,8 +111,21 @@ func TestSync(t *testing.T) { var cancelContextCancelFunc context.CancelFunc var syncContext *controllerlib.Context var frozenNow time.Time + var frozenMetav1Now metav1.Time var federationDomainsSetter *fakeFederationDomainsSetter var federationDomainGVR schema.GroupVersionResource + var allHappyConditions func(issuer string, time metav1.Time, observedGeneration int64) []v1alpha1.Condition + var happyReadyCondition func(issuer string, time metav1.Time, observedGeneration int64) v1alpha1.Condition + var happyIssuerIsUniqueCondition, + unknownIssuerIsUniqueCondition, + sadIssuerIsUniqueCondition, + happyOneTLSSecretPerIssuerHostnameCondition, + unknownOneTLSSecretPerIssuerHostnameCondition, + sadOneTLSSecretPerIssuerHostnameCondition, + happyIssuerURLValidCondition, + sadIssuerURLValidConditionCannotHaveQuery, + sadIssuerURLValidConditionCannotParse, + sadReadyCondition func(time metav1.Time, observedGeneration int64) v1alpha1.Condition // Defer starting the informers until the last possible moment so that the // nested Before's can keep adding things to the informer caches. @@ -163,6 +174,139 @@ func TestSync(t *testing.T) { Version: v1alpha1.SchemeGroupVersion.Version, Resource: "federationdomains", } + + frozenMetav1Now = metav1.NewTime(frozenNow) + + happyReadyCondition = func(issuer string, time metav1.Time, observedGeneration int64) v1alpha1.Condition { + return v1alpha1.Condition{ + Type: "Ready", + Status: "True", + ObservedGeneration: observedGeneration, + LastTransitionTime: time, + Reason: "Success", + Message: fmt.Sprintf("the FederationDomain is ready and its endpoints are available: "+ + "the discovery endpoint is %s/.well-known/openid-configuration", issuer), + } + } + + sadReadyCondition = func(time metav1.Time, observedGeneration int64) v1alpha1.Condition { + return v1alpha1.Condition{ + Type: "Ready", + Status: "False", + ObservedGeneration: observedGeneration, + LastTransitionTime: time, + Reason: "NotReady", + Message: "the FederationDomain is not ready: see other conditions for details", + } + } + + happyIssuerIsUniqueCondition = func(time metav1.Time, observedGeneration int64) v1alpha1.Condition { + return v1alpha1.Condition{ + Type: "IssuerIsUnique", + Status: "True", + ObservedGeneration: observedGeneration, + LastTransitionTime: time, + Reason: "Success", + Message: "spec.issuer is unique among all FederationDomains", + } + } + + unknownIssuerIsUniqueCondition = func(time metav1.Time, observedGeneration int64) v1alpha1.Condition { + return v1alpha1.Condition{ + Type: "IssuerIsUnique", + Status: "Unknown", + ObservedGeneration: observedGeneration, + LastTransitionTime: time, + Reason: "UnableToValidate", + Message: "unable to check if spec.issuer is unique among all FederationDomains because URL cannot be parsed", + } + } + + sadIssuerIsUniqueCondition = func(time metav1.Time, observedGeneration int64) v1alpha1.Condition { + return v1alpha1.Condition{ + Type: "IssuerIsUnique", + Status: "False", + ObservedGeneration: observedGeneration, + LastTransitionTime: time, + Reason: "DuplicateIssuer", + Message: "multiple FederationDomains have the same spec.issuer URL: these URLs must be unique (can use different hosts or paths)", + } + } + + happyOneTLSSecretPerIssuerHostnameCondition = func(time metav1.Time, observedGeneration int64) v1alpha1.Condition { + return v1alpha1.Condition{ + Type: "OneTLSSecretPerIssuerHostname", + Status: "True", + ObservedGeneration: observedGeneration, + LastTransitionTime: time, + Reason: "Success", + Message: "all FederationDomains are using the same TLS secret when using the same hostname in the spec.issuer URL", + } + } + + unknownOneTLSSecretPerIssuerHostnameCondition = func(time metav1.Time, observedGeneration int64) v1alpha1.Condition { + return v1alpha1.Condition{ + Type: "OneTLSSecretPerIssuerHostname", + Status: "Unknown", + ObservedGeneration: observedGeneration, + LastTransitionTime: time, + Reason: "UnableToValidate", + Message: "unable to check if all FederationDomains are using the same TLS secret when using the same hostname in the spec.issuer URL because URL cannot be parsed", + } + } + + sadOneTLSSecretPerIssuerHostnameCondition = func(time metav1.Time, observedGeneration int64) v1alpha1.Condition { + return v1alpha1.Condition{ + Type: "OneTLSSecretPerIssuerHostname", + Status: "False", + ObservedGeneration: observedGeneration, + LastTransitionTime: time, + Reason: "DifferentSecretRefsFound", + Message: "when different FederationDomains are using the same hostname in the spec.issuer URL then they must also use the same TLS secretRef: different secretRefs found", + } + } + + happyIssuerURLValidCondition = func(time metav1.Time, observedGeneration int64) v1alpha1.Condition { + return v1alpha1.Condition{ + Type: "IssuerURLValid", + Status: "True", + ObservedGeneration: observedGeneration, + LastTransitionTime: time, + Reason: "Success", + Message: "spec.issuer is a valid URL", + } + } + + sadIssuerURLValidConditionCannotHaveQuery = func(time metav1.Time, observedGeneration int64) v1alpha1.Condition { + return v1alpha1.Condition{ + Type: "IssuerURLValid", + Status: "False", + ObservedGeneration: observedGeneration, + LastTransitionTime: time, + Reason: "InvalidIssuerURL", + Message: "issuer must not have query", + } + } + + sadIssuerURLValidConditionCannotParse = func(time metav1.Time, observedGeneration int64) v1alpha1.Condition { + return v1alpha1.Condition{ + Type: "IssuerURLValid", + Status: "False", + ObservedGeneration: observedGeneration, + LastTransitionTime: time, + Reason: "InvalidIssuerURL", + Message: `could not parse issuer as URL: parse ":/host//path": missing protocol scheme`, + } + } + + allHappyConditions = func(issuer string, time metav1.Time, observedGeneration int64) []v1alpha1.Condition { + return []v1alpha1.Condition{ + happyIssuerIsUniqueCondition(time, observedGeneration), + happyIssuerURLValidCondition(time, observedGeneration), + happyOneTLSSecretPerIssuerHostnameCondition(time, observedGeneration), + happyReadyCondition(issuer, time, observedGeneration), + } + } }) it.After(func() { @@ -177,14 +321,14 @@ func TestSync(t *testing.T) { it.Before(func() { federationDomain1 = &v1alpha1.FederationDomain{ - ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: namespace}, + ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: namespace, Generation: 123}, Spec: v1alpha1.FederationDomainSpec{Issuer: "https://issuer1.com"}, } r.NoError(pinnipedAPIClient.Tracker().Add(federationDomain1)) r.NoError(pinnipedInformerClient.Tracker().Add(federationDomain1)) federationDomain2 = &v1alpha1.FederationDomain{ - ObjectMeta: metav1.ObjectMeta{Name: "config2", Namespace: namespace}, + ObjectMeta: metav1.ObjectMeta{Name: "config2", Namespace: namespace, Generation: 123}, Spec: v1alpha1.FederationDomainSpec{Issuer: "https://issuer2.com"}, } r.NoError(pinnipedAPIClient.Tracker().Add(federationDomain2)) @@ -212,36 +356,24 @@ func TestSync(t *testing.T) { ) }) - it("updates the status to success in the FederationDomains", func() { + it("updates the status to ready in the FederationDomains", func() { startInformersAndController() err := controllerlib.TestSync(t, subject, *syncContext) r.NoError(err) - federationDomain1.Status.Status = v1alpha1.SuccessFederationDomainStatusCondition - federationDomain1.Status.Message = "Provider successfully created" - federationDomain1.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + federationDomain1.Status.Phase = v1alpha1.FederationDomainPhaseReady + federationDomain1.Status.Conditions = allHappyConditions(federationDomain1.Spec.Issuer, frozenMetav1Now, 123) - federationDomain2.Status.Status = v1alpha1.SuccessFederationDomainStatusCondition - federationDomain2.Status.Message = "Provider successfully created" - federationDomain2.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + federationDomain2.Status.Phase = v1alpha1.FederationDomainPhaseReady + federationDomain2.Status.Conditions = allHappyConditions(federationDomain2.Spec.Issuer, frozenMetav1Now, 123) expectedActions := []coretesting.Action{ - coretesting.NewGetAction( - federationDomainGVR, - federationDomain1.Namespace, - federationDomain1.Name, - ), coretesting.NewUpdateSubresourceAction( federationDomainGVR, "status", federationDomain1.Namespace, federationDomain1, ), - coretesting.NewGetAction( - federationDomainGVR, - federationDomain2.Namespace, - federationDomain2.Name, - ), coretesting.NewUpdateSubresourceAction( federationDomainGVR, "status", @@ -254,9 +386,8 @@ func TestSync(t *testing.T) { when("one FederationDomain is already up to date", func() { it.Before(func() { - federationDomain1.Status.Status = v1alpha1.SuccessFederationDomainStatusCondition - federationDomain1.Status.Message = "Provider successfully created" - federationDomain1.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + federationDomain1.Status.Phase = v1alpha1.FederationDomainPhaseReady + federationDomain1.Status.Conditions = allHappyConditions(federationDomain1.Spec.Issuer, frozenMetav1Now, 123) r.NoError(pinnipedAPIClient.Tracker().Update(federationDomainGVR, federationDomain1, federationDomain1.Namespace)) r.NoError(pinnipedInformerClient.Tracker().Update(federationDomainGVR, federationDomain1, federationDomain1.Namespace)) @@ -267,21 +398,10 @@ func TestSync(t *testing.T) { err := controllerlib.TestSync(t, subject, *syncContext) r.NoError(err) - federationDomain2.Status.Status = v1alpha1.SuccessFederationDomainStatusCondition - federationDomain2.Status.Message = "Provider successfully created" - federationDomain2.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + federationDomain2.Status.Phase = v1alpha1.FederationDomainPhaseReady + federationDomain2.Status.Conditions = allHappyConditions(federationDomain2.Spec.Issuer, frozenMetav1Now, 123) expectedActions := []coretesting.Action{ - coretesting.NewGetAction( - federationDomainGVR, - federationDomain1.Namespace, - federationDomain1.Name, - ), - coretesting.NewGetAction( - federationDomainGVR, - federationDomain2.Namespace, - federationDomain2.Name, - ), coretesting.NewUpdateSubresourceAction( federationDomainGVR, "status", @@ -314,7 +434,7 @@ func TestSync(t *testing.T) { }) }) - when("updating only one FederationDomain fails for a reason other than conflict", func() { + when("updating only one FederationDomain fails", func() { it.Before(func() { once := sync.Once{} pinnipedAPIClient.PrependReactor( @@ -354,31 +474,19 @@ func TestSync(t *testing.T) { err := controllerlib.TestSync(t, subject, *syncContext) r.EqualError(err, "could not update status: some update error") - federationDomain1.Status.Status = v1alpha1.SuccessFederationDomainStatusCondition - federationDomain1.Status.Message = "Provider successfully created" - federationDomain1.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + federationDomain1.Status.Phase = v1alpha1.FederationDomainPhaseReady + federationDomain1.Status.Conditions = allHappyConditions(federationDomain1.Spec.Issuer, frozenMetav1Now, 123) - federationDomain2.Status.Status = v1alpha1.SuccessFederationDomainStatusCondition - federationDomain2.Status.Message = "Provider successfully created" - federationDomain2.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + federationDomain2.Status.Phase = v1alpha1.FederationDomainPhaseReady + federationDomain2.Status.Conditions = allHappyConditions(federationDomain2.Spec.Issuer, frozenMetav1Now, 123) expectedActions := []coretesting.Action{ - coretesting.NewGetAction( - federationDomainGVR, - federationDomain1.Namespace, - federationDomain1.Name, - ), coretesting.NewUpdateSubresourceAction( federationDomainGVR, "status", federationDomain1.Namespace, federationDomain1, ), - coretesting.NewGetAction( - federationDomainGVR, - federationDomain2.Namespace, - federationDomain2.Name, - ), coretesting.NewUpdateSubresourceAction( federationDomainGVR, "status", @@ -398,67 +506,14 @@ func TestSync(t *testing.T) { it.Before(func() { federationDomain = &v1alpha1.FederationDomain{ - ObjectMeta: metav1.ObjectMeta{Name: "config", Namespace: namespace}, + ObjectMeta: metav1.ObjectMeta{Name: "config", Namespace: namespace, Generation: 123}, Spec: v1alpha1.FederationDomainSpec{Issuer: "https://issuer.com"}, } r.NoError(pinnipedAPIClient.Tracker().Add(federationDomain)) r.NoError(pinnipedInformerClient.Tracker().Add(federationDomain)) }) - when("there is a conflict while updating an FederationDomain", func() { - it.Before(func() { - once := sync.Once{} - pinnipedAPIClient.PrependReactor( - "update", - "federationdomains", - func(_ coretesting.Action) (bool, runtime.Object, error) { - var err error - once.Do(func() { - err = k8serrors.NewConflict(schema.GroupResource{}, "", nil) - }) - return true, nil, err - }, - ) - }) - - it("retries updating the FederationDomain", func() { - startInformersAndController() - err := controllerlib.TestSync(t, subject, *syncContext) - r.NoError(err) - - federationDomain.Status.Status = v1alpha1.SuccessFederationDomainStatusCondition - federationDomain.Status.Message = "Provider successfully created" - federationDomain.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) - - expectedActions := []coretesting.Action{ - coretesting.NewGetAction( - federationDomainGVR, - federationDomain.Namespace, - federationDomain.Name, - ), - coretesting.NewUpdateSubresourceAction( - federationDomainGVR, - "status", - federationDomain.Namespace, - federationDomain, - ), - coretesting.NewGetAction( - federationDomainGVR, - federationDomain.Namespace, - federationDomain.Name, - ), - coretesting.NewUpdateSubresourceAction( - federationDomainGVR, - "status", - federationDomain.Namespace, - federationDomain, - ), - } - r.Equal(expectedActions, pinnipedAPIClient.Actions()) - }) - }) - - when("updating the FederationDomain fails for a reason other than conflict", func() { + when("updating the FederationDomain fails", func() { it.Before(func() { pinnipedAPIClient.PrependReactor( "update", @@ -474,16 +529,10 @@ func TestSync(t *testing.T) { err := controllerlib.TestSync(t, subject, *syncContext) r.EqualError(err, "could not update status: some update error") - federationDomain.Status.Status = v1alpha1.SuccessFederationDomainStatusCondition - federationDomain.Status.Message = "Provider successfully created" - federationDomain.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + federationDomain.Status.Phase = v1alpha1.FederationDomainPhaseReady + federationDomain.Status.Conditions = allHappyConditions(federationDomain.Spec.Issuer, frozenMetav1Now, 123) expectedActions := []coretesting.Action{ - coretesting.NewGetAction( - federationDomainGVR, - federationDomain.Namespace, - federationDomain.Name, - ), coretesting.NewUpdateSubresourceAction( federationDomainGVR, "status", @@ -491,38 +540,7 @@ func TestSync(t *testing.T) { federationDomain, ), } - r.Equal(expectedActions, pinnipedAPIClient.Actions()) - }) - }) - - when("there is an error when getting the FederationDomain", func() { - it.Before(func() { - pinnipedAPIClient.PrependReactor( - "get", - "federationdomains", - func(_ coretesting.Action) (bool, runtime.Object, error) { - return true, nil, errors.New("some get error") - }, - ) - }) - - it("returns the get error", func() { - startInformersAndController() - err := controllerlib.TestSync(t, subject, *syncContext) - r.EqualError(err, "could not update status: get failed: some get error") - - federationDomain.Status.Status = v1alpha1.SuccessFederationDomainStatusCondition - federationDomain.Status.Message = "Provider successfully created" - federationDomain.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) - - expectedActions := []coretesting.Action{ - coretesting.NewGetAction( - federationDomainGVR, - federationDomain.Namespace, - federationDomain.Name, - ), - } - r.Equal(expectedActions, pinnipedAPIClient.Actions()) + r.ElementsMatch(expectedActions, pinnipedAPIClient.Actions()) }) }) }) @@ -535,14 +553,14 @@ func TestSync(t *testing.T) { it.Before(func() { validFederationDomain = &v1alpha1.FederationDomain{ - ObjectMeta: metav1.ObjectMeta{Name: "valid-config", Namespace: namespace}, + ObjectMeta: metav1.ObjectMeta{Name: "valid-config", Namespace: namespace, Generation: 123}, Spec: v1alpha1.FederationDomainSpec{Issuer: "https://valid-issuer.com"}, } r.NoError(pinnipedAPIClient.Tracker().Add(validFederationDomain)) r.NoError(pinnipedInformerClient.Tracker().Add(validFederationDomain)) invalidFederationDomain = &v1alpha1.FederationDomain{ - ObjectMeta: metav1.ObjectMeta{Name: "invalid-config", Namespace: namespace}, + ObjectMeta: metav1.ObjectMeta{Name: "invalid-config", Namespace: namespace, Generation: 123}, Spec: v1alpha1.FederationDomainSpec{Issuer: "https://invalid-issuer.com?some=query"}, } r.NoError(pinnipedAPIClient.Tracker().Add(invalidFederationDomain)) @@ -566,36 +584,29 @@ func TestSync(t *testing.T) { ) }) - it("updates the status to success/invalid in the FederationDomains", func() { + it("updates the status in each FederationDomain", func() { startInformersAndController() err := controllerlib.TestSync(t, subject, *syncContext) r.NoError(err) - validFederationDomain.Status.Status = v1alpha1.SuccessFederationDomainStatusCondition - validFederationDomain.Status.Message = "Provider successfully created" - validFederationDomain.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + validFederationDomain.Status.Phase = v1alpha1.FederationDomainPhaseReady + validFederationDomain.Status.Conditions = allHappyConditions(validFederationDomain.Spec.Issuer, frozenMetav1Now, 123) - invalidFederationDomain.Status.Status = v1alpha1.InvalidFederationDomainStatusCondition - invalidFederationDomain.Status.Message = "Invalid: issuer must not have query" - invalidFederationDomain.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + invalidFederationDomain.Status.Phase = v1alpha1.FederationDomainPhaseError + invalidFederationDomain.Status.Conditions = []v1alpha1.Condition{ + happyIssuerIsUniqueCondition(frozenMetav1Now, 123), + sadIssuerURLValidConditionCannotHaveQuery(frozenMetav1Now, 123), + happyOneTLSSecretPerIssuerHostnameCondition(frozenMetav1Now, 123), + sadReadyCondition(frozenMetav1Now, 123), + } expectedActions := []coretesting.Action{ - coretesting.NewGetAction( - federationDomainGVR, - invalidFederationDomain.Namespace, - invalidFederationDomain.Name, - ), coretesting.NewUpdateSubresourceAction( federationDomainGVR, "status", invalidFederationDomain.Namespace, invalidFederationDomain, ), - coretesting.NewGetAction( - federationDomainGVR, - validFederationDomain.Namespace, - validFederationDomain.Name, - ), coretesting.NewUpdateSubresourceAction( federationDomainGVR, "status", @@ -606,7 +617,7 @@ func TestSync(t *testing.T) { r.ElementsMatch(expectedActions, pinnipedAPIClient.Actions()) }) - when("updating only the invalid FederationDomain fails for a reason other than conflict", func() { + when("updating only the invalid FederationDomain fails", func() { it.Before(func() { pinnipedAPIClient.PrependReactor( "update", @@ -645,31 +656,24 @@ func TestSync(t *testing.T) { err := controllerlib.TestSync(t, subject, *syncContext) r.EqualError(err, "could not update status: some update error") - validFederationDomain.Status.Status = v1alpha1.SuccessFederationDomainStatusCondition - validFederationDomain.Status.Message = "Provider successfully created" - validFederationDomain.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + validFederationDomain.Status.Phase = v1alpha1.FederationDomainPhaseReady + validFederationDomain.Status.Conditions = allHappyConditions(validFederationDomain.Spec.Issuer, frozenMetav1Now, 123) - invalidFederationDomain.Status.Status = v1alpha1.InvalidFederationDomainStatusCondition - invalidFederationDomain.Status.Message = "Invalid: issuer must not have query" - invalidFederationDomain.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + invalidFederationDomain.Status.Phase = v1alpha1.FederationDomainPhaseError + invalidFederationDomain.Status.Conditions = []v1alpha1.Condition{ + happyIssuerIsUniqueCondition(frozenMetav1Now, 123), + sadIssuerURLValidConditionCannotHaveQuery(frozenMetav1Now, 123), + happyOneTLSSecretPerIssuerHostnameCondition(frozenMetav1Now, 123), + sadReadyCondition(frozenMetav1Now, 123), + } expectedActions := []coretesting.Action{ - coretesting.NewGetAction( - federationDomainGVR, - invalidFederationDomain.Namespace, - invalidFederationDomain.Name, - ), coretesting.NewUpdateSubresourceAction( federationDomainGVR, "status", invalidFederationDomain.Namespace, invalidFederationDomain, ), - coretesting.NewGetAction( - federationDomainGVR, - validFederationDomain.Namespace, - validFederationDomain.Name, - ), coretesting.NewUpdateSubresourceAction( federationDomainGVR, "status", @@ -693,20 +697,20 @@ func TestSync(t *testing.T) { // Hostnames are case-insensitive, so consider them to be duplicates if they only differ by case. // Paths are case-sensitive, so having a path that differs only by case makes a new issuer. federationDomainDuplicate1 = &v1alpha1.FederationDomain{ - ObjectMeta: metav1.ObjectMeta{Name: "duplicate1", Namespace: namespace}, + ObjectMeta: metav1.ObjectMeta{Name: "duplicate1", Namespace: namespace, Generation: 123}, Spec: v1alpha1.FederationDomainSpec{Issuer: "https://iSSueR-duPlicAte.cOm/a"}, } r.NoError(pinnipedAPIClient.Tracker().Add(federationDomainDuplicate1)) r.NoError(pinnipedInformerClient.Tracker().Add(federationDomainDuplicate1)) federationDomainDuplicate2 = &v1alpha1.FederationDomain{ - ObjectMeta: metav1.ObjectMeta{Name: "duplicate2", Namespace: namespace}, + ObjectMeta: metav1.ObjectMeta{Name: "duplicate2", Namespace: namespace, Generation: 123}, Spec: v1alpha1.FederationDomainSpec{Issuer: "https://issuer-duplicate.com/a"}, } r.NoError(pinnipedAPIClient.Tracker().Add(federationDomainDuplicate2)) r.NoError(pinnipedInformerClient.Tracker().Add(federationDomainDuplicate2)) federationDomain = &v1alpha1.FederationDomain{ - ObjectMeta: metav1.ObjectMeta{Name: "not-duplicate", Namespace: namespace}, + ObjectMeta: metav1.ObjectMeta{Name: "not-duplicate", Namespace: namespace, Generation: 123}, Spec: v1alpha1.FederationDomainSpec{Issuer: "https://issuer-duplicate.com/A"}, // different path } r.NoError(pinnipedAPIClient.Tracker().Add(federationDomain)) @@ -735,46 +739,38 @@ func TestSync(t *testing.T) { err := controllerlib.TestSync(t, subject, *syncContext) r.NoError(err) - federationDomain.Status.Status = v1alpha1.SuccessFederationDomainStatusCondition - federationDomain.Status.Message = "Provider successfully created" - federationDomain.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + federationDomain.Status.Phase = v1alpha1.FederationDomainPhaseReady + federationDomain.Status.Conditions = allHappyConditions(federationDomain.Spec.Issuer, frozenMetav1Now, 123) - federationDomainDuplicate1.Status.Status = v1alpha1.DuplicateFederationDomainStatusCondition - federationDomainDuplicate1.Status.Message = "Duplicate issuer: https://iSSueR-duPlicAte.cOm/a" - federationDomainDuplicate1.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + federationDomainDuplicate1.Status.Phase = v1alpha1.FederationDomainPhaseError + federationDomainDuplicate1.Status.Conditions = []v1alpha1.Condition{ + sadIssuerIsUniqueCondition(frozenMetav1Now, 123), + happyIssuerURLValidCondition(frozenMetav1Now, 123), + happyOneTLSSecretPerIssuerHostnameCondition(frozenMetav1Now, 123), + sadReadyCondition(frozenMetav1Now, 123), + } - federationDomainDuplicate2.Status.Status = v1alpha1.DuplicateFederationDomainStatusCondition - federationDomainDuplicate2.Status.Message = "Duplicate issuer: https://issuer-duplicate.com/a" - federationDomainDuplicate2.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + federationDomainDuplicate2.Status.Phase = v1alpha1.FederationDomainPhaseError + federationDomainDuplicate2.Status.Conditions = []v1alpha1.Condition{ + sadIssuerIsUniqueCondition(frozenMetav1Now, 123), + happyIssuerURLValidCondition(frozenMetav1Now, 123), + happyOneTLSSecretPerIssuerHostnameCondition(frozenMetav1Now, 123), + sadReadyCondition(frozenMetav1Now, 123), + } expectedActions := []coretesting.Action{ - coretesting.NewGetAction( - federationDomainGVR, - federationDomainDuplicate1.Namespace, - federationDomainDuplicate1.Name, - ), coretesting.NewUpdateSubresourceAction( federationDomainGVR, "status", federationDomainDuplicate1.Namespace, federationDomainDuplicate1, ), - coretesting.NewGetAction( - federationDomainGVR, - federationDomainDuplicate2.Namespace, - federationDomainDuplicate2.Name, - ), coretesting.NewUpdateSubresourceAction( federationDomainGVR, "status", federationDomainDuplicate2.Namespace, federationDomainDuplicate2, ), - coretesting.NewGetAction( - federationDomainGVR, - federationDomain.Namespace, - federationDomain.Name, - ), coretesting.NewUpdateSubresourceAction( federationDomainGVR, "status", @@ -784,50 +780,6 @@ func TestSync(t *testing.T) { } r.ElementsMatch(expectedActions, pinnipedAPIClient.Actions()) }) - - when("we cannot talk to the API", func() { - var count int - it.Before(func() { - pinnipedAPIClient.PrependReactor( - "get", - "federationdomains", - func(_ coretesting.Action) (bool, runtime.Object, error) { - count++ - return true, nil, fmt.Errorf("some get error %d", count) - }, - ) - }) - - it("returns the get errors", func() { - expectedError := here.Doc(`[could not update status: get failed: some get error 1, could not update status: get failed: some get error 2, could not update status: get failed: some get error 3]`) - startInformersAndController() - err := controllerlib.TestSync(t, subject, *syncContext) - r.EqualError(err, expectedError) - - federationDomain.Status.Status = v1alpha1.SuccessFederationDomainStatusCondition - federationDomain.Status.Message = "Provider successfully created" - federationDomain.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) - - expectedActions := []coretesting.Action{ - coretesting.NewGetAction( - federationDomainGVR, - federationDomainDuplicate1.Namespace, - federationDomainDuplicate1.Name, - ), - coretesting.NewGetAction( - federationDomainGVR, - federationDomainDuplicate2.Namespace, - federationDomainDuplicate2.Name, - ), - coretesting.NewGetAction( - federationDomainGVR, - federationDomain.Namespace, - federationDomain.Name, - ), - } - r.ElementsMatch(expectedActions, pinnipedAPIClient.Actions()) - }) - }) }) when("there are FederationDomains with the same issuer DNS hostname using different secretNames", func() { @@ -840,7 +792,7 @@ func TestSync(t *testing.T) { it.Before(func() { federationDomainSameIssuerAddress1 = &v1alpha1.FederationDomain{ - ObjectMeta: metav1.ObjectMeta{Name: "fd1", Namespace: namespace}, + ObjectMeta: metav1.ObjectMeta{Name: "fd1", Namespace: namespace, Generation: 123}, Spec: v1alpha1.FederationDomainSpec{ Issuer: "https://iSSueR-duPlicAte-adDress.cOm/path1", TLS: &v1alpha1.FederationDomainTLSSpec{SecretName: "secret1"}, @@ -849,7 +801,7 @@ func TestSync(t *testing.T) { r.NoError(pinnipedAPIClient.Tracker().Add(federationDomainSameIssuerAddress1)) r.NoError(pinnipedInformerClient.Tracker().Add(federationDomainSameIssuerAddress1)) federationDomainSameIssuerAddress2 = &v1alpha1.FederationDomain{ - ObjectMeta: metav1.ObjectMeta{Name: "fd2", Namespace: namespace}, + ObjectMeta: metav1.ObjectMeta{Name: "fd2", Namespace: namespace, Generation: 123}, Spec: v1alpha1.FederationDomainSpec{ // Validation treats these as the same DNS hostname even though they have different port numbers, // because SNI information on the incoming requests is not going to include port numbers. @@ -861,7 +813,7 @@ func TestSync(t *testing.T) { r.NoError(pinnipedInformerClient.Tracker().Add(federationDomainSameIssuerAddress2)) federationDomainDifferentIssuerAddress = &v1alpha1.FederationDomain{ - ObjectMeta: metav1.ObjectMeta{Name: "differentIssuerAddressFederationDomain", Namespace: namespace}, + ObjectMeta: metav1.ObjectMeta{Name: "differentIssuerAddressFederationDomain", Namespace: namespace, Generation: 123}, Spec: v1alpha1.FederationDomainSpec{ Issuer: "https://issuer-not-duplicate.com", TLS: &v1alpha1.FederationDomainTLSSpec{SecretName: "secret1"}, @@ -876,7 +828,7 @@ func TestSync(t *testing.T) { _, err := url.Parse(invalidIssuerURL) //nolint:staticcheck // Yes, this URL is intentionally invalid. r.Error(err) federationDomainWithInvalidIssuerURL = &v1alpha1.FederationDomain{ - ObjectMeta: metav1.ObjectMeta{Name: "invalidIssuerURLFederationDomain", Namespace: namespace}, + ObjectMeta: metav1.ObjectMeta{Name: "invalidIssuerURLFederationDomain", Namespace: namespace, Generation: 123}, Spec: v1alpha1.FederationDomainSpec{ Issuer: invalidIssuerURL, TLS: &v1alpha1.FederationDomainTLSSpec{SecretName: "secret1"}, @@ -908,27 +860,39 @@ func TestSync(t *testing.T) { err := controllerlib.TestSync(t, subject, *syncContext) r.NoError(err) - federationDomainDifferentIssuerAddress.Status.Status = v1alpha1.SuccessFederationDomainStatusCondition - federationDomainDifferentIssuerAddress.Status.Message = "Provider successfully created" - federationDomainDifferentIssuerAddress.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + federationDomainDifferentIssuerAddress.Status.Phase = v1alpha1.FederationDomainPhaseReady + federationDomainDifferentIssuerAddress.Status.Conditions = allHappyConditions(federationDomainDifferentIssuerAddress.Spec.Issuer, frozenMetav1Now, 123) - federationDomainSameIssuerAddress1.Status.Status = v1alpha1.SameIssuerHostMustUseSameSecretFederationDomainStatusCondition - federationDomainSameIssuerAddress1.Status.Message = "Issuers with the same DNS hostname (address not including port) must use the same secretName: issuer-duplicate-address.com" - federationDomainSameIssuerAddress1.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + federationDomainSameIssuerAddress1.Status.Phase = v1alpha1.FederationDomainPhaseError + federationDomainSameIssuerAddress1.Status.Conditions = []v1alpha1.Condition{ + happyIssuerIsUniqueCondition(frozenMetav1Now, 123), + happyIssuerURLValidCondition(frozenMetav1Now, 123), + sadOneTLSSecretPerIssuerHostnameCondition(frozenMetav1Now, 123), + sadReadyCondition(frozenMetav1Now, 123), + } - federationDomainSameIssuerAddress2.Status.Status = v1alpha1.SameIssuerHostMustUseSameSecretFederationDomainStatusCondition - federationDomainSameIssuerAddress2.Status.Message = "Issuers with the same DNS hostname (address not including port) must use the same secretName: issuer-duplicate-address.com" - federationDomainSameIssuerAddress2.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + federationDomainSameIssuerAddress2.Status.Phase = v1alpha1.FederationDomainPhaseError + federationDomainSameIssuerAddress2.Status.Conditions = []v1alpha1.Condition{ + happyIssuerIsUniqueCondition(frozenMetav1Now, 123), + happyIssuerURLValidCondition(frozenMetav1Now, 123), + sadOneTLSSecretPerIssuerHostnameCondition(frozenMetav1Now, 123), + sadReadyCondition(frozenMetav1Now, 123), + } - federationDomainWithInvalidIssuerURL.Status.Status = v1alpha1.InvalidFederationDomainStatusCondition - federationDomainWithInvalidIssuerURL.Status.Message = `Invalid: could not parse issuer as URL: parse ":/host//path": missing protocol scheme` - federationDomainWithInvalidIssuerURL.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) + federationDomainWithInvalidIssuerURL.Status.Phase = v1alpha1.FederationDomainPhaseError + federationDomainWithInvalidIssuerURL.Status.Conditions = []v1alpha1.Condition{ + unknownIssuerIsUniqueCondition(frozenMetav1Now, 123), + sadIssuerURLValidConditionCannotParse(frozenMetav1Now, 123), + unknownOneTLSSecretPerIssuerHostnameCondition(frozenMetav1Now, 123), + sadReadyCondition(frozenMetav1Now, 123), + } expectedActions := []coretesting.Action{ - coretesting.NewGetAction( + coretesting.NewUpdateSubresourceAction( federationDomainGVR, - federationDomainSameIssuerAddress1.Namespace, - federationDomainSameIssuerAddress1.Name, + "status", + federationDomainDifferentIssuerAddress.Namespace, + federationDomainDifferentIssuerAddress, ), coretesting.NewUpdateSubresourceAction( federationDomainGVR, @@ -936,33 +900,12 @@ func TestSync(t *testing.T) { federationDomainSameIssuerAddress1.Namespace, federationDomainSameIssuerAddress1, ), - coretesting.NewGetAction( - federationDomainGVR, - federationDomainSameIssuerAddress2.Namespace, - federationDomainSameIssuerAddress2.Name, - ), coretesting.NewUpdateSubresourceAction( federationDomainGVR, "status", federationDomainSameIssuerAddress2.Namespace, federationDomainSameIssuerAddress2, ), - coretesting.NewGetAction( - federationDomainGVR, - federationDomainDifferentIssuerAddress.Namespace, - federationDomainDifferentIssuerAddress.Name, - ), - coretesting.NewUpdateSubresourceAction( - federationDomainGVR, - "status", - federationDomainDifferentIssuerAddress.Namespace, - federationDomainDifferentIssuerAddress, - ), - coretesting.NewGetAction( - federationDomainGVR, - federationDomainWithInvalidIssuerURL.Namespace, - federationDomainWithInvalidIssuerURL.Name, - ), coretesting.NewUpdateSubresourceAction( federationDomainGVR, "status", @@ -972,55 +915,6 @@ func TestSync(t *testing.T) { } r.ElementsMatch(expectedActions, pinnipedAPIClient.Actions()) }) - - when("we cannot talk to the API", func() { - var count int - it.Before(func() { - pinnipedAPIClient.PrependReactor( - "get", - "federationdomains", - func(_ coretesting.Action) (bool, runtime.Object, error) { - count++ - return true, nil, fmt.Errorf("some get error %d", count) - }, - ) - }) - - it("returns the get errors", func() { - expectedError := here.Doc(`[could not update status: get failed: some get error 1, could not update status: get failed: some get error 2, could not update status: get failed: some get error 3, could not update status: get failed: some get error 4]`) - startInformersAndController() - err := controllerlib.TestSync(t, subject, *syncContext) - r.EqualError(err, expectedError) - - federationDomainDifferentIssuerAddress.Status.Status = v1alpha1.SuccessFederationDomainStatusCondition - federationDomainDifferentIssuerAddress.Status.Message = "Provider successfully created" - federationDomainDifferentIssuerAddress.Status.LastUpdateTime = timePtr(metav1.NewTime(frozenNow)) - - expectedActions := []coretesting.Action{ - coretesting.NewGetAction( - federationDomainGVR, - federationDomainSameIssuerAddress1.Namespace, - federationDomainSameIssuerAddress1.Name, - ), - coretesting.NewGetAction( - federationDomainGVR, - federationDomainSameIssuerAddress2.Namespace, - federationDomainSameIssuerAddress2.Name, - ), - coretesting.NewGetAction( - federationDomainGVR, - federationDomainDifferentIssuerAddress.Namespace, - federationDomainDifferentIssuerAddress.Name, - ), - coretesting.NewGetAction( - federationDomainGVR, - federationDomainWithInvalidIssuerURL.Namespace, - federationDomainWithInvalidIssuerURL.Name, - ), - } - r.ElementsMatch(expectedActions, pinnipedAPIClient.Actions()) - }) - }) }) when("there are no FederationDomains in the informer", func() { diff --git a/internal/controller/supervisorconfig/oidcclientwatcher/oidc_client_watcher.go b/internal/controller/supervisorconfig/oidcclientwatcher/oidc_client_watcher.go index 3209a7f27..918f91e51 100644 --- a/internal/controller/supervisorconfig/oidcclientwatcher/oidc_client_watcher.go +++ b/internal/controller/supervisorconfig/oidcclientwatcher/oidc_client_watcher.go @@ -133,11 +133,12 @@ func (c *oidcClientWatcherController) updateStatus( ) error { updated := upstream.DeepCopy() - hadErrorCondition := conditionsutil.MergeConfigConditions(conditions, upstream.Generation, &updated.Status.Conditions, plog.New()) + hadErrorCondition := conditionsutil.MergeConfigConditions(conditions, + upstream.Generation, &updated.Status.Conditions, plog.New(), metav1.Now()) - updated.Status.Phase = v1alpha1.PhaseReady + updated.Status.Phase = v1alpha1.OIDCClientPhaseReady if hadErrorCondition { - updated.Status.Phase = v1alpha1.PhaseError + updated.Status.Phase = v1alpha1.OIDCClientPhaseError } updated.Status.TotalClientSecrets = int32(totalClientSecrets) diff --git a/test/integration/e2e_test.go b/test/integration/e2e_test.go index 98df33dee..5e8c800ba 100644 --- a/test/integration/e2e_test.go +++ b/test/integration/e2e_test.go @@ -103,7 +103,7 @@ func TestE2EFullIntegration_Browser(t *testing.T) { downstream := testlib.CreateTestFederationDomain(topSetupCtx, t, issuerURL.String(), certSecret.Name, - configv1alpha1.SuccessFederationDomainStatusCondition, + configv1alpha1.FederationDomainPhaseReady, ) // Create a JWTAuthenticator that will validate the tokens from the downstream issuer. diff --git a/test/integration/kube_api_discovery_test.go b/test/integration/kube_api_discovery_test.go index 2884c7153..08727e6d4 100644 --- a/test/integration/kube_api_discovery_test.go +++ b/test/integration/kube_api_discovery_test.go @@ -441,7 +441,7 @@ func TestGetAPIResourceList(t *testing.T) { //nolint:gocyclo // each t.Run is pr // over time, make a rudimentary assertion that this test exercised the whole tree of all fields of all // Pinniped API resources. Without this, the test could accidentally skip parts of the tree if the // format has changed. - require.Equal(t, 254, foundFieldNames, + require.Equal(t, 259, foundFieldNames, "Expected to find all known fields of all Pinniped API resources. "+ "You may will need to update this expectation if you added new fields to the API types.", ) diff --git a/test/integration/supervisor_discovery_test.go b/test/integration/supervisor_discovery_test.go index 9813d9585..d591c139c 100644 --- a/test/integration/supervisor_discovery_test.go +++ b/test/integration/supervisor_discovery_test.go @@ -125,14 +125,24 @@ func TestSupervisorOIDCDiscovery_Disruptive(t *testing.T) { // When the same issuer is added twice, both issuers are marked as duplicates, and neither provider is serving. config6Duplicate1, _ := requireCreatingFederationDomainCausesDiscoveryEndpointsToAppear(ctx, t, scheme, addr, caBundle, issuer6, client) config6Duplicate2 := testlib.CreateTestFederationDomain(ctx, t, issuer6, "", "") - requireStatus(t, client, ns, config6Duplicate1.Name, v1alpha1.DuplicateFederationDomainStatusCondition) - requireStatus(t, client, ns, config6Duplicate2.Name, v1alpha1.DuplicateFederationDomainStatusCondition) + requireStatus(t, client, ns, config6Duplicate1.Name, v1alpha1.FederationDomainPhaseError, map[string]v1alpha1.ConditionStatus{ + "Ready": v1alpha1.ConditionFalse, + "IssuerIsUnique": v1alpha1.ConditionFalse, + "OneTLSSecretPerIssuerHostname": v1alpha1.ConditionTrue, + "IssuerURLValid": v1alpha1.ConditionTrue, + }) + requireStatus(t, client, ns, config6Duplicate2.Name, v1alpha1.FederationDomainPhaseError, map[string]v1alpha1.ConditionStatus{ + "Ready": v1alpha1.ConditionFalse, + "IssuerIsUnique": v1alpha1.ConditionFalse, + "OneTLSSecretPerIssuerHostname": v1alpha1.ConditionTrue, + "IssuerURLValid": v1alpha1.ConditionTrue, + }) requireDiscoveryEndpointsAreNotFound(t, scheme, addr, caBundle, issuer6) // If we delete the first duplicate issuer, the second duplicate issuer starts serving. requireDelete(t, client, ns, config6Duplicate1.Name) requireWellKnownEndpointIsWorking(t, scheme, addr, caBundle, issuer6, nil) - requireStatus(t, client, ns, config6Duplicate2.Name, v1alpha1.SuccessFederationDomainStatusCondition) + requireFullySuccessfulStatus(t, client, ns, config6Duplicate2.Name) // When we finally delete all issuers, the endpoint should be down. requireDeletingFederationDomainCausesDiscoveryEndpointsToDisappear(t, config6Duplicate2, client, ns, scheme, addr, caBundle, issuer6) @@ -144,7 +154,12 @@ func TestSupervisorOIDCDiscovery_Disruptive(t *testing.T) { // When we create a provider with an invalid issuer, the status is set to invalid. badConfig := testlib.CreateTestFederationDomain(ctx, t, badIssuer, "", "") - requireStatus(t, client, ns, badConfig.Name, v1alpha1.InvalidFederationDomainStatusCondition) + requireStatus(t, client, ns, badConfig.Name, v1alpha1.FederationDomainPhaseError, map[string]v1alpha1.ConditionStatus{ + "Ready": v1alpha1.ConditionFalse, + "IssuerIsUnique": v1alpha1.ConditionTrue, + "OneTLSSecretPerIssuerHostname": v1alpha1.ConditionTrue, + "IssuerURLValid": v1alpha1.ConditionFalse, + }) requireDiscoveryEndpointsAreNotFound(t, scheme, addr, caBundle, badIssuer) requireDeletingFederationDomainCausesDiscoveryEndpointsToDisappear(t, badConfig, client, ns, scheme, addr, caBundle, badIssuer) }) @@ -172,7 +187,7 @@ func TestSupervisorTLSTerminationWithSNI_Disruptive(t *testing.T) { // Create an FederationDomain with a spec.tls.secretName. federationDomain1 := testlib.CreateTestFederationDomain(ctx, t, issuer1, certSecretName1, "") - requireStatus(t, pinnipedClient, federationDomain1.Namespace, federationDomain1.Name, v1alpha1.SuccessFederationDomainStatusCondition) + requireFullySuccessfulStatus(t, pinnipedClient, federationDomain1.Namespace, federationDomain1.Name) // The spec.tls.secretName Secret does not exist, so the endpoints should fail with TLS errors. requireEndpointHasBootstrapTLSErrorBecauseCertificatesAreNotReady(t, issuer1) @@ -212,7 +227,7 @@ func TestSupervisorTLSTerminationWithSNI_Disruptive(t *testing.T) { // Create an FederationDomain with a spec.tls.secretName. federationDomain2 := testlib.CreateTestFederationDomain(ctx, t, issuer2, certSecretName2, "") - requireStatus(t, pinnipedClient, federationDomain2.Namespace, federationDomain2.Name, v1alpha1.SuccessFederationDomainStatusCondition) + requireFullySuccessfulStatus(t, pinnipedClient, federationDomain2.Namespace, federationDomain2.Name) // Create the Secret. ca2 := createTLSCertificateSecret(ctx, t, ns, hostname2, nil, certSecretName2, kubeClient) @@ -256,7 +271,7 @@ func TestSupervisorTLSTerminationWithDefaultCerts_Disruptive(t *testing.T) { // Create an FederationDomain without a spec.tls.secretName. federationDomain1 := testlib.CreateTestFederationDomain(ctx, t, issuerUsingIPAddress, "", "") - requireStatus(t, pinnipedClient, federationDomain1.Namespace, federationDomain1.Name, v1alpha1.SuccessFederationDomainStatusCondition) + requireFullySuccessfulStatus(t, pinnipedClient, federationDomain1.Namespace, federationDomain1.Name) // There is no default TLS cert and the spec.tls.secretName was not set, so the endpoints should fail with TLS errors. requireEndpointHasBootstrapTLSErrorBecauseCertificatesAreNotReady(t, issuerUsingIPAddress) @@ -270,7 +285,7 @@ func TestSupervisorTLSTerminationWithDefaultCerts_Disruptive(t *testing.T) { // Create an FederationDomain with a spec.tls.secretName. certSecretName := "integration-test-cert-1" federationDomain2 := testlib.CreateTestFederationDomain(ctx, t, issuerUsingHostname, certSecretName, "") - requireStatus(t, pinnipedClient, federationDomain2.Namespace, federationDomain2.Name, v1alpha1.SuccessFederationDomainStatusCondition) + requireFullySuccessfulStatus(t, pinnipedClient, federationDomain2.Namespace, federationDomain2.Name) // Create the Secret. certCA := createTLSCertificateSecret(ctx, t, ns, hostname, nil, certSecretName, kubeClient) @@ -458,7 +473,7 @@ func requireCreatingFederationDomainCausesDiscoveryEndpointsToAppear( t.Helper() newFederationDomain := testlib.CreateTestFederationDomain(ctx, t, issuerName, "", "") jwksResult := requireDiscoveryEndpointsAreWorking(t, supervisorScheme, supervisorAddress, supervisorCABundle, issuerName, nil) - requireStatus(t, client, newFederationDomain.Namespace, newFederationDomain.Name, v1alpha1.SuccessFederationDomainStatusCondition) + requireFullySuccessfulStatus(t, client, newFederationDomain.Namespace, newFederationDomain.Name) return newFederationDomain, jwksResult } @@ -626,7 +641,16 @@ func requireDelete(t *testing.T, client pinnipedclientset.Interface, ns, name st require.NoError(t, err) } -func requireStatus(t *testing.T, client pinnipedclientset.Interface, ns, name string, status v1alpha1.FederationDomainStatusCondition) { +func requireFullySuccessfulStatus(t *testing.T, client pinnipedclientset.Interface, ns, name string) { + requireStatus(t, client, ns, name, v1alpha1.FederationDomainPhaseReady, map[string]v1alpha1.ConditionStatus{ + "Ready": v1alpha1.ConditionTrue, + "IssuerIsUnique": v1alpha1.ConditionTrue, + "OneTLSSecretPerIssuerHostname": v1alpha1.ConditionTrue, + "IssuerURLValid": v1alpha1.ConditionTrue, + }) +} + +func requireStatus(t *testing.T, client pinnipedclientset.Interface, ns, name string, phase v1alpha1.FederationDomainPhase, conditionTypeToStatus map[string]v1alpha1.ConditionStatus) { t.Helper() testlib.RequireEventually(t, func(requireEventually *require.Assertions) { @@ -636,8 +660,14 @@ func requireStatus(t *testing.T, client pinnipedclientset.Interface, ns, name st federationDomain, err := client.ConfigV1alpha1().FederationDomains(ns).Get(ctx, name, metav1.GetOptions{}) requireEventually.NoError(err) - t.Logf("found FederationDomain %s/%s with status %s", ns, name, federationDomain.Status.Status) - requireEventually.Equalf(status, federationDomain.Status.Status, "unexpected status (message = '%s')", federationDomain.Status.Message) + t.Logf("found FederationDomain %s/%s with phase %s", ns, name, federationDomain.Status.Phase) + requireEventually.Equalf(phase, federationDomain.Status.Phase, "unexpected phase (conditions = '%#v')", federationDomain.Status.Conditions) + + actualConditionTypeToStatus := map[string]v1alpha1.ConditionStatus{} + for _, c := range federationDomain.Status.Conditions { + actualConditionTypeToStatus[c.Type] = c.Status + } + requireEventually.Equal(conditionTypeToStatus, actualConditionTypeToStatus, "unexpected statuses for conditions by type") }, 5*time.Minute, 200*time.Millisecond) } diff --git a/test/integration/supervisor_login_test.go b/test/integration/supervisor_login_test.go index 3bfd780d3..8b4728100 100644 --- a/test/integration/supervisor_login_test.go +++ b/test/integration/supervisor_login_test.go @@ -1469,7 +1469,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { AllowedRedirectURIs: []configv1alpha1.RedirectURI{configv1alpha1.RedirectURI(callbackURL)}, AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code", "urn:ietf:params:oauth:grant-type:token-exchange", "refresh_token"}, AllowedScopes: []configv1alpha1.Scope{"openid", "offline_access", "pinniped:request-audience", "username", "groups"}, - }, configv1alpha1.PhaseReady) + }, configv1alpha1.OIDCClientPhaseReady) }, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, // the ID token Subject should include the upstream user ID after the upstream issuer name @@ -1502,7 +1502,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { AllowedRedirectURIs: []configv1alpha1.RedirectURI{configv1alpha1.RedirectURI(callbackURL)}, AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code", "urn:ietf:params:oauth:grant-type:token-exchange", "refresh_token"}, AllowedScopes: []configv1alpha1.Scope{"openid", "offline_access", "pinniped:request-audience", "username", "groups"}, - }, configv1alpha1.PhaseReady) + }, configv1alpha1.OIDCClientPhaseReady) }, requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC, // the ID token Subject should include the upstream user ID after the upstream issuer name @@ -1526,7 +1526,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { AllowedRedirectURIs: []configv1alpha1.RedirectURI{configv1alpha1.RedirectURI(callbackURL)}, AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code", "urn:ietf:params:oauth:grant-type:token-exchange", "refresh_token"}, AllowedScopes: []configv1alpha1.Scope{"openid", "offline_access", "pinniped:request-audience", "username", "groups"}, - }, configv1alpha1.PhaseReady) + }, configv1alpha1.OIDCClientPhaseReady) }, testUser: func(t *testing.T) (string, string) { // return the username and password of the existing user that we want to use for this test @@ -1558,7 +1558,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { AllowedRedirectURIs: []configv1alpha1.RedirectURI{configv1alpha1.RedirectURI(callbackURL)}, AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code", "refresh_token"}, // token exchange grant type not allowed AllowedScopes: []configv1alpha1.Scope{"openid", "offline_access", "username", "groups"}, // a validation requires that we also disallow the pinniped:request-audience scope - }, configv1alpha1.PhaseReady) + }, configv1alpha1.OIDCClientPhaseReady) }, testUser: func(t *testing.T) (string, string) { // return the username and password of the existing user that we want to use for this test @@ -1592,7 +1592,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { AllowedRedirectURIs: []configv1alpha1.RedirectURI{configv1alpha1.RedirectURI(callbackURL)}, AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code", "urn:ietf:params:oauth:grant-type:token-exchange", "refresh_token"}, AllowedScopes: []configv1alpha1.Scope{"openid", "offline_access", "pinniped:request-audience", "username", "groups"}, - }, configv1alpha1.PhaseReady) + }, configv1alpha1.OIDCClientPhaseReady) }, testUser: func(t *testing.T) (string, string) { // return the username and password of the existing user that we want to use for this test @@ -1626,7 +1626,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { AllowedRedirectURIs: []configv1alpha1.RedirectURI{configv1alpha1.RedirectURI(callbackURL)}, AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code", "refresh_token"}, // token exchange not allowed (required to exclude groups scope) AllowedScopes: []configv1alpha1.Scope{"openid", "offline_access", "groups"}, // username not allowed - }, configv1alpha1.PhaseReady) + }, configv1alpha1.OIDCClientPhaseReady) }, testUser: func(t *testing.T) (string, string) { // return the username and password of the existing user that we want to use for this test @@ -1652,7 +1652,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { AllowedRedirectURIs: []configv1alpha1.RedirectURI{configv1alpha1.RedirectURI(callbackURL)}, AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code", "refresh_token"}, // token exchange not allowed (required to exclude groups scope) AllowedScopes: []configv1alpha1.Scope{"openid", "offline_access", "username"}, // groups not allowed - }, configv1alpha1.PhaseReady) + }, configv1alpha1.OIDCClientPhaseReady) }, testUser: func(t *testing.T) (string, string) { // return the username and password of the existing user that we want to use for this test @@ -1678,7 +1678,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { AllowedRedirectURIs: []configv1alpha1.RedirectURI{configv1alpha1.RedirectURI(callbackURL)}, AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code", "urn:ietf:params:oauth:grant-type:token-exchange", "refresh_token"}, AllowedScopes: []configv1alpha1.Scope{"openid", "offline_access", "pinniped:request-audience", "username", "groups"}, - }, configv1alpha1.PhaseReady) + }, configv1alpha1.OIDCClientPhaseReady) }, testUser: func(t *testing.T) (string, string) { // return the username and password of the existing user that we want to use for this test @@ -1711,7 +1711,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { AllowedRedirectURIs: []configv1alpha1.RedirectURI{configv1alpha1.RedirectURI(callbackURL)}, AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code", "urn:ietf:params:oauth:grant-type:token-exchange", "refresh_token"}, AllowedScopes: []configv1alpha1.Scope{"openid", "offline_access", "pinniped:request-audience", "username", "groups"}, - }, configv1alpha1.PhaseReady) + }, configv1alpha1.OIDCClientPhaseReady) }, testUser: func(t *testing.T) (string, string) { // return the username and password of the existing user that we want to use for this test @@ -1750,7 +1750,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { AllowedRedirectURIs: []configv1alpha1.RedirectURI{configv1alpha1.RedirectURI(callbackURL)}, AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code", "refresh_token"}, AllowedScopes: []configv1alpha1.Scope{"openid", "offline_access"}, // validations require that when username/groups are excluded, then token exchange must also not be allowed - }, configv1alpha1.PhaseReady) + }, configv1alpha1.OIDCClientPhaseReady) }, testUser: func(t *testing.T) (string, string) { // return the username and password of the existing user that we want to use for this test @@ -1789,7 +1789,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { AllowedRedirectURIs: []configv1alpha1.RedirectURI{configv1alpha1.RedirectURI(callbackURL)}, AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code", "urn:ietf:params:oauth:grant-type:token-exchange", "refresh_token"}, AllowedScopes: []configv1alpha1.Scope{"openid", "offline_access", "pinniped:request-audience", "username", "groups"}, - }, configv1alpha1.PhaseReady) + }, configv1alpha1.OIDCClientPhaseReady) }, requestAuthorization: func(t *testing.T, downstreamIssuer, downstreamAuthorizeURL, downstreamCallbackURL, _, _ string, httpClient *http.Client) { requestAuthorizationUsingBrowserAuthcodeFlowLDAP(t, @@ -1825,7 +1825,7 @@ func TestSupervisorLogin_Browser(t *testing.T) { AllowedRedirectURIs: []configv1alpha1.RedirectURI{configv1alpha1.RedirectURI(callbackURL)}, AllowedGrantTypes: []configv1alpha1.GrantType{"authorization_code", "urn:ietf:params:oauth:grant-type:token-exchange", "refresh_token"}, AllowedScopes: []configv1alpha1.Scope{"openid", "offline_access", "pinniped:request-audience", "username", "groups"}, - }, configv1alpha1.PhaseReady) + }, configv1alpha1.OIDCClientPhaseReady) return clientID, "wrong-client-secret" }, testUser: func(t *testing.T) (string, string) { @@ -2080,7 +2080,7 @@ func testSupervisorLogin( downstream := testlib.CreateTestFederationDomain(ctx, t, issuerURL.String(), certSecret.Name, - configv1alpha1.SuccessFederationDomainStatusCondition, + configv1alpha1.FederationDomainPhaseReady, // TODO: expect another phase because this is a legacy FederationDomain and there is no IDP yet, so it is not safe to try to do logins until the IDP exists and the controller has a chance to run again to set the default IDP ) // Ensure the the JWKS data is created and ready for the new FederationDomain by waiting for @@ -2104,6 +2104,9 @@ func testSupervisorLogin( // Create upstream IDP and wait for it to become ready. idpName := createIDP(t) + // Now that both the FederationDomain and the IDP are created, the FederationDomain should be ready. + testlib.WaitForTestFederationDomainStatus(ctx, t, downstream.Name, configv1alpha1.FederationDomainPhaseReady) + // Start a callback server on localhost. localCallbackServer := startLocalCallbackServer(t) diff --git a/test/integration/supervisor_warnings_test.go b/test/integration/supervisor_warnings_test.go index a025c8887..6068ee8c4 100644 --- a/test/integration/supervisor_warnings_test.go +++ b/test/integration/supervisor_warnings_test.go @@ -85,7 +85,7 @@ func TestSupervisorWarnings_Browser(t *testing.T) { downstream := testlib.CreateTestFederationDomain(ctx, t, issuerURL.String(), certSecret.Name, - configv1alpha1.SuccessFederationDomainStatusCondition, + configv1alpha1.FederationDomainPhaseReady, ) // Create a JWTAuthenticator that will validate the tokens from the downstream issuer. diff --git a/test/testlib/client.go b/test/testlib/client.go index 55f208c11..4ab526a52 100644 --- a/test/testlib/client.go +++ b/test/testlib/client.go @@ -272,7 +272,13 @@ func CreateTestJWTAuthenticator(ctx context.Context, t *testing.T, spec auth1alp // current test's lifetime. // If the provided issuer is not the empty string, then it will be used for the // FederationDomain.Spec.Issuer field. Else, a random issuer will be generated. -func CreateTestFederationDomain(ctx context.Context, t *testing.T, issuer string, certSecretName string, expectStatus configv1alpha1.FederationDomainStatusCondition) *configv1alpha1.FederationDomain { +func CreateTestFederationDomain( + ctx context.Context, + t *testing.T, + issuer string, + certSecretName string, + expectStatus configv1alpha1.FederationDomainPhase, +) *configv1alpha1.FederationDomain { t.Helper() testEnv := IntegrationEnv(t) @@ -283,8 +289,8 @@ func CreateTestFederationDomain(ctx context.Context, t *testing.T, issuer string issuer = fmt.Sprintf("http://test-issuer-%s.pinniped.dev", RandHex(t, 8)) } - federationDomains := NewSupervisorClientset(t).ConfigV1alpha1().FederationDomains(testEnv.SupervisorNamespace) - federationDomain, err := federationDomains.Create(createContext, &configv1alpha1.FederationDomain{ + federationDomainsClient := NewSupervisorClientset(t).ConfigV1alpha1().FederationDomains(testEnv.SupervisorNamespace) + federationDomain, err := federationDomainsClient.Create(createContext, &configv1alpha1.FederationDomain{ ObjectMeta: testObjectMeta(t, "oidc-provider"), Spec: configv1alpha1.FederationDomainSpec{ Issuer: issuer, @@ -299,7 +305,7 @@ func CreateTestFederationDomain(ctx context.Context, t *testing.T, issuer string t.Logf("cleaning up test FederationDomain %s/%s", federationDomain.Namespace, federationDomain.Name) deleteCtx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() - err := federationDomains.Delete(deleteCtx, federationDomain.Name, metav1.DeleteOptions{}) + err := federationDomainsClient.Delete(deleteCtx, federationDomain.Name, metav1.DeleteOptions{}) notFound := k8serrors.IsNotFound(err) // It's okay if it is not found, because it might have been deleted by another part of this test. if !notFound { @@ -313,22 +319,31 @@ func CreateTestFederationDomain(ctx context.Context, t *testing.T, issuer string } // Wait for the FederationDomain to enter the expected phase (or time out). + WaitForTestFederationDomainStatus(ctx, t, federationDomain.Name, expectStatus) + + return federationDomain +} + +func WaitForTestFederationDomainStatus(ctx context.Context, t *testing.T, federationDomainName string, expectStatus configv1alpha1.FederationDomainPhase) { + t.Helper() + testEnv := IntegrationEnv(t) + federationDomainsClient := NewSupervisorClientset(t).ConfigV1alpha1().FederationDomains(testEnv.SupervisorNamespace) + var result *configv1alpha1.FederationDomain RequireEventuallyf(t, func(requireEventually *require.Assertions) { var err error - result, err = federationDomains.Get(ctx, federationDomain.Name, metav1.GetOptions{}) + result, err = federationDomainsClient.Get(ctx, federationDomainName, metav1.GetOptions{}) requireEventually.NoError(err) - requireEventually.Equal(expectStatus, result.Status.Status) + requireEventually.Equal(expectStatus, result.Status.Phase) // If the FederationDomain was successfully created, ensure all secrets are present before continuing - if expectStatus == configv1alpha1.SuccessFederationDomainStatusCondition { + if expectStatus == configv1alpha1.FederationDomainPhaseReady { requireEventually.NotEmpty(result.Status.Secrets.JWKS.Name, "expected status.secrets.jwks.name not to be empty") requireEventually.NotEmpty(result.Status.Secrets.TokenSigningKey.Name, "expected status.secrets.tokenSigningKey.name not to be empty") requireEventually.NotEmpty(result.Status.Secrets.StateSigningKey.Name, "expected status.secrets.stateSigningKey.name not to be empty") requireEventually.NotEmpty(result.Status.Secrets.StateEncryptionKey.Name, "expected status.secrets.stateEncryptionKey.name not to be empty") } }, 60*time.Second, 1*time.Second, "expected the FederationDomain to have status %q", expectStatus) - return federationDomain } func RandBytes(t *testing.T, numBytes int) []byte {