Skip to content

Commit 4591280

Browse files
authored
fix (bug): Host name error for ApplicationSet Webhook Server route (argoproj-labs#1730)
* fix Host name error for ApplicationSet Webhook Server route Signed-off-by: Atif Ali <atali@redhat.com> * ensure route name and its label value is always ≤ 63 characters Signed-off-by: Atif Ali <atali@redhat.com> * added tests Signed-off-by: Atif Ali <atali@redhat.com> * resolve linter error Signed-off-by: Atif Ali <atali@redhat.com> * fix test failure Signed-off-by: Atif Ali <atali@redhat.com> * define a constant for appset webhook in commons Signed-off-by: Atif Ali <atali@redhat.com> * fix linter Signed-off-by: Atif Ali <atali@redhat.com> --------- Signed-off-by: Atif Ali <atali@redhat.com>
1 parent 7a39dab commit 4591280

File tree

6 files changed

+154
-15
lines changed

6 files changed

+154
-15
lines changed

common/values.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ const (
8686
//ApplicationSetServiceNameSuffix is the suffix for Apllication Set Controller Service
8787
ApplicationSetServiceNameSuffix = "applicationset-controller"
8888

89+
// ApplicationSetControllerWebhookSuffix is the suffix for ApplicationSet Webhook
90+
ApplicationSetControllerWebhookSuffix = "appset-webhook"
91+
8992
// ArgoCDAggregateToControllerLabelKey is label to configure base aggregated ClusterRole for Argo CD Application Controller.
9093
ArgoCDAggregateToControllerLabelKey = "argocd/aggregate-to-controller"
9194

controllers/argocd/route.go

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ import (
2525
"k8s.io/apimachinery/pkg/util/intstr"
2626
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
2727

28+
configv1 "github.com/openshift/api/config/v1"
29+
"sigs.k8s.io/controller-runtime/pkg/client"
30+
2831
argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1"
2932
"github.com/argoproj-labs/argocd-operator/common"
3033
"github.com/argoproj-labs/argocd-operator/controllers/argoutil"
@@ -325,8 +328,14 @@ func isCreatedByServiceCA(crName string, secret corev1.Secret) bool {
325328

326329
// reconcileApplicationSetControllerWebhookRoute will ensure that the ArgoCD Server Route is present.
327330
func (r *ReconcileArgoCD) reconcileApplicationSetControllerWebhookRoute(cr *argoproj.ArgoCD) error {
328-
name := fmt.Sprintf("%s-%s", common.ApplicationSetServiceNameSuffix, "webhook")
329-
route := newRouteWithSuffix(name, cr)
331+
// Generate a base name for the route
332+
baseName := fmt.Sprintf("%s-%s", cr.Name, common.ApplicationSetControllerWebhookSuffix)
333+
// Truncate to 63 characters if needed (Kubernetes label value limit)
334+
if len(baseName) > maxLabelLength {
335+
baseName = baseName[:maxLabelLength]
336+
}
337+
route := newRouteWithName(baseName, cr)
338+
330339
found := argoutil.IsObjectFound(r.Client, cr.Namespace, route.Name, route)
331340
if found {
332341
if cr.Spec.ApplicationSet == nil || !cr.Spec.ApplicationSet.WebhookServer.Route.Enabled {
@@ -361,15 +370,36 @@ func (r *ReconcileArgoCD) reconcileApplicationSetControllerWebhookRoute(cr *argo
361370
}
362371

363372
// Allow override of the Host for the Route.
373+
var host string
364374
if len(cr.Spec.ApplicationSet.WebhookServer.Host) > 0 {
365-
route.Spec.Host = cr.Spec.ApplicationSet.WebhookServer.Host
375+
host = cr.Spec.ApplicationSet.WebhookServer.Host
376+
} else {
377+
// Generate the default host
378+
baseHost := fmt.Sprintf("%s-%s-%s", cr.Name, common.ApplicationSetControllerWebhookSuffix, cr.Namespace)
379+
ingressConfig := &configv1.Ingress{}
380+
err := r.Get(context.TODO(), client.ObjectKey{Name: "cluster"}, ingressConfig)
381+
if err != nil {
382+
return err
383+
}
384+
appsDomain := ingressConfig.Spec.Domain
385+
host = fmt.Sprintf("%s.%s", baseHost, appsDomain)
386+
}
387+
388+
// Truncate the first label if needed
389+
labels := strings.SplitN(host, ".", 2)
390+
if len(labels[0]) > maxLabelLength {
391+
labels[0] = labels[0][:maxLabelLength]
392+
}
393+
if len(labels) > 1 {
394+
route.Spec.Host = fmt.Sprintf("%s.%s", labels[0], labels[1])
395+
} else {
396+
route.Spec.Host = labels[0]
366397
}
367398

368399
hostname, err := shortenHostname(route.Spec.Host)
369400
if err != nil {
370401
return err
371402
}
372-
373403
route.Spec.Host = hostname
374404

375405
route.Spec.Port = &routev1.RoutePort{

controllers/argocd/route_test.go

Lines changed: 95 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ func TestReconcileRouteApplicationSetHost(t *testing.T) {
253253
assert.NoError(t, err)
254254

255255
loaded := &routev1.Route{}
256-
err = r.Client.Get(ctx, types.NamespacedName{Name: fmt.Sprintf("%s-%s-%s", testArgoCDName, common.ApplicationSetServiceNameSuffix, "webhook"), Namespace: testNamespace}, loaded)
256+
err = r.Client.Get(ctx, types.NamespacedName{Name: fmt.Sprintf("%s-%s", testArgoCDName, common.ApplicationSetControllerWebhookSuffix), Namespace: testNamespace}, loaded)
257257
fatalIfError(t, err, "failed to load route %q: %s", testArgoCDName+"-server", err)
258258

259259
wantTLSConfig := &routev1.TLSConfig{
@@ -309,7 +309,7 @@ func TestReconcileRouteApplicationSetTlsTermination(t *testing.T) {
309309
assert.NoError(t, err)
310310

311311
loaded := &routev1.Route{}
312-
err = r.Client.Get(ctx, types.NamespacedName{Name: fmt.Sprintf("%s-%s-%s", testArgoCDName, common.ApplicationSetServiceNameSuffix, "webhook"), Namespace: testNamespace}, loaded)
312+
err = r.Client.Get(ctx, types.NamespacedName{Name: fmt.Sprintf("%s-%s", testArgoCDName, common.ApplicationSetControllerWebhookSuffix), Namespace: testNamespace}, loaded)
313313
fatalIfError(t, err, "failed to load route %q: %s", testArgoCDName+"-server", err)
314314

315315
wantTLSConfig := &routev1.TLSConfig{
@@ -351,7 +351,17 @@ func TestReconcileRouteApplicationSetTls(t *testing.T) {
351351
}
352352
})
353353

354-
resObjs := []client.Object{argoCD}
354+
// Create the Ingress configuration
355+
ingressConfig := &configv1.Ingress{
356+
ObjectMeta: metav1.ObjectMeta{
357+
Name: "cluster",
358+
},
359+
Spec: configv1.IngressSpec{
360+
Domain: "apps.example.com",
361+
},
362+
}
363+
364+
resObjs := []client.Object{argoCD, ingressConfig}
355365
subresObjs := []client.Object{argoCD}
356366
runtimeObjs := []runtime.Object{}
357367
sch := makeTestReconcilerScheme(argoproj.AddToScheme, configv1.Install, routev1.Install)
@@ -370,10 +380,17 @@ func TestReconcileRouteApplicationSetTls(t *testing.T) {
370380
_, err := r.Reconcile(context.TODO(), req)
371381
assert.NoError(t, err)
372382

383+
// The route name should be based on the ArgoCD instance name
384+
expectedRouteName := fmt.Sprintf("%s-%s", testArgoCDName, common.ApplicationSetControllerWebhookSuffix)
385+
if len(expectedRouteName) > 63 {
386+
expectedRouteName = expectedRouteName[:63]
387+
}
388+
373389
loaded := &routev1.Route{}
374-
err = r.Client.Get(ctx, types.NamespacedName{Name: fmt.Sprintf("%s-%s-%s", testArgoCDName, common.ApplicationSetServiceNameSuffix, "webhook"), Namespace: testNamespace}, loaded)
375-
fatalIfError(t, err, "failed to load route %q: %s", testArgoCDName+"-server", err)
390+
err = r.Client.Get(ctx, types.NamespacedName{Name: expectedRouteName, Namespace: testNamespace}, loaded)
391+
fatalIfError(t, err, "failed to load route %q: %s", expectedRouteName, err)
376392

393+
// Verify TLS configuration
377394
wantTLSConfig := &routev1.TLSConfig{
378395
Termination: routev1.TLSTerminationEdge,
379396
Certificate: "test-certificate",
@@ -383,28 +400,36 @@ func TestReconcileRouteApplicationSetTls(t *testing.T) {
383400
InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyRedirect,
384401
}
385402
if diff := cmp.Diff(wantTLSConfig, loaded.Spec.TLS); diff != "" {
386-
t.Fatalf("failed to reconcile route:\n%s", diff)
403+
t.Fatalf("failed to reconcile route TLS config:\n%s", diff)
387404
}
388405

389-
assert.Empty(t, loaded.Spec.Host)
406+
// Verify hostname
407+
expectedHost := fmt.Sprintf("%s-%s-%s.apps.example.com", testArgoCDName, common.ApplicationSetControllerWebhookSuffix, testNamespace)
408+
if diff := cmp.Diff(expectedHost, loaded.Spec.Host); diff != "" {
409+
t.Fatalf("failed to reconcile route hostname:\n%s", diff)
410+
}
390411

412+
// Verify port configuration
391413
wantPort := &routev1.RoutePort{
392414
TargetPort: intstr.FromString("webhook"),
393415
}
394416
if diff := cmp.Diff(wantPort, loaded.Spec.Port); diff != "" {
395-
t.Fatalf("failed to reconcile route:\n%s", diff)
417+
t.Fatalf("failed to reconcile route port:\n%s", diff)
396418
}
397419

420+
// Verify annotations
398421
if diff := cmp.Diff("my-annotation-value", loaded.Annotations["my-annotation-key"]); diff != "" {
399-
t.Fatalf("failed to reconcile route:\n%s", diff)
422+
t.Fatalf("failed to reconcile route annotations:\n%s", diff)
400423
}
401424

425+
// Verify labels
402426
if diff := cmp.Diff("my-label-value", loaded.Labels["my-label-key"]); diff != "" {
403-
t.Fatalf("failed to reconcile route:\n%s", diff)
427+
t.Fatalf("failed to reconcile route labels:\n%s", diff)
404428
}
405429

430+
// Verify wildcard policy
406431
if diff := cmp.Diff(wildcardPolicy, loaded.Spec.WildcardPolicy); diff != "" {
407-
t.Fatalf("failed to reconcile route:\n%s", diff)
432+
t.Fatalf("failed to reconcile route wildcard policy:\n%s", diff)
408433
}
409434
}
410435

@@ -492,6 +517,65 @@ func TestReconcileRouteForShorteningHostname(t *testing.T) {
492517
}
493518
}
494519

520+
func TestReconcileRouteForShorteningRoutename(t *testing.T) {
521+
routeAPIFound = true
522+
ctx := context.Background()
523+
logf.SetLogger(ZapLogger(true))
524+
525+
// Use a long ArgoCD instance name to force truncation
526+
longName := "this-is-a-very-long-argocd-instance-name-that-will-break-the-route-name-limit"
527+
argoCD := makeArgoCD(func(a *argoproj.ArgoCD) {
528+
a.Name = longName
529+
a.Spec.ApplicationSet = &argoproj.ArgoCDApplicationSet{
530+
WebhookServer: argoproj.WebhookServerSpec{
531+
Route: argoproj.ArgoCDRouteSpec{
532+
Enabled: true,
533+
},
534+
},
535+
}
536+
})
537+
538+
// Add a fake Ingress resource to satisfy the domain lookup
539+
ingressConfig := &configv1.Ingress{
540+
ObjectMeta: metav1.ObjectMeta{
541+
Name: "cluster",
542+
},
543+
Spec: configv1.IngressSpec{
544+
Domain: "apps.example.com",
545+
},
546+
}
547+
548+
resObjs := []client.Object{argoCD, ingressConfig}
549+
subresObjs := []client.Object{argoCD}
550+
runtimeObjs := []runtime.Object{}
551+
sch := makeTestReconcilerScheme(argoproj.AddToScheme, configv1.Install, routev1.Install)
552+
cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
553+
r := makeTestReconciler(cl, sch)
554+
555+
assert.NoError(t, createNamespace(r, argoCD.Namespace, ""))
556+
557+
req := reconcile.Request{
558+
NamespacedName: types.NamespacedName{
559+
Name: longName,
560+
Namespace: testNamespace,
561+
},
562+
}
563+
564+
_, err := r.Reconcile(context.TODO(), req)
565+
assert.NoError(t, err)
566+
567+
// The route name should be truncated to 63 chars
568+
expectedRouteName := longName + "-" + common.ApplicationSetControllerWebhookSuffix
569+
if len(expectedRouteName) > 63 {
570+
expectedRouteName = expectedRouteName[:63]
571+
}
572+
573+
loaded := &routev1.Route{}
574+
err = r.Client.Get(ctx, types.NamespacedName{Name: expectedRouteName, Namespace: testNamespace}, loaded)
575+
assert.NoError(t, err)
576+
assert.LessOrEqual(t, len(loaded.Name), 63)
577+
}
578+
495579
func TestReconcileRouteTLSConfig(t *testing.T) {
496580
routeAPIFound = true
497581
ctx := context.Background()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: route.openshift.io/v1
2+
kind: Route
3+
metadata:
4+
name: "argocd-long-name-for-route-testiiiiiiiiiiiiiiiiiiiiiiiing-appset"
5+
spec: {}
6+
status:
7+
ingress:
8+
- host: argocd-long-name-for-route-testiiiiiiiiiiiiiiiiiiiiiiiing-appset.apps.testing.lab.upshift.rdu2.redhat.com
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: argoproj.io/v1beta1
2+
kind: ArgoCD
3+
metadata:
4+
name: argocd-long-name-for-route-testiiiiiiiiiiiiiiiiiiiiiiiing
5+
spec:
6+
applicationSet:
7+
webhookServer:
8+
route:
9+
enabled: true
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
apiVersion: argoproj.io/v1beta1
2+
kind: ArgoCD
3+
metadata:
4+
name: argocd-long-name-for-route-test-applicationset-controller-webho
5+
$delete: true

0 commit comments

Comments
 (0)