mirror of
https://github.com/hanzoai/coredns-manager-operator.git
synced 2026-10-30 18:43:53 +00:00
147 lines
4.6 KiB
Go
147 lines
4.6 KiB
Go
package controller
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
"text/template"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/fields"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
monkalev1alpha1 "github.com/monkale.io/coredns-manager-operator/api/v1alpha1"
|
|
)
|
|
|
|
// bakedRecords represents the records that are members of the Zonefile.
|
|
type bakedRecords struct {
|
|
count int
|
|
recordsString string
|
|
}
|
|
|
|
// constructZoneFile - constructs and validates Zone.
|
|
func constructZoneFile(dnsZone *monkalev1alpha1.DNSZone, records string, serialNumber string) (string, error) {
|
|
var newZoneHeader string
|
|
newZoneHeaderValues := monkalev1alpha1.DNSZoneHeader{
|
|
DomainName: monkalev1alpha1.EnsureFQDN(dnsZone.Spec.Domain),
|
|
PrimaryNSHostname: dnsZone.Spec.PrimaryNS.Hostname,
|
|
PrimaryNSIp: dnsZone.Spec.PrimaryNS.IPAddress,
|
|
PrimaryNSType: dnsZone.Spec.PrimaryNS.RecordType,
|
|
RespPerson: dnsZone.Spec.RespPersonEmail,
|
|
ZoneTTL: dnsZone.Spec.TTL,
|
|
Serial: serialNumber,
|
|
Refresh: dnsZone.Spec.RefreshRate,
|
|
Retry: dnsZone.Spec.RetryInterval,
|
|
Expire: dnsZone.Spec.ExpireTime,
|
|
MinimumTTL: dnsZone.Spec.MinimumTTL,
|
|
}
|
|
newZoneHeader, err := templateZoneHeader(newZoneHeaderValues)
|
|
if err != nil {
|
|
return "", fmt.Errorf("unable to template the zoneHeader: %v", err)
|
|
}
|
|
zonefileContent := newZoneHeader + records
|
|
return zonefileContent, nil
|
|
}
|
|
|
|
// constructZoneConfigMap constructs config map for the Zone
|
|
func constructZoneConfigMap(cmObj string, dnsZone *monkalev1alpha1.DNSZone, zonefileContent string, upcomingCMAnnotations map[string]string) (corev1.ConfigMap, error) {
|
|
cm := corev1.ConfigMap{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: cmObj,
|
|
Namespace: dnsZone.ObjectMeta.Namespace,
|
|
Labels: map[string]string{"app": "coredns-addon-operator"},
|
|
Annotations: upcomingCMAnnotations,
|
|
OwnerReferences: []metav1.OwnerReference{
|
|
{
|
|
APIVersion: dnsZone.APIVersion,
|
|
Kind: dnsZone.Kind,
|
|
Name: dnsZone.Name,
|
|
UID: dnsZone.UID,
|
|
},
|
|
},
|
|
},
|
|
Data: map[string]string{
|
|
monkalev1alpha1.EnsureFQDN(dnsZone.Spec.Domain) + "zone": zonefileContent,
|
|
},
|
|
}
|
|
return cm, nil
|
|
}
|
|
|
|
// templateZoneHeader builds Zone header: SOA and first NS A record
|
|
func templateZoneHeader(header monkalev1alpha1.DNSZoneHeader) (string, error) {
|
|
zoneTmpl := `$ORIGIN {{.DomainName}}
|
|
$TTL {{ .ZoneTTL }}s
|
|
@ IN SOA {{.PrimaryNSHostname}}.{{.DomainName}} {{.RespPerson}}. (
|
|
{{.Serial}} ; Serial
|
|
{{.Refresh}} ; Refresh
|
|
{{.Retry}} ; Retry
|
|
{{.Expire}} ; Expire
|
|
{{.MinimumTTL}} ; Minimum TTL
|
|
)
|
|
@ IN NS {{.PrimaryNSHostname}}.{{.DomainName}}
|
|
{{.PrimaryNSHostname}} IN {{.PrimaryNSType}} {{.PrimaryNSIp}}
|
|
`
|
|
tmpl, err := template.New("HEADER").Parse(zoneTmpl)
|
|
if err != nil {
|
|
return "", fmt.Errorf("could not template SOA or NS: %v", err)
|
|
}
|
|
|
|
var result bytes.Buffer
|
|
if err := tmpl.Execute(&result, header); err != nil {
|
|
return "", fmt.Errorf("could not template SOA or NS: %v", err)
|
|
}
|
|
zoneHeader := result.String()
|
|
return zoneHeader, nil
|
|
}
|
|
|
|
// getGoodDnsRecords fetches all DNSRecords. fails if bad records found
|
|
func (r *DNSZoneReconciler) getGoodDnsRecords(ctx context.Context, dnsZone *monkalev1alpha1.DNSZone) (monkalev1alpha1.DNSRecordList, error) {
|
|
records := &monkalev1alpha1.DNSRecordList{}
|
|
// list all DNSRecord with the same DNSZone.
|
|
fieldSelector := fields.OneTermEqualSelector(monkalev1alpha1.DnsRecordIndex, dnsZone.Name)
|
|
listOps := &client.ListOptions{
|
|
FieldSelector: fieldSelector,
|
|
Namespace: dnsZone.Namespace,
|
|
}
|
|
if err := r.List(ctx, records, listOps); err != nil {
|
|
return monkalev1alpha1.DNSRecordList{}, fmt.Errorf("could not list DNSRecords: %v", err)
|
|
}
|
|
|
|
// get only good records
|
|
goodRecords := monkalev1alpha1.DNSRecordList{}
|
|
for _, record := range records.Items {
|
|
if record.Status.ValidationPassed {
|
|
goodRecords.Items = append(goodRecords.Items, record)
|
|
}
|
|
}
|
|
|
|
// sort records a-z
|
|
sort.Slice(goodRecords.Items, func(i, j int) bool {
|
|
return goodRecords.Items[i].Name < goodRecords.Items[j].Name
|
|
})
|
|
|
|
return goodRecords, nil
|
|
}
|
|
|
|
// bakeRecords bakes DNSRecords into the single Zone file compatible string.
|
|
func bakeRecords(dnsRecords monkalev1alpha1.DNSRecordList) (bakedRecords, error) {
|
|
records := dnsRecords.Items
|
|
var sb strings.Builder
|
|
for i, record := range records {
|
|
sb.WriteString(record.Status.GeneratedRecord)
|
|
if i < len(records)-1 {
|
|
sb.WriteString("\n")
|
|
}
|
|
}
|
|
linesCount := len(strings.Split(sb.String(), "\n"))
|
|
corednsEntries := bakedRecords{
|
|
count: linesCount,
|
|
recordsString: sb.String(),
|
|
}
|
|
|
|
return corednsEntries, nil
|
|
}
|