Skip to content

Commit 945b4a4

Browse files
authored
feat(openbaocluster): add ingress integration readiness (#409)
Signed-off-by: Roel de Cort <roel.decort@adfinis.com>
1 parent 1451d1f commit 945b4a4

19 files changed

Lines changed: 806 additions & 23 deletions

api/v1alpha1/openbaocluster_types.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ const (
7373
// the referenced Gateway and GatewayClass integration contract for the chosen
7474
// Gateway API mode.
7575
ConditionGatewayIntegrationReady ConditionType = "GatewayIntegrationReady"
76+
// ConditionIngressIntegrationReady indicates whether the operator can verify
77+
// the managed Ingress integration contract for the chosen ingress mode.
78+
ConditionIngressIntegrationReady ConditionType = "IngressIntegrationReady"
7679
// ConditionAPIServerNetworkReady indicates whether the operator can validate
7780
// the Kubernetes API egress contract used by the operator-managed NetworkPolicy.
7881
// Unknown means the common service-VIP path is configured, but some CNIs may
@@ -352,6 +355,34 @@ type ReadReplicaConfig struct {
352355
Storage *ReadReplicaStorageConfig `json:"storage,omitempty"`
353356
}
354357

358+
// IngressPathType identifies how a Kubernetes Ingress path should match requests.
359+
// +kubebuilder:validation:Enum=Prefix;Exact;ImplementationSpecific
360+
type IngressPathType string
361+
362+
const (
363+
// IngressPathTypePrefix uses prefix path matching.
364+
IngressPathTypePrefix IngressPathType = "Prefix"
365+
// IngressPathTypeExact uses exact path matching.
366+
IngressPathTypeExact IngressPathType = "Exact"
367+
// IngressPathTypeImplementationSpecific defers path matching to the controller.
368+
IngressPathTypeImplementationSpecific IngressPathType = "ImplementationSpecific"
369+
)
370+
371+
// IngressReadinessMode identifies how the operator decides whether ingress
372+
// integration is ready for endpoint publication.
373+
// +kubebuilder:validation:Enum=Created;LoadBalancerPublished
374+
type IngressReadinessMode string
375+
376+
const (
377+
// IngressReadinessModeCreated considers ingress integration ready once the
378+
// managed Ingress object exists.
379+
IngressReadinessModeCreated IngressReadinessMode = "Created"
380+
// IngressReadinessModeLoadBalancerPublished considers ingress integration
381+
// ready only after the managed Ingress reports a published load balancer
382+
// address in status.
383+
IngressReadinessModeLoadBalancerPublished IngressReadinessMode = "LoadBalancerPublished"
384+
)
385+
355386
// IngressConfig controls optional HTTP(S) ingress in front of the OpenBao Service.
356387
type IngressConfig struct {
357388
// Enabled controls whether the Operator manages an Ingress for external access.
@@ -366,12 +397,21 @@ type IngressConfig struct {
366397
// Path is the HTTP path to route to OpenBao, defaulting to "/".
367398
// +optional
368399
Path string `json:"path,omitempty"`
400+
// PathType identifies how the ingress controller should interpret Path.
401+
// +kubebuilder:default=Prefix
402+
// +optional
403+
PathType IngressPathType `json:"pathType,omitempty"`
369404
// TLSSecretName is an optional TLS Secret name; when empty the cluster TLS Secret is used.
370405
// +optional
371406
TLSSecretName string `json:"tlsSecretName,omitempty"`
372407
// Annotations are additional annotations to apply to the Ingress.
373408
// +optional
374409
Annotations map[string]string `json:"annotations,omitempty"`
410+
// ReadinessMode identifies when the operator should consider ingress
411+
// integration ready for endpoint publication.
412+
// +kubebuilder:default=LoadBalancerPublished
413+
// +optional
414+
ReadinessMode IngressReadinessMode `json:"readinessMode,omitempty"`
375415
}
376416

377417
// MaintenanceConfig defines supported maintenance operations.

charts/openbao-operator/crds/openbao.org_openbaoclusters.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,24 @@ spec:
860860
description: Path is the HTTP path to route to OpenBao, defaulting
861861
to "/".
862862
type: string
863+
pathType:
864+
default: Prefix
865+
description: PathType identifies how the ingress controller should
866+
interpret Path.
867+
enum:
868+
- Prefix
869+
- Exact
870+
- ImplementationSpecific
871+
type: string
872+
readinessMode:
873+
default: LoadBalancerPublished
874+
description: |-
875+
ReadinessMode identifies when the operator should consider ingress
876+
integration ready for endpoint publication.
877+
enum:
878+
- Created
879+
- LoadBalancerPublished
880+
type: string
863881
tlsSecretName:
864882
description: TLSSecretName is an optional TLS Secret name; when
865883
empty the cluster TLS Secret is used.

config/crd/bases/openbao.org_openbaoclusters.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,24 @@ spec:
859859
description: Path is the HTTP path to route to OpenBao, defaulting
860860
to "/".
861861
type: string
862+
pathType:
863+
default: Prefix
864+
description: PathType identifies how the ingress controller should
865+
interpret Path.
866+
enum:
867+
- Prefix
868+
- Exact
869+
- ImplementationSpecific
870+
type: string
871+
readinessMode:
872+
default: LoadBalancerPublished
873+
description: |-
874+
ReadinessMode identifies when the operator should consider ingress
875+
integration ready for endpoint publication.
876+
enum:
877+
- Created
878+
- LoadBalancerPublished
879+
type: string
862880
tlsSecretName:
863881
description: TLSSecretName is an optional TLS Secret name; when
864882
empty the cluster TLS Secret is used.

docs/reference/api.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,8 +687,48 @@ _Appears in:_
687687
| `className` _string_ | ClassName is an optional IngressClassName (for example, "nginx", "traefik"). | | Optional: \{\} <br /> |
688688
| `host` _string_ | Host is the primary host for external access, for example "bao.example.com". | | MinLength: 1 <br /> |
689689
| `path` _string_ | Path is the HTTP path to route to OpenBao, defaulting to "/". | | Optional: \{\} <br /> |
690+
| `pathType` _[IngressPathType](#ingresspathtype)_ | PathType identifies how the ingress controller should interpret Path. | Prefix | Enum: [Prefix Exact ImplementationSpecific] <br />Optional: \{\} <br /> |
690691
| `tlsSecretName` _string_ | TLSSecretName is an optional TLS Secret name; when empty the cluster TLS Secret is used. | | Optional: \{\} <br /> |
691692
| `annotations` _object (keys:string, values:string)_ | Annotations are additional annotations to apply to the Ingress. | | Optional: \{\} <br /> |
693+
| `readinessMode` _[IngressReadinessMode](#ingressreadinessmode)_ | ReadinessMode identifies when the operator should consider ingress<br />integration ready for endpoint publication. | LoadBalancerPublished | Enum: [Created LoadBalancerPublished] <br />Optional: \{\} <br /> |
694+
695+
696+
#### IngressPathType
697+
698+
_Underlying type:_ _string_
699+
700+
IngressPathType identifies how a Kubernetes Ingress path should match requests.
701+
702+
_Validation:_
703+
- Enum: [Prefix Exact ImplementationSpecific]
704+
705+
_Appears in:_
706+
- [IngressConfig](#ingressconfig)
707+
708+
| Field | Description |
709+
| --- | --- |
710+
| `Prefix` | IngressPathTypePrefix uses prefix path matching.<br /> |
711+
| `Exact` | IngressPathTypeExact uses exact path matching.<br /> |
712+
| `ImplementationSpecific` | IngressPathTypeImplementationSpecific defers path matching to the controller.<br /> |
713+
714+
715+
#### IngressReadinessMode
716+
717+
_Underlying type:_ _string_
718+
719+
IngressReadinessMode identifies how the operator decides whether ingress
720+
integration is ready for endpoint publication.
721+
722+
_Validation:_
723+
- Enum: [Created LoadBalancerPublished]
724+
725+
_Appears in:_
726+
- [IngressConfig](#ingressconfig)
727+
728+
| Field | Description |
729+
| --- | --- |
730+
| `Created` | IngressReadinessModeCreated considers ingress integration ready once the<br />managed Ingress object exists.<br /> |
731+
| `LoadBalancerPublished` | IngressReadinessModeLoadBalancerPublished considers ingress integration<br />ready only after the managed Ingress reports a published load balancer<br />address in status.<br /> |
692732

693733

694734
#### InitContainerConfig
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package openbaocluster
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
"k8s.io/apimachinery/pkg/runtime"
10+
"sigs.k8s.io/controller-runtime/pkg/client"
11+
12+
openbaov1alpha1 "github.com/dc-tec/openbao-operator/api/v1alpha1"
13+
"github.com/dc-tec/openbao-operator/internal/platform/constants"
14+
networkingmanager "github.com/dc-tec/openbao-operator/internal/service/networking"
15+
)
16+
17+
// IngressIntegrationDependencies groups infrastructure readers required to
18+
// evaluate the operator-owned ingress integration contract.
19+
type IngressIntegrationDependencies struct {
20+
Client client.Client
21+
APIReader client.Reader
22+
Scheme *runtime.Scheme
23+
OperatorNamespace string
24+
Platform string
25+
}
26+
27+
// IngressIntegrationResult is the controller-facing evaluation result for the
28+
// operator-managed ingress contract.
29+
type IngressIntegrationResult struct {
30+
Status metav1.ConditionStatus
31+
Reason string
32+
Message string
33+
}
34+
35+
// EvaluateIngressIntegration validates the operator-managed ingress
36+
// prerequisites and controller support for the selected ingress mode.
37+
func EvaluateIngressIntegration(
38+
ctx context.Context,
39+
deps IngressIntegrationDependencies,
40+
cluster *openbaov1alpha1.OpenBaoCluster,
41+
) IngressIntegrationResult {
42+
manager := networkingmanager.NewManagerWithReader(
43+
deps.Client,
44+
deps.APIReader,
45+
deps.Scheme,
46+
deps.OperatorNamespace,
47+
deps.Platform,
48+
)
49+
err := manager.ValidateIngressIntegration(ctx, cluster)
50+
51+
switch {
52+
case err == nil:
53+
return IngressIntegrationResult{
54+
Status: metav1.ConditionTrue,
55+
Reason: constants.ReasonIngressIntegrationReady,
56+
Message: "Ingress integration prerequisites are satisfied",
57+
}
58+
case errors.Is(err, networkingmanager.ErrIngressClassMissing):
59+
return IngressIntegrationResult{
60+
Status: metav1.ConditionFalse,
61+
Reason: constants.ReasonIngressClassMissing,
62+
Message: err.Error(),
63+
}
64+
case errors.Is(err, networkingmanager.ErrIngressObjectMissing):
65+
return IngressIntegrationResult{
66+
Status: metav1.ConditionUnknown,
67+
Reason: constants.ReasonIngressObjectPending,
68+
Message: err.Error(),
69+
}
70+
case errors.Is(err, networkingmanager.ErrIngressLoadBalancerPending):
71+
return IngressIntegrationResult{
72+
Status: metav1.ConditionUnknown,
73+
Reason: constants.ReasonIngressLoadBalancerPending,
74+
Message: err.Error(),
75+
}
76+
case errors.Is(err, networkingmanager.ErrIngressCapabilitiesUnknown):
77+
return IngressIntegrationResult{
78+
Status: metav1.ConditionUnknown,
79+
Reason: constants.ReasonIngressCapabilitiesUnknown,
80+
Message: err.Error(),
81+
}
82+
default:
83+
return IngressIntegrationResult{
84+
Status: metav1.ConditionUnknown,
85+
Reason: constants.ReasonUnknown,
86+
Message: fmt.Sprintf("Failed to evaluate ingress integration prerequisites: %v", err),
87+
}
88+
}
89+
}

internal/controller/openbaocluster/constants.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const (
4848
ReasonACMEIntegrationReady = constants.ReasonACMEIntegrationReady
4949
ReasonACMECacheReady = "ACMECacheReady"
5050
ReasonGatewayIntegrationReady = constants.ReasonGatewayIntegrationReady
51+
ReasonIngressIntegrationReady = constants.ReasonIngressIntegrationReady
5152
ReasonAPIServerNetworkReady = constants.ReasonAPIServerNetworkReady
5253
ReasonAPIServerEndpointIPsRecommended = constants.ReasonAPIServerEndpointIPsRecommended
5354
ReasonGatewayReferenceMissing = constants.ReasonGatewayReferenceMissing
@@ -60,6 +61,10 @@ const (
6061
ReasonGatewayNotProgrammed = constants.ReasonGatewayNotProgrammed
6162
ReasonGatewayProgrammingPending = constants.ReasonGatewayProgrammingPending
6263
ReasonGatewayListenerIncompatible = constants.ReasonGatewayListenerIncompatible
64+
ReasonIngressClassMissing = constants.ReasonIngressClassMissing
65+
ReasonIngressCapabilitiesUnknown = constants.ReasonIngressCapabilitiesUnknown
66+
ReasonIngressObjectPending = constants.ReasonIngressObjectPending
67+
ReasonIngressLoadBalancerPending = constants.ReasonIngressLoadBalancerPending
6368
ReasonACMECacheNotConfigured = "ACMECacheNotConfigured"
6469
ReasonACMECacheMissing = "ACMECacheMissing"
6570
ReasonACMECachePending = "ACMECachePending"

internal/controller/openbaocluster/dependencies.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,16 @@ func (r *OpenBaoClusterReconciler) gatewayIntegrationDependencies() appopenbaocl
9292
}
9393
}
9494

95+
func (r *OpenBaoClusterReconciler) ingressIntegrationDependencies() appopenbaocluster.IngressIntegrationDependencies {
96+
return appopenbaocluster.IngressIntegrationDependencies{
97+
Client: r.Client,
98+
APIReader: r.APIReader,
99+
Scheme: r.ControllerRuntime.Scheme,
100+
OperatorNamespace: r.OperatorNamespace,
101+
Platform: r.Platform,
102+
}
103+
}
104+
95105
func (r *OpenBaoClusterReconciler) apiServerNetworkDependencies() appopenbaocluster.APIServerNetworkDependencies {
96106
return appopenbaocluster.APIServerNetworkDependencies{
97107
Client: r.Client,

internal/controller/openbaocluster/status.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func (r *OpenBaoClusterReconciler) updateStatus(ctx context.Context, logger logr
3434
r.setACMEIntegrationReadyCondition(ctx, cluster)
3535
r.setACMECacheReadyCondition(ctx, cluster)
3636
r.setGatewayIntegrationReadyCondition(ctx, cluster)
37+
r.setIngressIntegrationReadyCondition(ctx, cluster)
3738
r.setBackupConfigurationReadyCondition(ctx, cluster)
3839
r.setCloudUnsealIdentityReadyCondition(ctx, cluster)
3940

internal/controller/openbaocluster/status_condition_contract.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,17 @@ var gatewayIntegrationReadyConditionContract = newConditionContract(
6767
conditionContractEntry{reason: reasonUnknown, status: metav1.ConditionUnknown},
6868
)
6969

70+
var ingressIntegrationReadyConditionContract = newConditionContract(
71+
conditionContractEntry{reason: ReasonIngressIntegrationReady, status: metav1.ConditionTrue},
72+
conditionContractEntry{reason: ReasonIngressClassMissing, status: metav1.ConditionFalse},
73+
conditionContractEntry{reason: ReasonIngressCapabilitiesUnknown, status: metav1.ConditionUnknown},
74+
conditionContractEntry{reason: ReasonIngressObjectPending, status: metav1.ConditionUnknown},
75+
conditionContractEntry{reason: ReasonIngressLoadBalancerPending, status: metav1.ConditionUnknown},
76+
conditionContractEntry{reason: reasonPaused, status: metav1.ConditionUnknown},
77+
conditionContractEntry{reason: ReasonProfileNotSet, status: metav1.ConditionUnknown},
78+
conditionContractEntry{reason: reasonUnknown, status: metav1.ConditionUnknown},
79+
)
80+
7081
var apiServerNetworkReadyConditionContract = newConditionContract(
7182
conditionContractEntry{reason: ReasonAPIServerNetworkReady, status: metav1.ConditionTrue},
7283
conditionContractEntry{reason: ReasonAPIServerEndpointIPsRecommended, status: metav1.ConditionUnknown},
@@ -235,6 +246,19 @@ func setGatewayIntegrationReadyEvaluatedCondition(
235246
)
236247
}
237248

249+
func setIngressIntegrationReadyEvaluatedCondition(
250+
cluster *openbaov1alpha1.OpenBaoCluster,
251+
result appopenbaocluster.IngressIntegrationResult,
252+
) {
253+
applyConditionContract(
254+
&cluster.Status.Conditions,
255+
openbaov1alpha1.ConditionIngressIntegrationReady,
256+
cluster.Generation,
257+
statusConditionResult{Status: result.Status, Reason: result.Reason, Message: result.Message},
258+
ingressIntegrationReadyConditionContract,
259+
)
260+
}
261+
238262
func setAPIServerNetworkReadyEvaluatedCondition(
239263
cluster *openbaov1alpha1.OpenBaoCluster,
240264
result appopenbaocluster.APIServerNetworkResult,

0 commit comments

Comments
 (0)