Name it and watch with annotations

This commit is contained in:
superflo22 2025-04-11 16:23:52 +02:00
parent e5902a49f8
commit fda3120bd3
5 changed files with 230 additions and 12 deletions

View file

@ -24,12 +24,58 @@ import (
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
) )
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AuthorityEndpoint) DeepCopyInto(out *AuthorityEndpoint) {
*out = *in
out.APIKeySecretRef = in.APIKeySecretRef
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthorityEndpoint.
func (in *AuthorityEndpoint) DeepCopy() *AuthorityEndpoint {
if in == nil {
return nil
}
out := new(AuthorityEndpoint)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AuthorityReference) DeepCopyInto(out *AuthorityReference) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthorityReference.
func (in *AuthorityReference) DeepCopy() *AuthorityReference {
if in == nil {
return nil
}
out := new(AuthorityReference)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecretKeySelector) DeepCopyInto(out *SecretKeySelector) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretKeySelector.
func (in *SecretKeySelector) DeepCopy() *SecretKeySelector {
if in == nil {
return nil
}
out := new(SecretKeySelector)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TechnitiumAuthority) DeepCopyInto(out *TechnitiumAuthority) { func (in *TechnitiumAuthority) DeepCopyInto(out *TechnitiumAuthority) {
*out = *in *out = *in
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status out.Status = in.Status
} }
@ -86,6 +132,12 @@ func (in *TechnitiumAuthorityList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TechnitiumAuthoritySpec) DeepCopyInto(out *TechnitiumAuthoritySpec) { func (in *TechnitiumAuthoritySpec) DeepCopyInto(out *TechnitiumAuthoritySpec) {
*out = *in *out = *in
out.Primary = in.Primary
if in.Secondaries != nil {
in, out := &in.Secondaries, &out.Secondaries
*out = make([]AuthorityEndpoint, len(*in))
copy(*out, *in)
}
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TechnitiumAuthoritySpec. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TechnitiumAuthoritySpec.
@ -118,7 +170,7 @@ func (in *TechnitiumRecord) DeepCopyInto(out *TechnitiumRecord) {
*out = *in *out = *in
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status out.Status = in.Status
} }
@ -175,6 +227,23 @@ func (in *TechnitiumRecordList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TechnitiumRecordSpec) DeepCopyInto(out *TechnitiumRecordSpec) { func (in *TechnitiumRecordSpec) DeepCopyInto(out *TechnitiumRecordSpec) {
*out = *in *out = *in
out.AuthorityRef = in.AuthorityRef
if in.RecordData != nil {
in, out := &in.RecordData, &out.RecordData
*out = make(map[string][]string, len(*in))
for key, val := range *in {
var outVal []string
if val == nil {
(*out)[key] = nil
} else {
inVal := (*in)[key]
in, out := &inVal, &outVal
*out = make([]string, len(*in))
copy(*out, *in)
}
(*out)[key] = outVal
}
}
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TechnitiumRecordSpec. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TechnitiumRecordSpec.

View file

@ -40,10 +40,58 @@ spec:
spec: spec:
description: TechnitiumAuthoritySpec defines the desired state of TechnitiumAuthority description: TechnitiumAuthoritySpec defines the desired state of TechnitiumAuthority
properties: properties:
foo: dnsApp.config:
description: Foo is an example field of TechnitiumAuthority. Edit
technitiumauthority_types.go to remove/update
type: string type: string
primary:
description: AuthorityEndpoint defines the API endpoint and credentials
properties:
apiKeySecretRef:
description: SecretKeySelector defines the reference to a key
inside a Kubernetes Secret
properties:
key:
type: string
name:
type: string
required:
- key
- name
type: object
endpoint:
type: string
required:
- apiKeySecretRef
- endpoint
type: object
secondaries:
items:
description: AuthorityEndpoint defines the API endpoint and credentials
properties:
apiKeySecretRef:
description: SecretKeySelector defines the reference to a key
inside a Kubernetes Secret
properties:
key:
type: string
name:
type: string
required:
- key
- name
type: object
endpoint:
type: string
required:
- apiKeySecretRef
- endpoint
type: object
type: array
zone:
type: string
required:
- dnsApp.config
- primary
- zone
type: object type: object
status: status:
description: TechnitiumAuthorityStatus defines the observed state of TechnitiumAuthority description: TechnitiumAuthorityStatus defines the observed state of TechnitiumAuthority

View file

@ -39,10 +39,31 @@ spec:
spec: spec:
description: TechnitiumRecordSpec defines the desired state of TechnitiumRecord description: TechnitiumRecordSpec defines the desired state of TechnitiumRecord
properties: properties:
foo: authorityRef:
description: Foo is an example field of TechnitiumRecord. Edit technitiumrecord_types.go description: AuthorityReference references a TechnitiumAuthority
to remove/update properties:
name:
type: string type: string
required:
- name
type: object
classPath:
type: string
name:
type: string
recordData:
additionalProperties:
items:
type: string
type: array
type: object
ttl:
type: integer
required:
- authorityRef
- classPath
- name
- ttl
type: object type: object
status: status:
description: TechnitiumRecordStatus defines the observed state of TechnitiumRecord description: TechnitiumRecordStatus defines the observed state of TechnitiumRecord

View file

@ -7,6 +7,7 @@ rules:
- apiGroups: - apiGroups:
- dns.mayers.cloud - dns.mayers.cloud
resources: resources:
- externaldnswatchers
- technitiumauthorities - technitiumauthorities
- technitiumrecords - technitiumrecords
verbs: verbs:
@ -20,6 +21,7 @@ rules:
- apiGroups: - apiGroups:
- dns.mayers.cloud - dns.mayers.cloud
resources: resources:
- externaldnswatchers/finalizers
- technitiumauthorities/finalizers - technitiumauthorities/finalizers
- technitiumrecords/finalizers - technitiumrecords/finalizers
verbs: verbs:
@ -27,6 +29,7 @@ rules:
- apiGroups: - apiGroups:
- dns.mayers.cloud - dns.mayers.cloud
resources: resources:
- externaldnswatchers/status
- technitiumauthorities/status - technitiumauthorities/status
- technitiumrecords/status - technitiumrecords/status
verbs: verbs:

View file

@ -18,6 +18,10 @@ package controller
import ( import (
"context" "context"
"fmt"
dnsv1alpha1 "git.mayers.cloud/superflo22/split-horizon-operator/api/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/handler"
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -46,9 +50,75 @@ type ExternalDNSWatcherReconciler struct {
// For more details, check Reconcile and its Result here: // For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/reconcile // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/reconcile
func (r *ExternalDNSWatcherReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { func (r *ExternalDNSWatcherReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx) log := log.FromContext(ctx)
// TODO(user): your logic here // Step 1: Fetch the Gateway
var gateway gatewayv1.Gateway
if err := r.Get(ctx, req.NamespacedName, &gateway); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// Step 2: Check annotation
annotations := gateway.GetAnnotations()
if annotations["dns.mayers.cloud/enabled"] != "true" {
return ctrl.Result{}, nil
}
network := annotations["dns.mayers.cloud/network"]
authority := annotations["dns.mayers.cloud/authority"]
if network == "" || authority == "" {
log.Info("Missing required annotations")
return ctrl.Result{}, nil
}
// Step 3: Get Gateway IP (if available)
var gatewayIPs []string
for _, addr := range gateway.Status.Addresses {
if addr.Type == nil || *addr.Type == gatewayv1.IPAddressType {
gatewayIPs = append(gatewayIPs, addr.Value)
}
}
// Step 4: List HTTPRoutes that reference this Gateway
var routes gatewayv1.HTTPRouteList
if err := r.List(ctx, &routes, client.InNamespace(req.Namespace)); err != nil {
return ctrl.Result{}, err
}
for _, route := range routes.Items {
for _, parent := range route.Spec.ParentRefs {
// Check if the parent is a Gateway and matches the current gateway
if string(*parent.Kind) == "Gateway" && string(parent.Name) == gateway.Name {
// Step 5: Collect hostnames
for _, hostname := range route.Spec.Hostnames {
record := &dnsv1alpha1.TechnitiumRecord{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-%s", route.Name, string(hostname)),
Namespace: route.Namespace,
},
Spec: dnsv1alpha1.TechnitiumRecordSpec{
AuthorityRef: dnsv1alpha1.AuthorityReference{
Name: authority,
},
Name: string(hostname),
TTL: 300,
ClassPath: "SimpleAddress",
RecordData: map[string][]string{
network: gatewayIPs,
},
},
}
if err := r.Client.Patch(ctx, record, client.Apply, client.ForceOwnership, client.FieldOwner("external-dns-watcher")); err != nil {
log.Error(err, "Failed to apply TechnitiumRecord")
} else {
log.Info("Reconciled TechnitiumRecord", "name", record.Name)
}
}
}
}
}
return ctrl.Result{}, nil return ctrl.Result{}, nil
} }
@ -57,7 +127,14 @@ func (r *ExternalDNSWatcherReconciler) Reconcile(ctx context.Context, req ctrl.R
func (r *ExternalDNSWatcherReconciler) SetupWithManager(mgr ctrl.Manager) error { func (r *ExternalDNSWatcherReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr). return ctrl.NewControllerManagedBy(mgr).
// Uncomment the following line adding a pointer to an instance of the controlled resource as an argument // Uncomment the following line adding a pointer to an instance of the controlled resource as an argument
For(&gatewayv1.HTTPRoute{}). Named("external_dns_controller").
For(&gatewayv1.Gateway{}). Watches(
&gatewayv1.Gateway{},
&handler.EnqueueRequestForObject{},
).
Watches(
&gatewayv1.HTTPRoute{},
&handler.EnqueueRequestForObject{},
).
Complete(r) Complete(r)
} }