Skip to content

Commit

Permalink
feat: Add catalog specification for Zone
Browse files Browse the repository at this point in the history
Signed-off-by: Anthony TREUILLIER <[email protected]>
  • Loading branch information
antrema committed Sep 11, 2024
1 parent 2e4ace0 commit 10e0f55
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 6 deletions.
3 changes: 3 additions & 0 deletions api/v1alpha1/zone_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ type ZoneSpec struct {
// +kubebuilder:validation:MinItems=1
// +kubebuilder:validation:items:Pattern=`^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+$`
Nameservers []string `json:"nameservers"`
// The catalog this zone is a member of
// +optional
Catalog *string `json:"catalog,omitempty"`
}

// ZoneStatus defines the observed state of Zone
Expand Down
5 changes: 5 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions config/crd/bases/dns.cav.enablers.ob_zones.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ spec:
spec:
description: ZoneSpec defines the desired state of Zone
properties:
catalog:
description: The catalog this zone is a member of
type: string
kind:
description: Kind of the zone, one of "Native", "Master", "Slave",
"Producer", "Consumer".
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/onsi/gomega v1.34.1
k8s.io/apimachinery v0.31.0
k8s.io/client-go v0.31.0
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
sigs.k8s.io/controller-runtime v0.19.0
)

Expand Down Expand Up @@ -67,7 +68,6 @@ require (
k8s.io/apiextensions-apiserver v0.31.0 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20240816214639-573285566f34 // indirect
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
Expand Down
12 changes: 10 additions & 2 deletions internal/controller/pdns_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,17 @@ type PdnsClienter struct {
Zones pdnsZonesClienter
}

// zoneIsIdenticalToExternalZone return True, True if respectively kind and nameservers are identical between Zone and External Resource
// zoneIsIdenticalToExternalZone return True, True if respectively kind and Catalog are identical
// and nameservers are identical between Zone and External Resource
func zoneIsIdenticalToExternalZone(zone *dnsv1alpha1.Zone, externalZone *powerdns.Zone, ns []string) (bool, bool) {
return zone.Spec.Kind == string(*externalZone.Kind), reflect.DeepEqual(zone.Spec.Nameservers, ns)
var zoneCatalog, externalZoneCatalog string
if zone.Spec.Catalog != nil {
zoneCatalog = *zone.Spec.Catalog
}
if externalZone.Catalog != nil {
externalZoneCatalog = *externalZone.Catalog
}
return zone.Spec.Kind == string(*externalZone.Kind) && zoneCatalog == externalZoneCatalog, reflect.DeepEqual(zone.Spec.Nameservers, ns)
}

// rrsetIsIdenticalToExternalRRset return True if Comments, Name, Type, TTL and Records are identical between RRSet and External Resource
Expand Down
11 changes: 10 additions & 1 deletion internal/controller/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ func (m mockZonesClient) Change(ctx context.Context, domain string, zone *powerd
return powerdns.Error{StatusCode: ZONE_NOT_FOUND_CODE, Status: fmt.Sprintf("%d %s", ZONE_NOT_FOUND_CODE, ZONE_NOT_FOUND_MSG), Message: ZONE_NOT_FOUND_MSG}
}
serial := localZone.Serial
if *zone.Kind != *localZone.Kind {
if *zone.Kind != *localZone.Kind || *zone.Catalog != *localZone.Catalog {
serial = Uint32(*localZone.Serial + uint32(1))
}
zone.Serial = serial
Expand Down Expand Up @@ -389,3 +389,12 @@ func getMockedComment(rrsetName, rrsetType string) (result string) {
}
return
}

//nolint:unparam
func getMockedCatalog(zoneName string) (result string) {
zone, _ := readFromZonesMap(makeCanonical(zoneName))
if zone.Kind != nil {
result = *zone.Catalog
}
return
}
6 changes: 6 additions & 0 deletions internal/controller/zone_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,15 @@ func (r *ZoneReconciler) deleteExternalResources(ctx context.Context, zone *dnsv
func (r *ZoneReconciler) updateExternalResources(ctx context.Context, zone *dnsv1alpha1.Zone) error {
log := log.FromContext(ctx)
zoneKind := powerdns.ZoneKind(zone.Spec.Kind)
catalog := ""
if zone.Spec.Catalog != nil {
catalog = *zone.Spec.Catalog
}
err := r.PDNSClient.Zones.Change(ctx, zone.ObjectMeta.Name, &powerdns.Zone{
Name: &zone.ObjectMeta.Name,
Kind: &zoneKind,
Nameservers: zone.Spec.Nameservers,
Catalog: &catalog,
})
if err != nil {
log.Error(err, "Failed to update zone")
Expand Down Expand Up @@ -246,6 +251,7 @@ func (r *ZoneReconciler) createExternalResources(ctx context.Context, zone *dnsv
// SOAEditAPI: &soaEditApi,
// APIRectify: &apiRectify,
Nameservers: zone.Spec.Nameservers,
Catalog: zone.Spec.Catalog,
}

_, err := r.PDNSClient.Zones.Add(ctx, &z)
Expand Down
52 changes: 50 additions & 2 deletions internal/controller/zone_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

dnsv1alpha1 "github.com/orange-opensource/powerdns-operator/api/v1alpha1"
Expand All @@ -29,8 +30,9 @@ import (
var _ = Describe("Zone Controller", func() {

const (
resourceName = "example1.org"
resourceKind = "Native"
resourceName = "example1.org"
resourceKind = "Native"
resourceCatalog = "catalog.example1.org."

timeout = time.Second * 5
interval = time.Millisecond * 250
Expand All @@ -54,6 +56,7 @@ var _ = Describe("Zone Controller", func() {
resource.Spec = dnsv1alpha1.ZoneSpec{
Kind: resourceKind,
Nameservers: resourceNameservers,
Catalog: ptr.To(resourceCatalog),
}
return nil
})
Expand Down Expand Up @@ -106,6 +109,7 @@ var _ = Describe("Zone Controller", func() {
}, timeout, interval).Should(BeTrue())
Expect(getMockedKind(resourceName)).To(Equal(resourceKind), "Kind should be equal")
Expect(getMockedNameservers(resourceName)).To(Equal(resourceNameservers), "Nameservers should be equal")
Expect(getMockedCatalog(resourceName)).To(Equal(resourceCatalog), "Catalog should be equal")
Expect(zone.GetFinalizers()).To(ContainElement(FINALIZER_NAME), "Zone should contain the finalizer")
Expect(fmt.Sprintf("%d", *(zone.Status.Serial))).To(Equal(fmt.Sprintf("%s01", time.Now().Format("20060102"))), "Serial should be YYYYMMDD01")
})
Expand Down Expand Up @@ -193,6 +197,50 @@ var _ = Describe("Zone Controller", func() {
})
})

Context("When existing resource", func() {
It("should successfully modify the catalog of the zone", Label("zone-modification", "catalog"), func() {
ctx := context.Background()
// Specific test variables
var modifiedResourceCatalog = []string{"", "catalog.other-domain.org.", ""}

By("Getting the initial Serial of the resource")
zone := &dnsv1alpha1.Zone{}
Eventually(func() bool {
err := k8sClient.Get(ctx, typeNamespacedName, zone)
return err == nil && zone.Status.Serial != nil
}, timeout, interval).Should(BeTrue())
initialSerial := zone.Status.Serial

By("Modifying the resource")
resource := &dnsv1alpha1.Zone{
ObjectMeta: metav1.ObjectMeta{
Name: resourceName,
},
}
// Update the resource for each kind and ensure the serial is incremented
for i, c := range modifiedResourceCatalog {
catalog := c
_, err := controllerutil.CreateOrUpdate(ctx, k8sClient, resource, func() error {
resource.Spec.Catalog = &catalog
return nil
})
Expect(err).NotTo(HaveOccurred())

By("Getting the modified resource")
modifiedZone := &dnsv1alpha1.Zone{}
// Waiting for the resource to be fully modified
Eventually(func() bool {
err := k8sClient.Get(ctx, typeNamespacedName, modifiedZone)
return err == nil && *modifiedZone.Status.Serial > *initialSerial+uint32(i)
}, timeout, interval).Should(BeTrue())

expectedSerial := *initialSerial + uint32(i+1)
Expect(getMockedCatalog(resourceName)).To(Equal(catalog), "Catalog should be equal")
Expect(*(modifiedZone.Status.Serial)).To(Equal(expectedSerial), "Serial should be incremented")
}
})
})

Context("When existing resource", func() {
It("should successfully recreate an existing zone", Label("zone-recreation"), func() {
ctx := context.Background()
Expand Down

0 comments on commit 10e0f55

Please sign in to comment.