Skip to content

Commit

Permalink
fix-apply-config-dns-aliases (#1093)
Browse files Browse the repository at this point in the history
* Added unit-tests

* Fixed dns-alias deletion

* Added unit-tests

* Set version
  • Loading branch information
satr authored Apr 18, 2024
1 parent 8fed749 commit 0582465
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 21 deletions.
4 changes: 2 additions & 2 deletions charts/radix-operator/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: v2
name: radix-operator
version: 1.31.3
appVersion: 1.51.2
version: 1.31.4
appVersion: 1.51.3
kubeVersion: ">=1.24.0"
description: Radix Operator
keywords:
Expand Down
5 changes: 3 additions & 2 deletions pkg/apis/environment/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,10 @@ func (env *Environment) handleDeletedRadixEnvironmentDependencies(re *v1.RadixEn
return err
}
env.logger.Debug().Msgf("delete %d RadixDNSAlias(es)", len(radixDNSAliasList.Items))
return env.kubeutil.DeleteRadixDNSAliases(slice.Reduce(radixDNSAliasList.Items, []*v1.RadixDNSAlias{}, func(acc []*v1.RadixDNSAlias, radixDNSAlias v1.RadixDNSAlias) []*v1.RadixDNSAlias {
dnsAliases := slice.Reduce(radixDNSAliasList.Items, []*v1.RadixDNSAlias{}, func(acc []*v1.RadixDNSAlias, radixDNSAlias v1.RadixDNSAlias) []*v1.RadixDNSAlias {
return append(acc, &radixDNSAlias)
})...)
})
return env.kubeutil.DeleteRadixDNSAliases(dnsAliases...)
}

// ApplyNamespace sets up namespace metadata and applies configuration to kubernetes
Expand Down
12 changes: 8 additions & 4 deletions pkg/apis/kube/radix_dns_alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package kube

import (
"context"
"errors"
"fmt"

"github.com/equinor/radix-common/utils/slice"
Expand Down Expand Up @@ -84,11 +85,14 @@ func (kubeutil *Kube) UpdateRadixDNSAlias(radixDNSAlias *radixv1.RadixDNSAlias)

// DeleteRadixDNSAliases Delete RadixDNSAliases
func (kubeutil *Kube) DeleteRadixDNSAliases(radixDNSAliases ...*radixv1.RadixDNSAlias) error {
var errs []error
for _, radixDNSAlias := range radixDNSAliases {
err := kubeutil.radixclient.RadixV1().RadixDNSAliases().Delete(context.Background(), radixDNSAlias.GetName(), metav1.DeleteOptions{})
if err != nil {
return err
if radixDNSAlias.ObjectMeta.DeletionTimestamp != nil {
continue
}
if err := kubeutil.radixclient.RadixV1().RadixDNSAliases().Delete(context.Background(), radixDNSAlias.GetName(), metav1.DeleteOptions{}); err != nil {
errs = append(errs, err)
}
}
return nil
return errors.Join(errs...)
}
48 changes: 41 additions & 7 deletions radix-operator/dnsalias/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"reflect"

radixutils "github.com/equinor/radix-common/utils"
"github.com/equinor/radix-common/utils/slice"
"github.com/equinor/radix-operator/pkg/apis/metrics"
radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1"
radixlabels "github.com/equinor/radix-operator/pkg/apis/utils/labels"
Expand Down Expand Up @@ -60,6 +61,7 @@ func NewController(kubeClient kubernetes.Interface,
addEventHandlersForRadixDeployments(radixInformerFactory, controller, radixClient, &logger)
addEventHandlersForIngresses(kubeInformerFactory, controller, &logger)
addEventHandlersForRadixRegistrations(radixInformerFactory, controller, radixClient, &logger)
addEventHandlersForRadixApplication(radixInformerFactory, controller, radixClient, &logger)
return controller
}

Expand All @@ -72,15 +74,46 @@ func addEventHandlersForRadixRegistrations(radixInformerFactory informers.Shared
if oldRR.GetResourceVersion() == newRR.GetResourceVersion() &&
radixutils.ArrayEqualElements(oldRR.Spec.AdGroups, newRR.Spec.AdGroups) &&
radixutils.ArrayEqualElements(oldRR.Spec.ReaderAdGroups, newRR.Spec.ReaderAdGroups) {
return // updating RadixDeployment has the same resource version. Do nothing.
return // updating RadixRegistration has the same resource version. Do nothing.
}
enqueueRadixDNSAliasesForAppName(controller, radixClient, newRR.GetName(), logger)
},
}); err != nil {
panic(err)
}
}

