Skip to content

Commit

Permalink
feat: Add SOA-ADIT-API field (#50)
Browse files Browse the repository at this point in the history
Signed-off-by: Anthony TREUILLIER <[email protected]>
  • Loading branch information
antrema authored Nov 7, 2024
1 parent b2c396d commit 24ec467
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 17 deletions.
5 changes: 5 additions & 0 deletions api/v1alpha1/zone_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ type ZoneSpec struct {
// The catalog this zone is a member of
// +optional
Catalog *string `json:"catalog,omitempty"`
// The SOA-EDIT-API metadata item, one of "DEFAULT", "INCREASE", "EPOCH", defaults to "DEFAULT"
// +kubebuilder:validation:Enum:=DEFAULT;INCREASE;EPOCH
// +kubebuilder:default:="DEFAULT"
// +optional
SOAEditAPI *string `json:"soa_edit_api,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.

9 changes: 9 additions & 0 deletions config/crd/bases/dns.cav.enablers.ob_zones.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ spec:
type: string
minItems: 1
type: array
soa_edit_api:
default: DEFAULT
description: The SOA-EDIT-API metadata item, one of "DEFAULT", "INCREASE",
"EPOCH", defaults to "DEFAULT"
enum:
- DEFAULT
- INCREASE
- EPOCH
type: string
required:
- kind
- nameservers
Expand Down
27 changes: 27 additions & 0 deletions config/samples/dns_v1alpha1_zone.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,30 @@ spec:
- ns1.helloworld.com
- ns2.helloworld.com
kind: Native

---
# Specific Catalog
apiVersion: dns.cav.enablers.ob/v1alpha1
kind: Zone
metadata:
name: example1.com
spec:
catalog: catalog.test
nameservers:
- ns1.example1.com
- ns2.example1.com
kind: Master

---
# Specific SOA_EDIT_API
apiVersion: dns.cav.enablers.ob/v1alpha1
kind: Zone
metadata:
name: example2.com
spec:
catalog: catalog.test
nameservers:
- ns1.example2.com
- ns2.example2.com
kind: Master
soa_edit_api: EPOCH
6 changes: 4 additions & 2 deletions internal/controller/pdns_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ type PdnsClienter struct {
Zones pdnsZonesClienter
}

// zoneIsIdenticalToExternalZone return True, True if respectively kind and Catalog are identical
// zoneIsIdenticalToExternalZone return True, True if respectively kind, soa_edit_api 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) {
zoneCatalog := makeCanonical(ptr.Deref(zone.Spec.Catalog, ""))
externalZoneCatalog := ptr.Deref(externalZone.Catalog, "")
return zone.Spec.Kind == string(*externalZone.Kind) && zoneCatalog == externalZoneCatalog, reflect.DeepEqual(zone.Spec.Nameservers, ns)
zoneSOAEditAPI := ptr.Deref(zone.Spec.SOAEditAPI, "")
externalZoneSOAEditAPI := ptr.Deref(externalZone.SOAEditAPI, "")
return zone.Spec.Kind == string(*externalZone.Kind) && zoneCatalog == externalZoneCatalog && zoneSOAEditAPI == externalZoneSOAEditAPI, 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
36 changes: 32 additions & 4 deletions internal/controller/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"fmt"
"path/filepath"
"reflect"
"regexp"
"runtime"
"slices"
"strings"
Expand Down Expand Up @@ -216,8 +217,16 @@ func (m mockZonesClient) Add(ctx context.Context, zone *powerdns.Zone) (*powerdn
}

// Serial initialization
now := time.Now().UTC()
serial := uint32(now.Year())*1000000 + uint32((now.Month()))*10000 + uint32(now.Day())*100 + 1
var serial uint32
switch *zone.SOAEditAPI {
case "EPOCH":
serial = uint32(time.Now().UTC().Unix())
case "INCREASE":
serial = uint32(1)
default:
now := time.Now().UTC()
serial = uint32(now.Year())*1000000 + uint32((now.Month()))*10000 + uint32(now.Day())*100 + 1
}
zone.Serial = &serial

// RRset type NS creation
Expand Down Expand Up @@ -259,8 +268,21 @@ 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 || *zone.Catalog != *localZone.Catalog {
serial = ptr.To(*localZone.Serial + uint32(1))
if *zone.Kind != *localZone.Kind || *zone.Catalog != *localZone.Catalog || *zone.SOAEditAPI != *localZone.SOAEditAPI {
switch *zone.SOAEditAPI {
case "EPOCH":
serial = ptr.To(uint32(time.Now().UTC().Unix()))
case "INCREASE":
serial = ptr.To(*localZone.Serial + uint32(1))
default:
match, _ := regexp.MatchString("[0-9]{10}", fmt.Sprintf("%d", *localZone.Serial))
if match {
serial = ptr.To(*localZone.Serial + uint32(1))
break
}
now := time.Now().UTC()
serial = ptr.To(uint32(now.Year())*1000000 + uint32((now.Month()))*10000 + uint32(now.Day())*100 + 1)
}
}
zone.Serial = serial

Expand Down Expand Up @@ -422,3 +444,9 @@ func getMockedCatalog(zoneName string) (result string) {
result = ptr.Deref(zone.Catalog, "")
return
}

func getMockedSOAEditAPI(zoneName string) (result string) {
zone, _ := readFromZonesMap(makeCanonical(zoneName))
result = ptr.Deref(zone.SOAEditAPI, "")
return
}
15 changes: 7 additions & 8 deletions internal/controller/zone_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (r *ZoneReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.
return ctrl.Result{}, err
}
} else {
// If Zone exists, compare Type and Nameservers and update it if necessary
// If Zone exists, compare content and update it if necessary
ns, err := r.PDNSClient.Records.Get(ctx, zone.ObjectMeta.Name, zone.ObjectMeta.Name, ptr.To(powerdns.RRTypeNS))
if err != nil {
return ctrl.Result{}, err
Expand Down Expand Up @@ -215,6 +215,7 @@ func (r *ZoneReconciler) updateExternalResources(ctx context.Context, zone *dnsv
Kind: &zoneKind,
Nameservers: zone.Spec.Nameservers,
Catalog: catalog,
SOAEditAPI: zone.Spec.SOAEditAPI,
})
if err != nil {
log.Error(err, "Failed to update zone")
Expand Down Expand Up @@ -253,13 +254,11 @@ func (r *ZoneReconciler) createExternalResources(ctx context.Context, zone *dnsv
}

z := powerdns.Zone{
ID: &zone.Name,
Name: &zone.Name,
Kind: powerdns.ZoneKindPtr(powerdns.ZoneKind(zone.Spec.Kind)),
DNSsec: ptr.To(false),
// SOAEdit: &soaEdit,
// SOAEditAPI: &soaEditApi,
// APIRectify: &apiRectify,
ID: &zone.Name,
Name: &zone.Name,
Kind: powerdns.ZoneKindPtr(powerdns.ZoneKind(zone.Spec.Kind)),
DNSsec: ptr.To(false),
SOAEditAPI: zone.Spec.SOAEditAPI,
Nameservers: zone.Spec.Nameservers,
Catalog: catalog,
}
Expand Down
46 changes: 43 additions & 3 deletions internal/controller/zone_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,45 @@ var _ = Describe("Zone Controller", func() {
})
})

Context("When existing resource", func() {
It("should successfully modify the catalog of the zone", Label("zone-modification", "soa-edit-api"), func() {
ctx := context.Background()
// Specific test variables
var modifiedResourceSOAEditAPI = "EPOCH"

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,
},
}
epochSerial := uint32(time.Now().UTC().Unix())
_, err := controllerutil.CreateOrUpdate(ctx, k8sClient, resource, func() error {
resource.Spec.SOAEditAPI = &modifiedResourceSOAEditAPI
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
}, timeout, interval).Should(BeTrue())
Expect(getMockedSOAEditAPI(resourceName)).To(Equal(modifiedResourceSOAEditAPI), "SOA-Edit-API should have changed")
Expect(*(modifiedZone.Status.Serial)).To(Equal(epochSerial), "Serial should have changed")
})
})

Context("When existing resource", func() {
It("should successfully recreate an existing zone", Label("zone-recreation"), func() {
ctx := context.Background()
Expand All @@ -259,9 +298,10 @@ var _ = Describe("Zone Controller", func() {
now := time.Now().UTC()
initialSerial := uint32(now.Year())*1000000 + uint32((now.Month()))*10000 + uint32(now.Day())*100 + 1
writeToZonesMap(makeCanonical(recreationResourceName), &powerdns.Zone{
Name: &recreationResourceName,
Kind: powerdns.ZoneKindPtr(powerdns.ZoneKind(recreationResourceKind)),
Serial: &initialSerial,
Name: &recreationResourceName,
Kind: powerdns.ZoneKindPtr(powerdns.ZoneKind(recreationResourceKind)),
Serial: &initialSerial,
SOAEditAPI: ptr.To("DEFAULT"),
})

By("Recreating a Zone")
Expand Down

0 comments on commit 24ec467

Please sign in to comment.