diff --git a/.gitignore b/.gitignore index 3e6795aafe..27ea83b214 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,13 @@ tofu.tfstate.* .tofu/ .tofu.lock.hcl +# custom org tooling +# TODO: replace Makefile by taskfile +taskfile.yaml + +# chainsaw local test run +chainsaw/kubeconfig/ + mkdocs-env/ site/ docs/__pycache__/ diff --git a/Makefile b/Makefile index ca746d8d2a..af65dc15fd 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,7 @@ DEMO_URL ?= http://failover.cloud.example.com DEMO_DEBUG ?=0 DEMO_DELAY ?=5 GSLB_CRD_YAML ?= chart/k8gb/crd/k8gb.absa.oss_gslbs.yaml +DZ_CRD_YAML ?= chart/k8gb/crd/k8gb.io_zonedelegation.yaml # GCP Cloud DNS testing variables GCP_PROJECT ?= @@ -651,7 +652,9 @@ endef define crd-manifest $(call install-controller-gen) @echo -e "\n$(YELLOW)Generating the CRD manifests$(NC)" - $(GOBIN)/controller-gen crd:crdVersions=v1 paths="./..." output:crd:stdout > $(GSLB_CRD_YAML) + $(GOBIN)/controller-gen crd:crdVersions=v1 paths="./api/v1beta1" output:crd:stdout > $(GSLB_CRD_YAML) + @echo -e "\n$(YELLOW)Generating the k8gb.io CRD manifests$(NC)" + $(GOBIN)/controller-gen crd:crdVersions=v1 paths="./api/k8gb.io/v1beta1" output:crd:stdout > $(DZ_CRD_YAML) endef define install-controller-gen diff --git a/api/k8gb.io/v1beta1/groupversion_info.go b/api/k8gb.io/v1beta1/groupversion_info.go new file mode 100644 index 0000000000..c364e37e13 --- /dev/null +++ b/api/k8gb.io/v1beta1/groupversion_info.go @@ -0,0 +1,43 @@ +// Package v1beta1 contains API Schema definitions for the k8gb v1beta1 API group +// +kubebuilder:object:generate=true +// +groupName=k8gb.io +package v1beta1 + +/* +Copyright 2021-2025 The k8gb Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Generated by GoLic, for more details see: https://github.com/AbsaOSS/golic +*/ + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "k8gb.io", Version: "v1beta1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) + +func init() { + // Register ZoneDelegation types with the runtime scheme so the controller client can use them + SchemeBuilder.Register(&ZoneDelegation{}, &ZoneDelegationList{}) +} diff --git a/api/k8gb.io/v1beta1/zonedelegation_types.go b/api/k8gb.io/v1beta1/zonedelegation_types.go new file mode 100644 index 0000000000..9417600495 --- /dev/null +++ b/api/k8gb.io/v1beta1/zonedelegation_types.go @@ -0,0 +1,75 @@ +package v1beta1 + +/* +Copyright 2021-2025 The k8gb Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Generated by GoLic, for more details see: https://github.com/AbsaOSS/golic +*/ + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ZoneDelegationSpec defines the desired state of ZoneDelegation + +type ZoneDelegationSpec struct { + // LoadBalancedZone is the DNS zone managed by this ZoneDelegation + LoadBalancedZone string `json:"loadBalancedZone"` + + // ParentZone is the zone under which this load-balanced zone is delegated + ParentZone string `json:"parentZone"` + + // DNSZoneNegTTL specifies the negative TTL for the DNS zone (in seconds) + DNSZoneNegTTL int `json:"dnsZoneNegTTL"` +} + +// ZoneDelegationStatus defines the observed state of ZoneDelegation +type ZoneDelegationStatus struct { + // DNSServers lists the authoritative DNS servers for the delegated zone + DNSServers []DNSServer `json:"dnsServers,omitempty"` +} + +// DNSServer represents a single DNS server for a zone delegation +type DNSServer struct { + // Name of the DNS server (FQDN) + Name string `json:"name"` + + // Address of the DNS server (IPv4 or IPv6) + Address string `json:"address"` +} + +// ZoneDelegation is the Schema for the zonedelegations API +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster,shortName=zd +// +kubebuilder:printcolumn:name="LoadBalancedZone",type=string,JSONPath=`.spec.loadBalancedZone` +// +kubebuilder:printcolumn:name="ParentZone",type=string,JSONPath=`.spec.parentZone` +// +kubebuilder:printcolumn:name="NegTTL",type=integer,JSONPath=`.spec.dnsZoneNegTTL` +type ZoneDelegation struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ZoneDelegationSpec `json:"spec,omitempty"` + Status ZoneDelegationStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ZoneDelegationList contains a list of ZoneDelegation +type ZoneDelegationList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ZoneDelegation `json:"items"` +} diff --git a/api/k8gb.io/v1beta1/zz_generated.deepcopy.go b/api/k8gb.io/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..eec89ec3db --- /dev/null +++ b/api/k8gb.io/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,136 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2021-2025 The k8gb Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Generated by GoLic, for more details see: https://github.com/AbsaOSS/golic +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1beta1 + +import ( + 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 *DNSServer) DeepCopyInto(out *DNSServer) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSServer. +func (in *DNSServer) DeepCopy() *DNSServer { + if in == nil { + return nil + } + out := new(DNSServer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ZoneDelegation) DeepCopyInto(out *ZoneDelegation) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ZoneDelegation. +func (in *ZoneDelegation) DeepCopy() *ZoneDelegation { + if in == nil { + return nil + } + out := new(ZoneDelegation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ZoneDelegation) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ZoneDelegationList) DeepCopyInto(out *ZoneDelegationList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ZoneDelegation, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ZoneDelegationList. +func (in *ZoneDelegationList) DeepCopy() *ZoneDelegationList { + if in == nil { + return nil + } + out := new(ZoneDelegationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ZoneDelegationList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ZoneDelegationSpec) DeepCopyInto(out *ZoneDelegationSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ZoneDelegationSpec. +func (in *ZoneDelegationSpec) DeepCopy() *ZoneDelegationSpec { + if in == nil { + return nil + } + out := new(ZoneDelegationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ZoneDelegationStatus) DeepCopyInto(out *ZoneDelegationStatus) { + *out = *in + if in.DNSServers != nil { + in, out := &in.DNSServers, &out.DNSServers + *out = make([]DNSServer, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ZoneDelegationStatus. +func (in *ZoneDelegationStatus) DeepCopy() *ZoneDelegationStatus { + if in == nil { + return nil + } + out := new(ZoneDelegationStatus) + in.DeepCopyInto(out) + return out +} diff --git a/chart/k8gb/Chart.yaml b/chart/k8gb/Chart.yaml index c3c3ac7c3b..500c7e3b43 100644 --- a/chart/k8gb/Chart.yaml +++ b/chart/k8gb/Chart.yaml @@ -57,6 +57,11 @@ annotations: version: v1alpha1 displayName: DNSEndpoint description: Using ExternalDNS it synchronizes exposed Kubernetes Services and Ingresses with DNS providers + - kind: ZoneDelegation + version: v1beta1 + name: zonedelegations.k8gb.io + displayName: ZoneDelegation + description: ZoneDelegation enables dynamic activation of DNS zones via the ZoneDelegation resource to prevent premature NXDOMAIN responses in multi-tenant or shared clusters. artifacthub.io/crdsExamples: | - apiVersion: k8gb.absa.oss/v1beta1 kind: Gslb @@ -78,3 +83,17 @@ annotations: strategy: type: failover # Global load balancing strategy primaryGeoTag: eu-west-1 # Primary cluster geo tag + - apiVersion: k8gb.io/v1beta1 + kind: ZoneDelegation + metadata: + name: test-zone + spec: + loadBalancedZone: test-zone.cloud.example.com + parentZone: cloud.example.com + dnsZoneNegTTL: 30 + status: + dnsServers: + - name: gslb-ns-eu-cloud.example.com + address: 172.18.0.6 + - name: gslb-ns-us-cloud.example.com + address: 172.18.0.10 \ No newline at end of file diff --git a/chart/k8gb/crd/k8gb.absa.oss_gslbs.yaml b/chart/k8gb/crd/k8gb.absa.oss_gslbs.yaml index 566aa5686e..c7e1ea8f33 100644 --- a/chart/k8gb/crd/k8gb.absa.oss_gslbs.yaml +++ b/chart/k8gb/crd/k8gb.absa.oss_gslbs.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.20.1 name: gslbs.k8gb.absa.oss spec: group: k8gb.absa.oss diff --git a/chart/k8gb/crd/k8gb.io_zonedelegation.yaml b/chart/k8gb/crd/k8gb.io_zonedelegation.yaml new file mode 100644 index 0000000000..730fa33dd9 --- /dev/null +++ b/chart/k8gb/crd/k8gb.io_zonedelegation.yaml @@ -0,0 +1,95 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.20.1 + name: zonedelegations.k8gb.io +spec: + group: k8gb.io + names: + kind: ZoneDelegation + listKind: ZoneDelegationList + plural: zonedelegations + shortNames: + - zd + singular: zonedelegation + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.loadBalancedZone + name: LoadBalancedZone + type: string + - jsonPath: .spec.parentZone + name: ParentZone + type: string + - jsonPath: .spec.dnsZoneNegTTL + name: NegTTL + type: integer + name: v1beta1 + schema: + openAPIV3Schema: + description: ZoneDelegation is the Schema for the zonedelegations API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + properties: + dnsZoneNegTTL: + description: DNSZoneNegTTL specifies the negative TTL for the DNS + zone (in seconds) + type: integer + loadBalancedZone: + description: LoadBalancedZone is the DNS zone managed by this ZoneDelegation + type: string + parentZone: + description: ParentZone is the zone under which this load-balanced + zone is delegated + type: string + required: + - dnsZoneNegTTL + - loadBalancedZone + - parentZone + type: object + status: + description: ZoneDelegationStatus defines the observed state of ZoneDelegation + properties: + dnsServers: + description: DNSServers lists the authoritative DNS servers for the + delegated zone + items: + description: DNSServer represents a single DNS server for a zone + delegation + properties: + address: + description: Address of the DNS server (IPv4 or IPv6) + type: string + name: + description: Name of the DNS server (FQDN) + type: string + required: + - address + - name + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/main.go b/main.go index 1e1c2a400f..d568961979 100644 --- a/main.go +++ b/main.go @@ -22,15 +22,16 @@ import ( "context" "os" - "github.com/k8gb-io/k8gb/controllers/resolver" - + k8gbiov1beta1 "github.com/k8gb-io/k8gb/api/k8gb.io/v1beta1" k8gbv1beta1 "github.com/k8gb-io/k8gb/api/v1beta1" "github.com/k8gb-io/k8gb/controllers" boot "github.com/k8gb-io/k8gb/controllers/bootstrap" "github.com/k8gb-io/k8gb/controllers/logging" "github.com/k8gb-io/k8gb/controllers/providers/dns" "github.com/k8gb-io/k8gb/controllers/providers/metrics" + "github.com/k8gb-io/k8gb/controllers/resolver" "github.com/k8gb-io/k8gb/controllers/tracing" + istio "istio.io/client-go/pkg/apis/networking/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" @@ -57,6 +58,7 @@ var ( func init() { utilruntime.Must(clientgoscheme.AddToScheme(runtimescheme)) utilruntime.Must(k8gbv1beta1.AddToScheme(runtimescheme)) + utilruntime.Must(k8gbiov1beta1.AddToScheme(runtimescheme)) utilruntime.Must(istio.AddToScheme(runtimescheme)) utilruntime.Must(gatewayapiv1.Install(runtimescheme)) utilruntime.Must(gatewayapiv1alpha2.Install(runtimescheme))