func addEventHandlersForRadixApplication(radixInformerFactory informers.SharedInformerFactory, controller *common.Controller, radixClient radixclient.Interface, logger *zerolog.Logger) {
informer := radixInformerFactory.Radix().V1().RadixApplications()
if _, err := informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
UpdateFunc: func(oldObj, newObj interface{}) {
oldRA := oldObj.(*radixv1.RadixApplication)
newRA := newObj.(*radixv1.RadixApplication)
if oldRA.GetResourceVersion() == newRA.GetResourceVersion() ||
equalDNSAliases(oldRA.Spec.DNSAlias, newRA.Spec.DNSAlias) {
return // updating RadixApplication has the same resource version and DNS aliases. Do nothing.
}
enqueueRadixDNSAliasesForRadixRegistration(controller, radixClient, newRR, logger)
enqueueRadixDNSAliasesForAppName(controller, radixClient, newRA.GetName(), logger)
},
}); err != nil {
panic(err)
}
}

func equalDNSAliases(dnsAliases1, dnsAliases2 []radixv1.DNSAlias) bool {
if len(dnsAliases1) != len(dnsAliases2) {
return false
}
dnsAlias1Map := slice.Reduce(dnsAliases1, make(map[string]radixv1.DNSAlias), func(acc map[string]radixv1.DNSAlias, dnsAlias radixv1.DNSAlias) map[string]radixv1.DNSAlias {
acc[dnsAlias.Alias] = dnsAlias
return acc
})
return slice.All(dnsAliases2, func(dnsAlias2 radixv1.DNSAlias) bool {
dnsAlias1, ok := dnsAlias1Map[dnsAlias2.Alias]
return ok && dnsAlias1.Environment == dnsAlias2.Environment && dnsAlias1.Component == dnsAlias2.Component
})
}

func addEventHandlersForIngresses(kubeInformerFactory kubeinformers.SharedInformerFactory, controller *common.Controller, logger *zerolog.Logger) {
ingressInformer := kubeInformerFactory.Networking().V1().Ingresses()
if _, err := ingressInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
Expand Down Expand Up @@ -192,11 +225,11 @@ func enqueueRadixDNSAliasesForRadixDeployment(controller *common.Controller, rad
}
}

