Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add catalog specification for Zone #14

Merged
merged 1 commit into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.Catalog != 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
}
mydoomfr marked this conversation as resolved.
Show resolved Hide resolved
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
57 changes: 55 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,55 @@ 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
// Sending a 'nil' catalog to PowerDNS is considered as a no modification, so
// to clear a catalog specification for a zone, you need to specify an empty catalog
// So we test all use-cases:
// a. from an empty catalog to a specific catalog
// b. from a specific catalog to an empty catalog
var modifiedResourceCatalog = []string{"", "catalog.other-domain.org.", ""}
mydoomfr marked this conversation as resolved.
Show resolved Hide resolved

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 catalog and ensure the serial is incremented
for i, c := range modifiedResourceCatalog {
catalog := c
mydoomfr marked this conversation as resolved.
Show resolved Hide resolved
_, 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