Skip to content

Commit e339557

Browse files
committed
Update non-Zen Clients
Signed-off-by: Rob Hundley <[email protected]>
1 parent 552d0fb commit e339557

File tree

3 files changed

+135
-1
lines changed

3 files changed

+135
-1
lines changed

internal/controller/operator/authentication_controller.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,10 @@ func (r *AuthenticationReconciler) Reconcile(ctx context.Context, req ctrl.Reque
364364
return subreconciler.Evaluate(subResult, err)
365365
}
366366

367+
if subResult, err := r.syncClientHostnames(ctx, req); subreconciler.ShouldHaltOrRequeue(subResult, err) {
368+
return subreconciler.Evaluate(subResult, err)
369+
}
370+
367371
return subreconciler.Evaluate(subreconciler.DoNotRequeue())
368372
}
369373

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
Copyright 2025.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package operator
18+
19+
import (
20+
"context"
21+
"errors"
22+
"fmt"
23+
"net/url"
24+
"slices"
25+
"strings"
26+
27+
oidcsecurityv1 "github.com/IBM/ibm-iam-operator/api/oidc.security/v1"
28+
operatorv1alpha1 "github.com/IBM/ibm-iam-operator/api/operator/v1alpha1"
29+
"github.com/IBM/ibm-iam-operator/internal/controller/common"
30+
"github.com/opdev/subreconciler"
31+
ctrl "sigs.k8s.io/controller-runtime"
32+
"sigs.k8s.io/controller-runtime/pkg/client"
33+
logf "sigs.k8s.io/controller-runtime/pkg/log"
34+
)
35+
36+
// syncClientHostnames iterates through all Clients in all namespaces visible to
37+
// the Operator and updates the hostnames present in their .spec.oidcLibertyClient fields.
38+
func (r *AuthenticationReconciler) syncClientHostnames(ctx context.Context, req ctrl.Request) (result *ctrl.Result, err error) {
39+
log := logf.FromContext(ctx)
40+
authCR := &operatorv1alpha1.Authentication{}
41+
if result, err = r.getLatestAuthentication(ctx, req, authCR); subreconciler.ShouldHaltOrRequeue(result, err) {
42+
log.Info("Failed to retrieve Authentication CR for status update")
43+
return
44+
}
45+
clientList := &oidcsecurityv1.ClientList{}
46+
if err = r.List(ctx, clientList); err != nil {
47+
log.Error(err, "Failed to list Clients in all watch namespaces")
48+
return subreconciler.RequeueWithError(err)
49+
}
50+
51+
var clusterAddress string
52+
if result, err = r.getClusterAddress(authCR, &clusterAddress)(ctx); subreconciler.ShouldHaltOrRequeue(result, err) {
53+
log.Error(err, "Could not get the cluster_address due to an unexpected error")
54+
return subreconciler.RequeueWithError(err)
55+
}
56+
57+
subRecs := common.Subreconcilers{}
58+
for _, clientCR := range clientList.Items {
59+
//
60+
if isOwnedByZenService(&clientCR) {
61+
continue
62+
}
63+
subRecs = append(subRecs, r.updateClientCRURIs(&clientCR, clusterAddress))
64+
}
65+
return subRecs.Reconcile(ctx)
66+
}
67+
68+
func isOwnedByZenService(obj client.Object) bool {
69+
for _, ownerRef := range obj.GetOwnerReferences() {
70+
if ownerRef.APIVersion == "zen.cpd.ibm.com/v1" && ownerRef.Kind == "ZenService" {
71+
return true
72+
}
73+
}
74+
return false
75+
}
76+
77+
func (r *AuthenticationReconciler) updateClientCRURIs(clientCR *oidcsecurityv1.Client, clusterAddress string) common.SecondaryReconcilerFn {
78+
return func(ctx context.Context) (result *ctrl.Result, err error) {
79+
log := logf.FromContext(ctx, "Client.Name", clientCR.Name, "Client.Namespace", clientCR.Namespace, "hostname", clusterAddress)
80+
modified := false
81+
for _, uris := range []*[]string{
82+
&clientCR.Spec.OidcLibertyClient.LogoutUris,
83+
&clientCR.Spec.OidcLibertyClient.RedirectUris,
84+
&clientCR.Spec.OidcLibertyClient.TrustedUris,
85+
} {
86+
newURIs, err := replaceURIListHostnames(*uris, clusterAddress)
87+
if err != nil {
88+
log.Error(err, "Failed to replace hostnames in all URIs for Client")
89+
return subreconciler.RequeueWithError(err)
90+
}
91+
if !slices.Equal(*uris, newURIs) {
92+
*uris = newURIs
93+
modified = true
94+
}
95+
}
96+
if !modified {
97+
log.Info("No URIs need to be updated on Client")
98+
return subreconciler.ContinueReconciling()
99+
} else {
100+
log.Info("URIs on Client were updated with new hostname")
101+
}
102+
103+
if err = r.Update(ctx, clientCR); err != nil {
104+
log.Error(err, "Failed to update Client with new URIs")
105+
return subreconciler.RequeueWithError(err)
106+
}
107+
log.Info("Successfully updated the hostname in URIs on Client")
108+
109+
return subreconciler.RequeueWithDelay(defaultLowerWait)
110+
}
111+
}
112+
113+
func replaceURIListHostnames(uris []string, newHostname string) (newURIs []string, err error) {
114+
newURIs = []string{}
115+
errs := []error{}
116+
for _, uriString := range uris {
117+
u, err := url.Parse(uriString)
118+
if err != nil {
119+
errs = append(errs, fmt.Errorf("failed to replace hostname in URI: %w", err))
120+
continue
121+
}
122+
oldHostname := u.Hostname()
123+
newURI := strings.Replace(uriString, oldHostname, newHostname, 1)
124+
newURIs = append(newURIs, newURI)
125+
}
126+
if len(errs) > 0 {
127+
err = fmt.Errorf("failed to make all hostname replacements in Client CR URIs: %w", errors.Join(errs...))
128+
return []string{}, err
129+
}
130+
return
131+
}

internal/controller/operator/configmap.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,6 @@ func (r *AuthenticationReconciler) getZenHost(ctx context.Context, authCR *opera
881881
err = r.Client.Get(ctx, types.NamespacedName{Name: ZenProductConfigmapName, Namespace: authCR.Namespace}, productConfigMap)
882882
if k8sErrors.IsNotFound(err) {
883883
reqLogger.Info("Zen product configmap does not exist")
884-
err = fmt.Errorf("expected product ConfigMap to be present but it was not found")
885884
return
886885
} else if err != nil {
887886
reqLogger.Error(err, "Failed to get Zen product configmap "+ZenProductConfigmapName)

0 commit comments

Comments
 (0)