func enqueueRadixDNSAliasesForRadixRegistration(controller *common.Controller, radixClient radixclient.Interface, rr *radixv1.RadixRegistration, logger *zerolog.Logger) {
logger.Debug().Msgf("Added or updated an RadixRegistration %s. Enqueue relevant RadixDNSAliases", rr.GetName())
radixDNSAliases, err := getRadixDNSAliasForApp(radixClient, rr.GetName())
func enqueueRadixDNSAliasesForAppName(controller *common.Controller, radixClient radixclient.Interface, appName string, logger *zerolog.Logger) {
logger.Debug().Msgf("Added or updated an RadixRegistration %s. Enqueue relevant RadixDNSAliases", appName)
radixDNSAliases, err := getRadixDNSAliasForApp(radixClient, appName)
if err != nil {
logger.Error().Err(err).Msgf("failed to get list of RadixDNSAliases for the application %s", rr.GetName())
logger.Error().Err(err).Msgf("failed to get list of RadixDNSAliases for the application %s", appName)
return
}
for _, radixDNSAlias := range radixDNSAliases {
Expand Down Expand Up @@ -233,7 +266,8 @@ func deepEqual(old, new *radixv1.RadixDNSAlias) bool {
return reflect.DeepEqual(new.Spec, old.Spec) &&
reflect.DeepEqual(new.ObjectMeta.Labels, old.ObjectMeta.Labels) &&
reflect.DeepEqual(new.ObjectMeta.Annotations, old.ObjectMeta.Annotations) &&
reflect.DeepEqual(new.ObjectMeta.Finalizers, old.ObjectMeta.Finalizers)
reflect.DeepEqual(new.ObjectMeta.Finalizers, old.ObjectMeta.Finalizers) &&
reflect.DeepEqual(new.ObjectMeta.DeletionTimestamp, old.ObjectMeta.DeletionTimestamp)
}

func getOwner(radixClient radixclient.Interface, _, name string) (interface{}, error) {
Expand Down
47 changes: 41 additions & 6 deletions radix-operator/dnsalias/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func (s *controllerTestSuite) Test_RadixDNSAliasEvents() {
const (
appName1 = "any-app1"
aliasName = "alias-alias-1"
aliasName2 = "alias-alias-2"
envName1 = "env1"
componentName1 = "server1"
componentName2 = "server2"
Expand All @@ -68,6 +69,12 @@ func (s *controllerTestSuite) Test_RadixDNSAliasEvents() {
s.Require().NoError(err)
s.WaitForSynced("Sync should be called on add RadixDNSAlias")

appNamespace := utils.GetAppNamespace(appName1)
ra, err := s.RadixClient.RadixV1().RadixApplications(appNamespace).Create(context.Background(), &radixv1.RadixApplication{
ObjectMeta: metav1.ObjectMeta{Name: appName1},
Spec: radixv1.RadixApplicationSpec{DNSAlias: []radixv1.DNSAlias{{Alias: aliasName, Environment: envName1, Component: componentName1}}}}, metav1.CreateOptions{})
s.Require().NoError(err)

// Updating the RadixDNSAlias with component should trigger a sync
s.Handler.EXPECT().Sync("", aliasName, s.EventRecorder).DoAndReturn(s.SyncedChannelCallback()).Times(1)
alias.Spec.Component = componentName2
Expand All @@ -85,28 +92,28 @@ func (s *controllerTestSuite) Test_RadixDNSAliasEvents() {
cfg := &dnsalias2.DNSConfig{DNSZone: dnsZone}
ing := buildRadixDNSAliasIngress(alias, int32(8080), cfg)
ing.SetOwnerReferences([]metav1.OwnerReference{{APIVersion: radixv1.SchemeGroupVersion.Identifier(), Kind: radixv1.KindRadixDNSAlias, Name: aliasName, Controller: pointers.Ptr(true)}})
namespace := utils.GetEnvironmentNamespace(alias.Spec.AppName, alias.Spec.Environment)
s.Handler.EXPECT().Sync(namespace, aliasName, s.EventRecorder).DoAndReturn(s.SyncedChannelCallback()).Times(0)
envNamespace := utils.GetEnvironmentNamespace(alias.Spec.AppName, alias.Spec.Environment)
s.Handler.EXPECT().Sync(envNamespace, aliasName, s.EventRecorder).DoAndReturn(s.SyncedChannelCallback()).Times(0)
ing, err = dnsaliasapi.CreateRadixDNSAliasIngress(s.KubeClient, alias.Spec.AppName, alias.Spec.Environment, ing)
s.Require().NoError(err)
s.WaitForNotSynced("Sync should not be called when adding ingress")

// Sync should not trigger on ingress update if resource version is unchanged
s.Handler.EXPECT().Sync(namespace, aliasName, s.EventRecorder).DoAndReturn(s.SyncedChannelCallback()).Times(0)
ing, err = s.KubeClient.NetworkingV1().Ingresses(namespace).Update(context.Background(), ing, metav1.UpdateOptions{})
s.Handler.EXPECT().Sync(envNamespace, aliasName, s.EventRecorder).DoAndReturn(s.SyncedChannelCallback()).Times(0)
ing, err = s.KubeClient.NetworkingV1().Ingresses(envNamespace).Update(context.Background(), ing, metav1.UpdateOptions{})
s.Require().NoError(err)
s.WaitForNotSynced("Sync should not be called on ingress update with no resource version change")

// Sync should trigger on ingress update if resource version is changed
ing.ResourceVersion = "2"
s.Handler.EXPECT().Sync("", aliasName, s.EventRecorder).DoAndReturn(s.SyncedChannelCallback()).Times(1)
_, err = s.KubeClient.NetworkingV1().Ingresses(namespace).Update(context.Background(), ing, metav1.UpdateOptions{})
_, err = s.KubeClient.NetworkingV1().Ingresses(envNamespace).Update(context.Background(), ing, metav1.UpdateOptions{})
s.Require().NoError(err)
s.WaitForSynced("Sync should be called on k8s ingress update with changed resource version")

// Sync should trigger when deleting ingress
s.Handler.EXPECT().Sync("", aliasName, s.EventRecorder).DoAndReturn(s.SyncedChannelCallback()).Times(1)
err = s.KubeClient.NetworkingV1().Ingresses(namespace).Delete(context.Background(), ing.GetName(), metav1.DeleteOptions{})
err = s.KubeClient.NetworkingV1().Ingresses(envNamespace).Delete(context.Background(), ing.GetName(), metav1.DeleteOptions{})
s.Require().NoError(err)
s.WaitForSynced("Sync should be called on ingress deletion")

Expand All @@ -124,6 +131,34 @@ func (s *controllerTestSuite) Test_RadixDNSAliasEvents() {
s.Require().NoError(err)
s.WaitForSynced("Sync should be called on updated reader ad group")

// Sync should trigger when changed DNSAlias in radix application
s.Handler.EXPECT().Sync("", aliasName, s.EventRecorder).DoAndReturn(s.SyncedChannelCallback()).Times(1)
ra.Spec.DNSAlias[0].Component = componentName2
ra.ObjectMeta.ResourceVersion = string(uuid.NewUUID())
_, err = s.RadixClient.RadixV1().RadixApplications(appNamespace).Update(context.Background(), ra, metav1.UpdateOptions{})
s.Require().NoError(err)
s.WaitForSynced("Sync should be called on updated alias")

// Sync should trigger when added DNSAlias in radix application
s.Handler.EXPECT().Sync("", aliasName, s.EventRecorder).DoAndReturn(s.SyncedChannelCallback()).Times(1)
ra.Spec.DNSAlias = append(ra.Spec.DNSAlias, radixv1.DNSAlias{
Alias: aliasName2,
Environment: envName1,
Component: componentName1,
})
ra.ObjectMeta.ResourceVersion = string(uuid.NewUUID())
_, err = s.RadixClient.RadixV1().RadixApplications(appNamespace).Update(context.Background(), ra, metav1.UpdateOptions{})
s.Require().NoError(err)
s.WaitForSynced("Sync should be called on added alias")

// Sync should trigger when deleted DNSAlias in radix application
s.Handler.EXPECT().Sync("", aliasName, s.EventRecorder).DoAndReturn(s.SyncedChannelCallback()).Times(1)
ra.Spec.DNSAlias = ra.Spec.DNSAlias[1:]
ra.ObjectMeta.ResourceVersion = string(uuid.NewUUID())
_, err = s.RadixClient.RadixV1().RadixApplications(appNamespace).Update(context.Background(), ra, metav1.UpdateOptions{})
s.Require().NoError(err)
s.WaitForSynced("Sync should be called on delete alias")

// Delete the RadixDNSAlias should not trigger a sync
s.Handler.EXPECT().Sync("", aliasName, s.EventRecorder).DoAndReturn(s.SyncedChannelCallback()).Times(0)
err = s.RadixClient.RadixV1().RadixDNSAliases().Delete(context.TODO(), alias.GetName(), metav1.DeleteOptions{})
Expand Down

0 comments on commit 0582465

Please sign in to comment.