Skip to content

Commit d3eead7

Browse files
committed
add unit tests for the mcp controller (part 1)
1 parent 04eb5ad commit d3eead7

File tree

9 files changed

+465
-14
lines changed

9 files changed

+465
-14
lines changed

internal/config/config_accessrequest.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import (
66
"k8s.io/apimachinery/pkg/util/validation/field"
77
)
88

9+
var _ Validatable = &AccessRequestConfig{}
10+
var _ Completable = &AccessRequestConfig{}
11+
912
type AccessRequestConfig struct {
1013
// If set, only AccessRequests that match the selector will be reconciled.
1114
Selector *Selector `json:"selector,omitempty"`

internal/config/config_managedcontrolplane.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
package config
22

33
import (
4+
"fmt"
5+
46
"k8s.io/apimachinery/pkg/util/validation/field"
57

68
commonapi "github.com/openmcp-project/openmcp-operator/api/common"
9+
corev2alpha1 "github.com/openmcp-project/openmcp-operator/api/core/v2alpha1"
710
)
811

12+
var _ Defaultable = &ManagedControlPlaneConfig{}
13+
var _ Validatable = &ManagedControlPlaneConfig{}
14+
915
type ManagedControlPlaneConfig struct {
1016
// MCPClusterPurpose is the purpose that is used for ClusterRequests created for ManagedControlPlane resources.
1117
MCPClusterPurpose string `json:"mcpClusterPurpose"`
@@ -22,6 +28,10 @@ type ManagedControlPlaneConfig struct {
2228
}
2329

2430
func (c *ManagedControlPlaneConfig) Default(_ *field.Path) error {
31+
c.StandardOIDCProvider.Default()
32+
if c.StandardOIDCProvider.Name == "" {
33+
c.StandardOIDCProvider.Name = corev2alpha1.DefaultOIDCProviderName
34+
}
2535
return nil
2636
}
2737

@@ -31,6 +41,18 @@ func (c *ManagedControlPlaneConfig) Validate(fldPath *field.Path) error {
3141
if c.MCPClusterPurpose == "" {
3242
errs = append(errs, field.Required(fldPath.Child("mcpClusterPurpose"), "MCP cluster purpose must be set"))
3343
}
44+
if c.ReconcileMCPEveryXDays < 0 {
45+
errs = append(errs, field.Invalid(fldPath.Child("reconcileMCPEveryXDays"), c.ReconcileMCPEveryXDays, "reconcile interval must be 0 or greater"))
46+
}
47+
if c.StandardOIDCProvider == nil {
48+
oidcFldPath := fldPath.Child("standardOIDCProvider")
49+
if len(c.StandardOIDCProvider.RoleBindings) > 0 {
50+
errs = append(errs, field.Forbidden(oidcFldPath.Child("roleBindings"), "role bindings are specified in the MCP spec and may not be set in the config"))
51+
}
52+
if c.StandardOIDCProvider.Name != "" && c.StandardOIDCProvider.Name != corev2alpha1.DefaultOIDCProviderName {
53+
errs = append(errs, field.Invalid(oidcFldPath.Child("name"), c.StandardOIDCProvider.Name, fmt.Sprintf("standard OIDC provider name must be '%s' or left empty (in which case it will be defaulted)", corev2alpha1.DefaultOIDCProviderName)))
54+
}
55+
}
3456

3557
return errs.ToAggregate()
3658
}

internal/config/config_scheduler.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import (
1111
clustersv1alpha1 "github.com/openmcp-project/openmcp-operator/api/clusters/v1alpha1"
1212
)
1313

14+
var _ Defaultable = &SchedulerConfig{}
15+
var _ Validatable = &SchedulerConfig{}
16+
var _ Completable = &SchedulerConfig{}
17+
1418
type SchedulerConfig struct {
1519
// Scope determines whether the scheduler considers all clusters or only the ones in the same namespace as the ClusterRequest.
1620
// Defaults to "Namespaced".

internal/controllers/managedcontrolplane/access.go

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"sigs.k8s.io/controller-runtime/pkg/client"
1212
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1313

14+
"github.com/openmcp-project/controller-utils/pkg/collections"
1415
ctrlutils "github.com/openmcp-project/controller-utils/pkg/controller"
1516
errutils "github.com/openmcp-project/controller-utils/pkg/errors"
1617
"github.com/openmcp-project/controller-utils/pkg/logging"
@@ -48,16 +49,21 @@ func (r *ManagedControlPlaneReconciler) manageAccessRequests(ctx context.Context
4849
}
4950

5051
// remove conditions for AccessRequests that are neither required nor in deletion (= have been deleted already)
51-
removeConditions := sets.New[string]()
52-
for _, con := range mcp.Status.Conditions {
53-
if !strings.HasPrefix(con.Type, corev2alpha1.ConditionPrefixOIDCAccessReady) {
54-
continue
55-
}
56-
providerName := strings.TrimPrefix(con.Type, corev2alpha1.ConditionPrefixOIDCAccessReady)
57-
if _, ok := updatedAccessRequests[providerName]; !ok && !accessRequestsInDeletion.Has(providerName) {
58-
removeConditions.Insert(con.Type)
52+
// first, build a set of OIDC provider names that have a condition in the MCP status
53+
removeConditions := collections.AggregateSlice(mcp.Status.Conditions, func(con metav1.Condition, cur sets.Set[string]) sets.Set[string] {
54+
if providerName, found := strings.CutPrefix(con.Type, corev2alpha1.ConditionPrefixOIDCAccessReady); found {
55+
cur.Insert(providerName)
5956
}
60-
}
57+
return cur
58+
}, sets.New[string]())
59+
// then, remove all conditions from it which belong to updated AccessRequests
60+
removeConditions = removeConditions.Difference(sets.KeySet(updatedAccessRequests))
61+
// and all conditions that are in deletion
62+
removeConditions = removeConditions.Difference(accessRequestsInDeletion)
63+
// now, add the condition prefix again
64+
removeConditions = collections.ProjectMapToMap(removeConditions, func(providerName string, _ sets.Empty) (string, sets.Empty) {
65+
return corev2alpha1.ConditionPrefixOIDCAccessReady + providerName, sets.Empty{}
66+
})
6167

6268
everythingReady := accessRequestsInDeletion.Len() == 0 && accessSecretsInDeletion.Len() == 0 && allAccessReady
6369
if everythingReady {
@@ -111,7 +117,7 @@ func (r *ManagedControlPlaneReconciler) createOrUpdateDesiredAccessRequests(ctx
111117
}
112118
ar.Labels[corev2alpha1.MCPLabel] = mcp.Name
113119
ar.Labels[apiconst.ManagedByLabel] = ControllerName
114-
ar.Labels[corev2alpha1.OIDCProviderLabel] = corev2alpha1.DefaultOIDCProviderName
120+
ar.Labels[corev2alpha1.OIDCProviderLabel] = oidc.Name
115121

116122
return nil
117123
}); err != nil {
@@ -120,7 +126,7 @@ func (r *ManagedControlPlaneReconciler) createOrUpdateDesiredAccessRequests(ctx
120126
createCon(corev2alpha1.ConditionAllAccessReady, metav1.ConditionFalse, cconst.ReasonWaitingForAccessRequest, "Error creating/updating AccessRequest for OIDC provider "+oidc.Name)
121127
return nil, rerr
122128
}
123-
updatedAccessRequests[corev2alpha1.DefaultOIDCProviderName] = ar
129+
updatedAccessRequests[oidc.Name] = ar
124130
}
125131

126132
return updatedAccessRequests, nil
@@ -154,7 +160,7 @@ func (r *ManagedControlPlaneReconciler) deleteUndesiredAccessRequests(ctx contex
154160
if ar.Spec.OIDCProvider != nil {
155161
providerName = ar.Spec.OIDCProvider.Name
156162
}
157-
accessRequestsInDeletion.Insert(ar.Name)
163+
accessRequestsInDeletion.Insert(providerName)
158164
if !ar.DeletionTimestamp.IsZero() {
159165
log.Debug("Waiting for deletion of AccessRequest that is no longer required", "accessRequestName", ar.Name, "accessRequestNamespace", ar.Namespace, "oidcProviderName", providerName)
160166
createCon(corev2alpha1.ConditionPrefixOIDCAccessReady+providerName, metav1.ConditionFalse, cconst.ReasonWaitingForAccessRequest, "AccessRequest is being deleted")
@@ -177,6 +183,7 @@ func (r *ManagedControlPlaneReconciler) deleteUndesiredAccessRequests(ctx contex
177183
}
178184

179185
// deleteUndesiredAccessSecrets deletes all access secrets belonging to the ManagedControlPlane that are not copied from an up-to-date AccessRequest.
186+
// It also deletes all mappings for which no secret exists from the ManagedControlPlane status.
180187
// It returns a set of OIDC provider names for which the AccessRequest secrets are still in deletion.
181188
func (r *ManagedControlPlaneReconciler) deleteUndesiredAccessSecrets(ctx context.Context, mcp *corev2alpha1.ManagedControlPlane, updatedAccessRequests map[string]*clustersv1alpha1.AccessRequest, createCon func(conType string, status metav1.ConditionStatus, reason, message string)) (sets.Set[string], errutils.ReasonableError) {
182189
log := logging.FromContextOrPanic(ctx)
@@ -223,6 +230,11 @@ func (r *ManagedControlPlaneReconciler) deleteUndesiredAccessSecrets(ctx context
223230
return accessSecretsInDeletion, rerr
224231
}
225232

233+
// delete all references to access secrets that are being deleted from the ManagedControlPlane status
234+
for providerName := range accessSecretsInDeletion {
235+
delete(mcp.Status.Access, providerName)
236+
}
237+
226238
return accessSecretsInDeletion, nil
227239
}
228240

@@ -244,7 +256,7 @@ func (r *ManagedControlPlaneReconciler) syncAccessSecrets(ctx context.Context, m
244256
// copy access request secret and reference it in the ManagedControlPlane status
245257
arSecret := &corev1.Secret{}
246258
arSecret.Name = ar.Status.SecretRef.Name
247-
arSecret.Namespace = ar.Status.SecretRef.Namespace
259+
arSecret.Namespace = ar.Namespace
248260
if err := r.PlatformCluster.Client().Get(ctx, client.ObjectKeyFromObject(arSecret), arSecret); err != nil {
249261
rerr := errutils.WithReason(fmt.Errorf("error getting AccessRequest secret '%s/%s': %w", arSecret.Namespace, arSecret.Name, err), cconst.ReasonPlatformClusterInteractionProblem)
250262
createCon(corev2alpha1.ConditionPrefixOIDCAccessReady+providerName, metav1.ConditionFalse, rerr.Reason(), rerr.Error())

internal/controllers/managedcontrolplane/controller.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ func (r *ManagedControlPlaneReconciler) handleCreateOrUpdate(ctx context.Context
204204

205205
log.Info("ClusterRequest not found, creating it", "clusterRequestName", cr.Name, "clusterRequestNamespace", cr.Namespace, "purpose", r.Config.MCPClusterPurpose)
206206
cr.Spec = clustersv1alpha1.ClusterRequestSpec{
207-
Purpose: r.Config.MCPClusterPurpose,
207+
Purpose: r.Config.MCPClusterPurpose,
208+
WaitForClusterDeletion: ptr.To(true),
208209
}
209210
if err := r.PlatformCluster.Client().Create(ctx, cr); err != nil {
210211
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error creating ClusterRequest '%s/%s': %w", cr.Namespace, cr.Name, err), cconst.ReasonPlatformClusterInteractionProblem)

0 commit comments

Comments
 (0)