From 12687cb94b7c75e66b62090cb49e84deb75f669a Mon Sep 17 00:00:00 2001 From: Michael Nairn Date: Tue, 20 Aug 2024 16:33:58 +0100 Subject: [PATCH] Remove ManagedZone API (#793) * chore: Remove ManagedZone API Signed-off-by: Michael Nairn Signed-off-by: craig * minor tweaks and change helm rf Signed-off-by: craig * remote zone id from secrets. Add example policy Signed-off-by: craig * update the policy reference doc Signed-off-by: craig --------- Signed-off-by: craig Co-authored-by: craig --- Makefile | 2 +- api/v1alpha1/dnspolicy_types.go | 33 +- api/v1alpha1/zz_generated.deepcopy.go | 5 + ...adrant-operator.clusterserviceversion.yaml | 10 +- bundle/manifests/kuadrant.io_dnspolicies.yaml | 17 +- bundle/metadata/dependencies.yaml | 2 +- charts/kuadrant-operator/Chart.yaml | 1 - .../templates/manifests.yaml | 1602 +++-------------- config/crd/bases/kuadrant.io_dnspolicies.yaml | 17 +- config/dependencies/dns/kustomization.yaml | 2 +- ...adrant-operator.clusterserviceversion.yaml | 5 - config/rbac/role.yaml | 8 - controllers/dns_helper.go | 52 - controllers/dnspolicy_controller.go | 2 - controllers/dnspolicy_dnsrecords.go | 15 +- doc/dns.md | 99 +- doc/reference/dnspolicy.md | 15 + doc/user-guides/gateway-dns.md | 30 +- ...re-protect-connect-single-multi-cluster.md | 39 +- doc/user-guides/secure-protect-connect.md | 22 +- .../dnspolicy/aws-dns-provider-secret.yaml | 10 + .../dnspolicy/dnspolicy-bad-strategy.yaml | 2 + .../dnspolicy/dnspolicy-healthchecks.yaml | 2 + examples/dnspolicy/dnspolicy.yaml | 2 + examples/dnspolicy/managedzone.yaml | 22 - examples/dnspolicy/script.sh | 4 +- go.mod | 4 +- go.sum | 6 +- hack/quickstart-setup.sh | 58 +- ...dnspolicy_controller_multi_cluster_test.go | 48 +- ...nspolicy_controller_single_cluster_test.go | 38 +- .../dnspolicy/dnspolicy_controller_test.go | 85 +- .../target_status_controller_test.go | 44 +- tests/commons.go | 33 +- 34 files changed, 533 insertions(+), 1803 deletions(-) create mode 100644 examples/dnspolicy/aws-dns-provider-secret.yaml delete mode 100644 examples/dnspolicy/managedzone.yaml diff --git a/Makefile b/Makefile index 042c23067..794208f53 100644 --- a/Makefile +++ b/Makefile @@ -140,7 +140,7 @@ endif LIMITADOR_OPERATOR_BUNDLE_IMG ?= quay.io/kuadrant/limitador-operator-bundle:$(LIMITADOR_OPERATOR_BUNDLE_IMG_TAG) ## dns -DNS_OPERATOR_VERSION ?= 0.4.1 +DNS_OPERATOR_VERSION ?= main kuadrantdns_bundle_is_semantic := $(call is_semantic_version,$(DNS_OPERATOR_VERSION)) ifeq (latest,$(DNS_OPERATOR_VERSION)) diff --git a/api/v1alpha1/dnspolicy_types.go b/api/v1alpha1/dnspolicy_types.go index e74801b9f..62568efa9 100644 --- a/api/v1alpha1/dnspolicy_types.go +++ b/api/v1alpha1/dnspolicy_types.go @@ -19,6 +19,7 @@ package v1alpha1 import ( "context" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/utils/ptr" @@ -26,7 +27,7 @@ import ( gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - "github.com/kuadrant/dns-operator/api/v1alpha1" + dnsv1alpha1 "github.com/kuadrant/dns-operator/api/v1alpha1" kuadrantgatewayapi "github.com/kuadrant/kuadrant-operator/pkg/library/gatewayapi" "github.com/kuadrant/kuadrant-operator/pkg/library/kuadrant" @@ -58,13 +59,13 @@ const ( // DNSPolicySpec defines the desired state of DNSPolicy // +kubebuilder:validation:XValidation:rule="!(self.routingStrategy == 'loadbalanced' && !has(self.loadBalancing))",message="spec.loadBalancing is a required field when spec.routingStrategy == 'loadbalanced'" type DNSPolicySpec struct { - // TargetRef identifies an API object to apply policy to. + // targetRef identifies an API object to apply policy to. // +kubebuilder:validation:XValidation:rule="self.group == 'gateway.networking.k8s.io'",message="Invalid targetRef.group. The only supported value is 'gateway.networking.k8s.io'" // +kubebuilder:validation:XValidation:rule="self.kind == 'Gateway'",message="Invalid targetRef.kind. The only supported values are 'Gateway'" TargetRef gatewayapiv1alpha2.LocalPolicyTargetReference `json:"targetRef"` // +optional - HealthCheck *v1alpha1.HealthCheckSpec `json:"healthCheck,omitempty"` + HealthCheck *dnsv1alpha1.HealthCheckSpec `json:"healthCheck,omitempty"` // +optional LoadBalancing *LoadBalancingSpec `json:"loadBalancing,omitempty"` @@ -73,6 +74,11 @@ type DNSPolicySpec struct { // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="RoutingStrategy is immutable" // +kubebuilder:default=loadbalanced RoutingStrategy RoutingStrategy `json:"routingStrategy"` + + // providerRefs is a list of references to provider secrets. Max is one but intention is to allow this to be more in the future + // +kubebuilder:validation:MaxItems=1 + // +kubebuilder:validation:MinItems=1 + ProviderRefs []dnsv1alpha1.ProviderRef `json:"providerRefs"` } type LoadBalancingSpec struct { @@ -142,7 +148,7 @@ type DNSPolicyStatus struct { ObservedGeneration int64 `json:"observedGeneration,omitempty"` // +optional - HealthCheck *v1alpha1.HealthCheckStatus `json:"healthCheck,omitempty"` + HealthCheck *dnsv1alpha1.HealthCheckStatus `json:"healthCheck,omitempty"` // +optional RecordConditions map[string][]metav1.Condition `json:"recordConditions,omitempty"` @@ -249,7 +255,7 @@ func (p *DNSPolicy) WithTargetRef(targetRef gatewayapiv1alpha2.LocalPolicyTarget return p } -func (p *DNSPolicy) WithHealthCheck(healthCheck v1alpha1.HealthCheckSpec) *DNSPolicy { +func (p *DNSPolicy) WithHealthCheck(healthCheck dnsv1alpha1.HealthCheckSpec) *DNSPolicy { p.Spec.HealthCheck = &healthCheck return p } @@ -264,6 +270,19 @@ func (p *DNSPolicy) WithRoutingStrategy(strategy RoutingStrategy) *DNSPolicy { return p } +func (p *DNSPolicy) WithProviderRef(providerRef dnsv1alpha1.ProviderRef) *DNSPolicy { + p.Spec.ProviderRefs = append(p.Spec.ProviderRefs, providerRef) + return p +} + +//ProviderRef + +func (p *DNSPolicy) WithProviderSecret(s corev1.Secret) *DNSPolicy { + return p.WithProviderRef(dnsv1alpha1.ProviderRef{ + Name: s.Name, + }) +} + //TargetRef func (p *DNSPolicy) WithTargetGateway(gwName string) *DNSPolicy { @@ -277,10 +296,10 @@ func (p *DNSPolicy) WithTargetGateway(gwName string) *DNSPolicy { //HealthCheck func (p *DNSPolicy) WithHealthCheckFor(endpoint string, port int, protocol string, failureThreshold int) *DNSPolicy { - return p.WithHealthCheck(v1alpha1.HealthCheckSpec{ + return p.WithHealthCheck(dnsv1alpha1.HealthCheckSpec{ Endpoint: endpoint, Port: &port, - Protocol: ptr.To(v1alpha1.HealthProtocol(protocol)), + Protocol: ptr.To(dnsv1alpha1.HealthProtocol(protocol)), FailureThreshold: &failureThreshold, }) } diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 49c970a98..f4d71f01e 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -161,6 +161,11 @@ func (in *DNSPolicySpec) DeepCopyInto(out *DNSPolicySpec) { *out = new(LoadBalancingSpec) (*in).DeepCopyInto(*out) } + if in.ProviderRefs != nil { + in, out := &in.ProviderRefs, &out.ProviderRefs + *out = make([]apiv1alpha1.ProviderRef, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSPolicySpec. diff --git a/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml b/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml index 0a32f256c..f765c2b54 100644 --- a/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml +++ b/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml @@ -106,7 +106,7 @@ metadata: capabilities: Basic Install categories: Integration & Delivery containerImage: quay.io/kuadrant/kuadrant-operator:latest - createdAt: "2024-08-14T15:45:59Z" + createdAt: "2024-08-20T09:51:49Z" operators.operatorframework.io/builder: operator-sdk-v1.32.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 repository: https://github.com/Kuadrant/kuadrant-operator @@ -432,14 +432,6 @@ spec: - get - patch - update - - apiGroups: - - kuadrant.io - resources: - - managedzones - verbs: - - get - - list - - watch - apiGroups: - kuadrant.io resources: diff --git a/bundle/manifests/kuadrant.io_dnspolicies.yaml b/bundle/manifests/kuadrant.io_dnspolicies.yaml index e0551e322..20043f024 100644 --- a/bundle/manifests/kuadrant.io_dnspolicies.yaml +++ b/bundle/manifests/kuadrant.io_dnspolicies.yaml @@ -201,6 +201,20 @@ spec: - geo - weighted type: object + providerRefs: + description: providerRefs is a list of references to provider secrets. + Max is one but intention is to allow this to be more in the future + items: + properties: + name: + minLength: 1 + type: string + required: + - name + type: object + maxItems: 1 + minItems: 1 + type: array routingStrategy: default: loadbalanced enum: @@ -211,7 +225,7 @@ spec: - message: RoutingStrategy is immutable rule: self == oldSelf targetRef: - description: TargetRef identifies an API object to apply policy to. + description: targetRef identifies an API object to apply policy to. properties: group: description: Group is the group of the target resource. @@ -240,6 +254,7 @@ spec: - message: Invalid targetRef.kind. The only supported values are 'Gateway' rule: self.kind == 'Gateway' required: + - providerRefs - routingStrategy - targetRef type: object diff --git a/bundle/metadata/dependencies.yaml b/bundle/metadata/dependencies.yaml index 00c085b56..ca93c344a 100644 --- a/bundle/metadata/dependencies.yaml +++ b/bundle/metadata/dependencies.yaml @@ -10,4 +10,4 @@ dependencies: - type: olm.package value: packageName: dns-operator - version: "0.4.1" + version: "0.0.0" diff --git a/charts/kuadrant-operator/Chart.yaml b/charts/kuadrant-operator/Chart.yaml index e62512ed3..d74b12d6e 100644 --- a/charts/kuadrant-operator/Chart.yaml +++ b/charts/kuadrant-operator/Chart.yaml @@ -14,7 +14,6 @@ keywords: - limitador - rate limiting - dns - - managed zones - kubernetes sources: - https://github.com/Kuadrant/kuadrant-operator/ diff --git a/charts/kuadrant-operator/templates/manifests.yaml b/charts/kuadrant-operator/templates/manifests.yaml index 2b75d1d1d..33b4853e3 100644 --- a/charts/kuadrant-operator/templates/manifests.yaml +++ b/charts/kuadrant-operator/templates/manifests.yaml @@ -13379,6 +13379,19 @@ spec: - geo - weighted type: object + providerRefs: + description: providerRefS is a list of references to provider secrets. + items: + properties: + name: + minLength: 1 + type: string + required: + - name + type: object + maxItems: 1 + minItems: 1 + type: array routingStrategy: default: loadbalanced enum: @@ -13389,7 +13402,7 @@ spec: - message: RoutingStrategy is immutable rule: self == oldSelf targetRef: - description: TargetRef identifies an API object to apply policy to. + description: targetRef identifies an API object to apply policy to. properties: group: description: Group is the group of the target resource. @@ -13418,6 +13431,7 @@ spec: - message: Invalid targetRef.kind. The only supported values are 'Gateway' rule: self.kind == 'Gateway' required: + - providerRefs - routingStrategy - targetRef type: object @@ -13567,1368 +13581,244 @@ spec: The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - probes: - items: - properties: - conditions: - items: - description: "Condition contains details for one aspect - of the current state of this API Resource.\n---\nThis - struct is intended for direct use as an array at the - field path .status.conditions. For example,\n\n\n\ttype - FooStatus struct{\n\t // Represents the observations - of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t - \ // +patchMergeKey=type\n\t // +patchStrategy=merge\n\t - \ // +listType=map\n\t // +listMapKey=type\n\t - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t - \ // other fields\n\t}" - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, - False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - host: - type: string - id: - type: string - ipAddress: - type: string - synced: - type: boolean - required: - - host - - id - - ipAddress - type: object - type: array - type: object - observedGeneration: - description: |- - observedGeneration is the most recently observed generation of the - DNSPolicy. When the DNSPolicy is updated, the controller updates the - corresponding configuration. If an update fails, that failure is - recorded in the status condition - format: int64 - type: integer - recordConditions: - additionalProperties: - items: - description: "Condition contains details for one aspect of the - current state of this API Resource.\n---\nThis struct is intended - for direct use as an array at the field path .status.conditions. - \ For example,\n\n\n\ttype FooStatus struct{\n\t // Represents - the observations of a foo's current state.\n\t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // - +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // - +listType=map\n\t // +listMapKey=type\n\t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t // other - fields\n\t}" - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, - Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: |- - type of condition in CamelCase or in foo.example.com/CamelCase. - --- - Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be - useful (see .node.status.conditions), the ability to deconflict is important. - The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - type: object - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.14.0 - labels: - app: kuadrant - name: kuadrants.kuadrant.io -spec: - group: kuadrant.io - names: - kind: Kuadrant - listKind: KuadrantList - plural: kuadrants - singular: kuadrant - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.conditions[0].reason - name: Status - priority: 2 - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: Kuadrant configures installations of Kuadrant Service Protection - components - 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: - description: KuadrantSpec defines the desired state of Kuadrant - properties: - limitador: - properties: - affinity: - description: Affinity is a group of affinity scheduling rules. - properties: - nodeAffinity: - description: Describes node affinity scheduling rules for - the pod. - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node matches the corresponding matchExpressions; the - node(s) with the highest sum are the most preferred. - items: - description: |- - An empty preferred scheduling term matches all objects with implicit weight 0 - (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). - properties: - preference: - description: A node selector term, associated with - the corresponding weight. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - properties: - key: - description: The label key that the selector - applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - properties: - key: - description: The label key that the selector - applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - type: object - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching the - corresponding nodeSelectorTerm, in the range 1-100. - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from its node. - properties: - nodeSelectorTerms: - description: Required. A list of node selector terms. - The terms are ORed. - items: - description: |- - A null or empty node selector term matches no objects. The requirements of - them are ANDed. - The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - properties: - key: - description: The label key that the selector - applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: |- - A node selector requirement is a selector that contains values, a key, and an operator - that relates the key and values. - properties: - key: - description: The label key that the selector - applies to. - type: string - operator: - description: |- - Represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. - type: string - values: - description: |- - An array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. If the operator is Gt or Lt, the values - array must have a single element, which will be interpreted as an integer. - This array is replaced during a strategic merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - type: object - x-kubernetes-map-type: atomic - type: array - x-kubernetes-list-type: atomic - required: - - nodeSelectorTerms - type: object - x-kubernetes-map-type: atomic - type: object - podAffinity: - description: Describes pod affinity scheduling rules (e.g. - co-locate this pod in the same node, zone, etc. as some - other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred - node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated - with the corresponding weight. - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - x-kubernetes-list-type: atomic - type: object - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules - (e.g. avoid putting this pod in the same node, zone, etc. - as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: |- - The scheduler will prefer to schedule pods to nodes that satisfy - the anti-affinity expressions specified by this field, but it may choose - a node that violates one or more of the expressions. The node that is - most preferred is the one with the greatest sum of weights, i.e. - for each node that meets all of the scheduling requirements (resource - request, requiredDuringScheduling anti-affinity expressions, etc.), - compute a sum by iterating through the elements of this field and adding - "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred - node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated - with the corresponding weight. - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: |- - weight associated with matching the corresponding podAffinityTerm, - in the range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - x-kubernetes-list-type: atomic - requiredDuringSchedulingIgnoredDuringExecution: - description: |- - If the anti-affinity requirements specified by this field are not met at - scheduling time, the pod will not be scheduled onto the node. - If the anti-affinity requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod label update), the - system may or may not try to eventually evict the pod from its node. - When there are multiple elements, the lists of nodes corresponding to each - podAffinityTerm are intersected, i.e. all terms must be satisfied. - items: - description: |- - Defines a set of pods (namely those matching the labelSelector - relative to the given namespace(s)) that this pod should be - co-located (affinity) or not co-located (anti-affinity) with, - where co-located is defined as running on a node whose value of - the label with key matches that of any node on which - a pod of the set of pods is running - properties: - labelSelector: - description: |- - A label query over a set of resources, in this case pods. - If it's null, this PodAffinityTerm matches with no Pods. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - matchLabelKeys: - description: |- - MatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both matchLabelKeys and labelSelector. - Also, matchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - mismatchLabelKeys: - description: |- - MismatchLabelKeys is a set of pod label keys to select which pods will - be taken into consideration. The keys are used to lookup values from the - incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` - to select the group of existing pods which pods will be taken into consideration - for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming - pod labels will be ignored. The default value is empty. - The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. - Also, mismatchLabelKeys cannot be set when labelSelector isn't set. - This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate. - items: - type: string - type: array - x-kubernetes-list-type: atomic - namespaceSelector: - description: |- - A label query over the set of namespaces that the term applies to. - The term is applied to the union of the namespaces selected by this field - and the ones listed in the namespaces field. - null selector and null or empty namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: |- - namespaces specifies a static list of namespace names that the term applies to. - The term is applied to the union of the namespaces listed in this field - and the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector means "this pod's namespace". - items: - type: string - type: array - x-kubernetes-list-type: atomic - topologyKey: - description: |- - This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching - the labelSelector in the specified namespaces, where co-located is defined as running on a node - whose value of the label with key topologyKey matches that of any node on which any of the - selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - x-kubernetes-list-type: atomic - type: object - type: object - pdb: - properties: - maxUnavailable: - anyOf: - - type: integer - - type: string - description: |- - An eviction is allowed if at most "maxUnavailable" limitador pods - are unavailable after the eviction, i.e. even in absence of - the evicted pod. For example, one can prevent all voluntary evictions - by specifying 0. This is a mutually exclusive setting with "minAvailable". - x-kubernetes-int-or-string: true - minAvailable: - anyOf: - - type: integer - - type: string - description: |- - An eviction is allowed if at least "minAvailable" limitador pods will - still be available after the eviction, i.e. even in the absence of - the evicted pod. So for example you can prevent all voluntary - evictions by specifying "100%". - x-kubernetes-int-or-string: true - type: object - rateLimitHeaders: - description: RateLimitHeadersType defines the valid options for - the --rate-limit-headers arg - enum: - - NONE - - DRAFT_VERSION_03 - type: string - replicas: - type: integer - resourceRequirements: - description: ResourceRequirements describes the compute resource - requirements. - properties: - claims: - description: |- - Claims lists the names of resources, defined in spec.resourceClaims, - that are used by this container. - - - This is an alpha field and requires enabling the - DynamicResourceAllocation feature gate. - - - This field is immutable. It can only be set for containers. - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: |- - Name must match the name of one entry in pod.spec.resourceClaims of - the Pod where this field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: |- - Limits describes the maximum amount of compute resources allowed. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: |- - Requests describes the minimum amount of compute resources required. - If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests cannot exceed Limits. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - type: object - type: object - storage: - description: Storage contains the options for Limitador counters - database or in-memory data storage - properties: - disk: - properties: - optimize: - description: DiskOptimizeType defines the valid options - for "optimize" option of the disk persistence type - enum: - - throughput - - disk - type: string - persistentVolumeClaim: + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + probes: + items: + properties: + conditions: + items: + description: "Condition contains details for one aspect + of the current state of this API Resource.\n---\nThis + struct is intended for direct use as an array at the + field path .status.conditions. For example,\n\n\n\ttype + FooStatus struct{\n\t // Represents the observations + of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t + \ // +patchMergeKey=type\n\t // +patchStrategy=merge\n\t + \ // +listType=map\n\t // +listMapKey=type\n\t + \ Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" properties: - resources: + lastTransitionTime: description: |- - Resources represents the minimum resources the volume should have. - Ignored when VolumeName field is set - properties: - requests: - anyOf: - - type: integer - - type: string - description: |- - Storage Resource requests to be used on the PersistentVolumeClaim. - To learn more about resource requests see: - https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - required: - - requests - type: object - storageClassName: + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time type: string - volumeName: - description: VolumeName is the binding reference to - the PersistentVolume backing this claim. + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 type: string - type: object - type: object - redis: - properties: - configSecretRef: - description: |- - LocalObjectReference contains enough information to let you locate the - referenced object inside the same namespace. - properties: - name: + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string - type: object - x-kubernetes-map-type: atomic - type: object - redis-cached: - properties: - configSecretRef: - description: |- - LocalObjectReference contains enough information to let you locate the - referenced object inside the same namespace. - properties: - name: + status: + description: status of the condition, one of True, + False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: description: |- - Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid? + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string + required: + - lastTransitionTime + - message + - reason + - status + - type type: object - x-kubernetes-map-type: atomic - options: - properties: - batch-size: - description: 'BatchSize defines the size of entries - to flush in as single flush [default: 100]' - type: integer - flush-period: - description: 'FlushPeriod for counters in milliseconds - [default: 1000]' - type: integer - max-cached: - description: 'MaxCached refers to the maximum amount - of counters cached [default: 10000]' - type: integer - response-timeout: - description: 'ResponseTimeout defines the timeout - for Redis commands in milliseconds [default: 350]' - type: integer - type: object - type: object + type: array + host: + type: string + id: + type: string + ipAddress: + type: string + synced: + type: boolean + required: + - host + - id + - ipAddress + type: object + type: array + type: object + observedGeneration: + description: |- + observedGeneration is the most recently observed generation of the + DNSPolicy. When the DNSPolicy is updated, the controller updates the + corresponding configuration. If an update fails, that failure is + recorded in the status condition + format: int64 + type: integer + recordConditions: + additionalProperties: + items: + description: "Condition contains details for one aspect of the + current state of this API Resource.\n---\nThis struct is intended + for direct use as an array at the field path .status.conditions. + \ For example,\n\n\n\ttype FooStatus struct{\n\t // Represents + the observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // + +listType=map\n\t // +listMapKey=type\n\t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t // other + fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type type: object - telemetry: - description: Telemetry defines the level of metrics Limitador - will expose to the user - enum: - - basic - - exhaustive - type: string - verbosity: - description: Sets the level of verbosity - maximum: 4 - minimum: 1 - type: integer + type: array type: object type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + labels: + app: kuadrant + name: kuadrants.kuadrant.io +spec: + group: kuadrant.io + names: + kind: Kuadrant + listKind: KuadrantList + plural: kuadrants + singular: kuadrant + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[0].reason + name: Status + priority: 2 + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: Kuadrant configures installations of Kuadrant Service Protection + components + 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: + description: KuadrantSpec defines the desired state of Kuadrant + type: object status: description: KuadrantStatus defines the observed state of Kuadrant properties: @@ -17093,14 +15983,6 @@ rules: - get - patch - update -- apiGroups: - - kuadrant.io - resources: - - managedzones - verbs: - - get - - list - - watch - apiGroups: - kuadrant.io resources: @@ -17311,7 +16193,7 @@ spec: env: - name: RELATED_IMAGE_WASMSHIM value: oci://quay.io/kuadrant/wasm-shim:latest - image: quay.io/kuadrant/kuadrant-operator:latest + image: quay.io/kuadrant/kuadrant-operator:remove_managed_zone_api livenessProbe: httpGet: path: /healthz diff --git a/config/crd/bases/kuadrant.io_dnspolicies.yaml b/config/crd/bases/kuadrant.io_dnspolicies.yaml index 5da7009e2..a5c25973e 100644 --- a/config/crd/bases/kuadrant.io_dnspolicies.yaml +++ b/config/crd/bases/kuadrant.io_dnspolicies.yaml @@ -200,6 +200,20 @@ spec: - geo - weighted type: object + providerRefs: + description: providerRefs is a list of references to provider secrets. + Max is one but intention is to allow this to be more in the future + items: + properties: + name: + minLength: 1 + type: string + required: + - name + type: object + maxItems: 1 + minItems: 1 + type: array routingStrategy: default: loadbalanced enum: @@ -210,7 +224,7 @@ spec: - message: RoutingStrategy is immutable rule: self == oldSelf targetRef: - description: TargetRef identifies an API object to apply policy to. + description: targetRef identifies an API object to apply policy to. properties: group: description: Group is the group of the target resource. @@ -239,6 +253,7 @@ spec: - message: Invalid targetRef.kind. The only supported values are 'Gateway' rule: self.kind == 'Gateway' required: + - providerRefs - routingStrategy - targetRef type: object diff --git a/config/dependencies/dns/kustomization.yaml b/config/dependencies/dns/kustomization.yaml index a67369c03..f7abc9be3 100644 --- a/config/dependencies/dns/kustomization.yaml +++ b/config/dependencies/dns/kustomization.yaml @@ -1,5 +1,5 @@ resources: -- github.com/kuadrant/dns-operator/config/default?ref=v0.4.1 +- github.com/kuadrant/dns-operator/config/default?ref=main patches: - path: deployment_patch.yaml diff --git a/config/manifests/bases/kuadrant-operator.clusterserviceversion.yaml b/config/manifests/bases/kuadrant-operator.clusterserviceversion.yaml index 9a25e50fd..7d0bc6ea4 100644 --- a/config/manifests/bases/kuadrant-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/kuadrant-operator.clusterserviceversion.yaml @@ -46,11 +46,6 @@ spec: kind: DNSRecord name: dnsrecords.kuadrant.io version: v1alpha1 - - description: ManagedZone configures domain or subdomain management for gateway hosts - displayName: ManagedZone - kind: ManagedZone - name: managedzones.kuadrant.io - version: v1alpha1 - description: TLSPolicy provides tls for gateway listeners by managing the lifecycle of tls certificates displayName: TLSPolicy kind: TLSPolicy diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 7d89b1790..650255d3d 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -280,14 +280,6 @@ rules: - get - patch - update -- apiGroups: - - kuadrant.io - resources: - - managedzones - verbs: - - get - - list - - watch - apiGroups: - kuadrant.io resources: diff --git a/controllers/dns_helper.go b/controllers/dns_helper.go index 06438d4cd..ceeae1275 100644 --- a/controllers/dns_helper.go +++ b/controllers/dns_helper.go @@ -7,17 +7,14 @@ import ( "strconv" "strings" - "golang.org/x/net/publicsuffix" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/log" externaldns "sigs.k8s.io/external-dns/endpoint" gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" kuadrantdnsv1alpha1 "github.com/kuadrant/dns-operator/api/v1alpha1" "github.com/kuadrant/kuadrant-operator/api/v1alpha1" - "github.com/kuadrant/kuadrant-operator/pkg/library/utils" "github.com/kuadrant/kuadrant-operator/pkg/multicluster" ) @@ -32,50 +29,12 @@ const ( var ( ErrUnknownRoutingStrategy = fmt.Errorf("unknown routing strategy") - ErrNoManagedZoneForHost = fmt.Errorf("no managed zone for host") ) type dnsHelper struct { client.Client } -func findMatchingManagedZone(originalHost, host string, zones []kuadrantdnsv1alpha1.ManagedZone) (*kuadrantdnsv1alpha1.ManagedZone, string, error) { - if len(zones) == 0 { - return nil, "", fmt.Errorf("%w : %s", ErrNoManagedZoneForHost, host) - } - host = strings.ToLower(host) - //get the TLD from this host - tld, _ := publicsuffix.PublicSuffix(host) - - //The host is a TLD, so we now know `originalHost` can't possibly have a valid `ManagedZone` available. - if host == tld { - return nil, "", fmt.Errorf("no valid zone found for host: %v", originalHost) - } - - hostParts := strings.SplitN(host, ".", 2) - if len(hostParts) < 2 { - return nil, "", fmt.Errorf("no valid zone found for host: %s", originalHost) - } - parentDomain := hostParts[1] - - // We do not currently support creating records for Apex domains, and a ManagedZone represents an Apex domain, as such - // we should never be trying to find a managed zone that matches the `originalHost` exactly. Instead, we just continue - // on to the next possible valid host to try i.e. the parent domain. - if host == originalHost { - return findMatchingManagedZone(originalHost, parentDomain, zones) - } - - zone, ok := utils.Find(zones, func(zone kuadrantdnsv1alpha1.ManagedZone) bool { - return strings.ToLower(zone.Spec.DomainName) == host - }) - - if ok { - subdomain := strings.Replace(strings.ToLower(originalHost), "."+strings.ToLower(zone.Spec.DomainName), "", 1) - return zone, subdomain, nil - } - return findMatchingManagedZone(originalHost, parentDomain, zones) -} - func commonDNSRecordLabels(gwKey client.ObjectKey, p *v1alpha1.DNSPolicy) map[string]string { commonLabels := map[string]string{} for k, v := range policyDNSRecordLabels(p) { @@ -310,17 +269,6 @@ func (dh *dnsHelper) removeDNSForDeletedListeners(ctx context.Context, upstreamG return nil } -func (dh *dnsHelper) getManagedZoneForListener(ctx context.Context, ns string, listener gatewayapiv1.Listener) (*kuadrantdnsv1alpha1.ManagedZone, error) { - var managedZones kuadrantdnsv1alpha1.ManagedZoneList - if err := dh.List(ctx, &managedZones, client.InNamespace(ns)); err != nil { - log.FromContext(ctx).Error(err, "unable to list managed zones for gateway ", "in ns", ns) - return nil, err - } - host := string(*listener.Hostname) - mz, _, err := findMatchingManagedZone(host, host, managedZones.Items) - return mz, err -} - func dnsRecordName(gatewayName, listenerName string) string { return fmt.Sprintf("%s-%s", gatewayName, listenerName) } diff --git a/controllers/dnspolicy_controller.go b/controllers/dnspolicy_controller.go index b7511aec2..4147276d1 100644 --- a/controllers/dnspolicy_controller.go +++ b/controllers/dnspolicy_controller.go @@ -59,8 +59,6 @@ type DNSPolicyReconciler struct { //+kubebuilder:rbac:groups=kuadrant.io,resources=dnsrecords,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=kuadrant.io,resources=dnsrecords/status,verbs=get -//+kubebuilder:rbac:groups=kuadrant.io,resources=managedzones,verbs=get;list;watch - func (r *DNSPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := r.Logger().WithValues("DNSPolicy", req.NamespacedName) log.Info("Reconciling DNSPolicy") diff --git a/controllers/dnspolicy_dnsrecords.go b/controllers/dnspolicy_dnsrecords.go index b993c8fbf..998568879 100644 --- a/controllers/dnspolicy_dnsrecords.go +++ b/controllers/dnspolicy_dnsrecords.go @@ -62,10 +62,6 @@ func (r *DNSPolicyReconciler) reconcileGatewayDNSRecords(ctx context.Context, gw log.V(3).Info("checking gateway for attached routes ", "gateway", gatewayWrapper.Name, "clusterGateways", clusterGateways) for _, listener := range gatewayWrapper.Spec.Listeners { - var mz, err = r.dnsHelper.getManagedZoneForListener(ctx, gatewayWrapper.Namespace, listener) - if err != nil { - return err - } listenerHost := *listener.Hostname if listenerHost == "" { log.Info("skipping listener no hostname assigned", listener.Name, "in ns ", gatewayWrapper.Namespace) @@ -92,7 +88,7 @@ func (r *DNSPolicyReconciler) reconcileGatewayDNSRecords(ctx context.Context, gw continue } - dnsRecord, err := r.desiredDNSRecord(gatewayWrapper, dnsPolicy, listener, listenerGateways, mz) + dnsRecord, err := r.desiredDNSRecord(gatewayWrapper, dnsPolicy, listener, listenerGateways) if err != nil { return err } @@ -111,7 +107,7 @@ func (r *DNSPolicyReconciler) reconcileGatewayDNSRecords(ctx context.Context, gw return nil } -func (r *DNSPolicyReconciler) desiredDNSRecord(gateway *multicluster.GatewayWrapper, dnsPolicy *v1alpha1.DNSPolicy, targetListener gatewayapiv1.Listener, clusterGateways []multicluster.ClusterGateway, managedZone *kuadrantdnsv1alpha1.ManagedZone) (*kuadrantdnsv1alpha1.DNSRecord, error) { +func (r *DNSPolicyReconciler) desiredDNSRecord(gateway *multicluster.GatewayWrapper, dnsPolicy *v1alpha1.DNSPolicy, targetListener gatewayapiv1.Listener, clusterGateways []multicluster.ClusterGateway) (*kuadrantdnsv1alpha1.DNSRecord, error) { rootHost := string(*targetListener.Hostname) var healthCheckSpec *kuadrantdnsv1alpha1.HealthCheckSpec @@ -126,13 +122,14 @@ func (r *DNSPolicyReconciler) desiredDNSRecord(gateway *multicluster.GatewayWrap dnsRecord := &kuadrantdnsv1alpha1.DNSRecord{ ObjectMeta: metav1.ObjectMeta{ Name: dnsRecordName(gateway.Name, string(targetListener.Name)), - Namespace: managedZone.Namespace, + Namespace: dnsPolicy.Namespace, Labels: commonDNSRecordLabels(client.ObjectKeyFromObject(gateway), dnsPolicy), }, Spec: kuadrantdnsv1alpha1.DNSRecordSpec{ RootHost: rootHost, - ManagedZoneRef: &kuadrantdnsv1alpha1.ManagedZoneReference{ - Name: managedZone.Name, + ProviderRef: kuadrantdnsv1alpha1.ProviderRef{ + // Currently we only allow a single providerRef to be added. When that changes, we will need to update this to deal with multiple records. + Name: dnsPolicy.Spec.ProviderRefs[0].Name, }, HealthCheck: healthCheckSpec, }, diff --git a/doc/dns.md b/doc/dns.md index 3ec867f27..1b33a60e3 100644 --- a/doc/dns.md +++ b/doc/dns.md @@ -35,7 +35,12 @@ spec: group: gateway.networking.k8s.io kind: Gateway name: mygateway - + + # reference to an existing secret resource containing provider credentials and configuration + # it can only refer to Secrets in the same namespace as the DNSPolicy that have the type kuadrant.io/(provider) e.g kuadrant.io/aws + providerRefs: + - name: my-aws-credentials + # (optional) routing strategy to use when creating DNS records, defaults to `loadbalanced` # determines what DNS records are created in the DNS provider # check out Kuadrant RFC 0005 https://github.com/Kuadrant/architecture/blob/main/rfcs/0005-single-cluster-dnspolicy.md to learn more about the Routing Strategy field @@ -82,11 +87,11 @@ Check out the [API reference](reference/dnspolicy.md) for a full specification o ## Using the DNSPolicy -### DNS Provider and ManagedZone Setup +### DNS Provider Setup A DNSPolicy acts against a target Gateway by processing its listeners for hostnames that it can create dns records for. -In order for it to do this, it must know about dns providers, and what domains these dns providers are currently hosting. -This is done through the creation of ManagedZones and dns provider secrets containing the credentials for the dns provider account. +In order for it to do this, it must know about the dns provider. +This is done through the creation of dns provider secrets containing the credentials and configuration for the dns provider account. If for example a Gateway is created with a listener with a hostname of `echo.apps.hcpapps.net`: ```yaml @@ -105,28 +110,14 @@ spec: protocol: HTTP ``` -In order for the DNSPolicy to act upon that listener, a ManagedZone must exist for that hostnames' domain. - -```yaml -apiVersion: kuadrant.io/v1alpha1 -kind: ManagedZone -metadata: - name: apps.hcpapps.net -spec: - domainName: apps.hcpapps.net - description: "apps.hcpapps.net managed domain" - dnsProviderSecretRef: - name: my-aws-credentials -``` - -The managed zone references a secret containing the external DNS provider services credentials. +In order for the DNSPolicy to act upon that listener, a DNS provider Secret must exist for that hostnames' domain. ```yaml apiVersion: v1 kind: Secret metadata: name: my-aws-credentials - namespace: + namespace: data: AWS_ACCESS_KEY_ID: AWS_REGION: @@ -134,9 +125,58 @@ data: type: kuadrant.io/aws ``` +By default, Kuadrant will list the available zones and find the matching zone based on the listener host in the gateway listener. If it finds more than one matching zone for a given listener host, it will not update any of those zones. +When providing a credential you should limit that credential down to just have write access to the zones you want Kuadrant to manage. Below is an example of a an AWS policy for doing this type of thing: + +``` +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "VisualEditor0", + "Effect": "Allow", + "Action": [ + "route53:ListTagsForResources", + "route53:GetHealthCheckLastFailureReason", + "route53:GetHealthCheckStatus", + "route53:GetChange", + "route53:GetHostedZone", + "route53:ChangeResourceRecordSets", + "route53:ListResourceRecordSets", + "route53:GetHealthCheck", + "route53:UpdateHostedZoneComment", + "route53:UpdateHealthCheck", + "route53:CreateHealthCheck", + "route53:DeleteHealthCheck", + "route53:ListTagsForResource", + "route53:ListHealthChecks", + "route53:GetGeoLocation", + "route53:ListGeoLocations", + "route53:ListHostedZonesByName", + "route53:GetHealthCheckCount" + ], + "Resource": [ + "arn:aws:route53:::hostedzone/Z08187901Y93585DDGM6K", + "arn:aws:route53:::healthcheck/*", + "arn:aws:route53:::change/*" + ] + }, + { + "Sid": "VisualEditor1", + "Effect": "Allow", + "Action": [ + "route53:ListHostedZones" + ], + "Resource": "*" + } + ] +} +``` + + ### Targeting a Gateway networking resource -When a DNSPolicy targets a Gateway, the policy will be enforced on all gateway listeners that have a matching ManagedZone. +When a DNSPolicy targets a Gateway, the policy will be enforced on all gateway listeners. Target a Gateway by setting the `spec.targetRef` field of the DNSPolicy as follows: @@ -154,7 +194,7 @@ spec: ### DNSRecord Resource -The DNSPolicy will create a DNSRecord resource for each listener hostname with a suitable ManagedZone configured. The DNSPolicy resource uses the status of the Gateway to determine what dns records need to be created based on the clusters it has been placed onto. +The DNSPolicy will create a DNSRecord resource for each listener hostname. The DNSPolicy resource uses the status of the Gateway to determine what dns records need to be created based on the clusters it has been placed onto. Given the following multi cluster gateway status: ```yaml @@ -228,8 +268,8 @@ spec: recordType: A targets: - 172.31.201.1 - managedZone: - name: apps.hcpapps.net + providerRefs: + - name: my-aws-credentials ``` After DNSRecord reconciliation the listener hostname should be resolvable through dns: @@ -257,8 +297,8 @@ spec: targets: - 172.31.201.1 - 172.31.202.1 - managedZone: - name: apps.hcpapps.net + providerRefs: + - name: my-aws-credentials ``` After DNSRecord reconciliation the listener hostname should be resolvable through dns: @@ -268,13 +308,6 @@ dig echo.apps.hcpapps.net +short 172.31.201.1 ``` -### Examples - -Check out the following user guides for examples of using the Kuadrant DNSPolicy: - -[//]: # (ToDo mnairn) -[//]: # (* [Multicluster LoadBalanced DNSPolicy](../how-to/multicluster-loadbalanced-dnspolicy.md)) - ### Known limitations * One Gateway can only be targeted by one DNSPolicy. diff --git a/doc/reference/dnspolicy.md b/doc/reference/dnspolicy.md index f778bd28f..87902b856 100644 --- a/doc/reference/dnspolicy.md +++ b/doc/reference/dnspolicy.md @@ -2,6 +2,7 @@ - [DNSPolicy](#DNSPolicy) - [DNSPolicySpec](#dnspolicyspec) + - [ProviderRefs](#providerRefs) - [HealthCheckSpec](#healthcheckspec) - [LoadBalancingSpec](#loadbalancingspec) - [LoadBalancingWeighted](#loadbalancingweighted) @@ -25,6 +26,20 @@ | `healthCheck` | [HealthCheckSpec](#healthcheckspec) | No | HealthCheck spec | | `loadBalancing` | [LoadBalancingSpec](#loadbalancingspec) | Yes(loadbalanced only) | LoadBalancing Spec, required when routingStrategy is "loadbalanced" | | `routingStrategy` | String (immutable) | Yes | **Immutable!** Routing Strategy to use, one of "simple" or "loadbalanced" | +| `providerRefs` | [ProviderRefs](#providerrefs) | Yes | array of references to providers. (currently limited to max 1) | + +## ProviderRefs + +| **Field** | **Type** | **Required** | **Description** | +|--------------------|-----------------------------------|:------------:|-----------------------------------------------------------------------------------------------------------| +| `providerRefs` | [][ProviderRef](#providerref) | Yes | max 1 reference. This is an array of providerRef that points to a local secret(s) that contains the required provider auth values + +## ProviderRef + +| **Field** | **Type** | **Required** | **Description** | +|------------|----------|:------------:|----------------------------------------------------------------------------------------| +| `name` | String | Yes | Name of the secret in the same namespace that contains the provider credentials + ## HealthCheckSpec diff --git a/doc/user-guides/gateway-dns.md b/doc/user-guides/gateway-dns.md index 63677ac64..211366a4f 100644 --- a/doc/user-guides/gateway-dns.md +++ b/doc/user-guides/gateway-dns.md @@ -34,14 +34,15 @@ kubectl create namespace my-gateways Export a root domain and hosted zone id: ```shell export ROOT_DOMAIN= -export AWS_HOSTED_ZONE_ID= ``` -> **Note:** ROOT_DOMAIN and AWS_HOSTED_ZONE_ID should be set to your AWS hosted zone *name* and *id* respectively. +> **Note:** ROOT_DOMAIN should be set to your AWS hosted zone *name*. + +### Create a dns provider secret + +Create AWS provider secret. You should limit the permissions of this credential to only the zones you want us to access. -### Create a ManagedZone -Create AWS credentials secret ```shell export AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= @@ -51,27 +52,6 @@ kubectl -n my-gateways create secret generic aws-credentials \ --from-literal=AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY ``` -Create a ManagedZone -```sh -kubectl -n my-gateways apply -f - < **Note:** You may need to create a ManagedZone resource depending on if one was created during your initial cluster setup or not. You should have a `aws-credentials` Secret already created in the `kuadrant-system` namespace as well. However, if either of these don't exist, you can follow these commands to create them: +> **Note:** You may need to create a DNS Provider Secret resource depending on if one was created during your initial cluster setup or not. You should have an `aws-credentials` Secret already created in the `kuadrant-system` namespace. However, if it doesn't exist, you can follow these commands to create one: ```sh export AWS_ACCESS_KEY_ID=xxxxxxx # Key ID from AWS with Route 53 access @@ -282,24 +282,6 @@ kubectl -n kuadrant-system create secret generic aws-credentials \ --from-literal=AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY ``` -```sh -export AWS_DNS_PUBLIC_ZONE_ID=xxxxxx # DNS Zone ID in AWS Route 53 - -kubectl apply -f - <&1 } | grep -v "Warning: .* deprecated" || true -info "Kuadrant installation applied, configuring ManagedZone if DNS provider is set..." +info "Kuadrant installation applied, configuring DNS provider if set..." if [ ! -z "$DNS_PROVIDER" ]; then postSetup ${KUADRANT_CLUSTER_NAME} ${KUADRANT_NAMESPACE} fi diff --git a/tests/common/dnspolicy/dnspolicy_controller_multi_cluster_test.go b/tests/common/dnspolicy/dnspolicy_controller_multi_cluster_test.go index 413793b34..0ec7b8f67 100644 --- a/tests/common/dnspolicy/dnspolicy_controller_multi_cluster_test.go +++ b/tests/common/dnspolicy/dnspolicy_controller_multi_cluster_test.go @@ -10,7 +10,6 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gstruct" - k8serrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/util/rand" externaldns "sigs.k8s.io/external-dns/endpoint" @@ -37,7 +36,6 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { var gatewayClass *gatewayapiv1.GatewayClass var dnsProviderSecret *corev1.Secret - var managedZone *kuadrantdnsv1alpha1.ManagedZone var testNamespace string var gateway *gatewayapiv1.Gateway var dnsPolicy *v1alpha1.DNSPolicy @@ -53,21 +51,8 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { gatewayClass = tests.BuildGatewayClass("gwc-"+testNamespace, "default", "kuadrant.io/bar") Expect(k8sClient.Create(ctx, gatewayClass)).To(Succeed()) - dnsProviderSecret = tests.BuildInMemoryCredentialsSecret("inmemory-credentials", testNamespace) - managedZone = tests.BuildManagedZone("mz-example-com", testNamespace, domain, dnsProviderSecret.Name) + dnsProviderSecret = tests.BuildInMemoryCredentialsSecret("inmemory-credentials", testNamespace, domain) Expect(k8sClient.Create(ctx, dnsProviderSecret)).To(Succeed()) - Expect(k8sClient.Create(ctx, managedZone)).To(Succeed()) - Eventually(func(g Gomega) { - err := k8sClient.Get(ctx, client.ObjectKeyFromObject(managedZone), managedZone) - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(managedZone.Status.Conditions).To( - ContainElement(MatchFields(IgnoreExtras, Fields{ - "Type": Equal(string(kuadrantdnsv1alpha1.ConditionTypeReady)), - "Status": Equal(metav1.ConditionTrue), - "ObservedGeneration": Equal(managedZone.Generation), - })), - ) - }, tests.TimeoutMedium, time.Second).Should(Succeed()) gateway = tests.NewGatewayBuilder(tests.GatewayName, gatewayClass.Name, testNamespace). WithHTTPListener(tests.ListenerNameOne, tests.HostOne(domain)). @@ -134,7 +119,7 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { if dnsPolicy != nil { err := k8sClient.Delete(ctx, dnsPolicy) Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) - // Wait until dns records are finished deleting since it can't finish deleting without managed zone + // Wait until dns records are finished deleting since it can't finish deleting without the DNS provider secret Eventually(func(g Gomega) { dnsRecords := &kuadrantdnsv1alpha1.DNSRecordList{} err := k8sClient.List(ctx, dnsRecords, client.InNamespace(testNamespace)) @@ -142,15 +127,6 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { g.Expect(dnsRecords.Items).To(HaveLen(0)) }).WithContext(ctx).Should(Succeed()) } - if managedZone != nil { - err := k8sClient.Delete(ctx, managedZone) - Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) - // Wait until managed zone is delete before deleting the provider secret - Eventually(func(g Gomega) { - err := k8sClient.Get(ctx, client.ObjectKeyFromObject(managedZone), managedZone) - g.Expect(k8serrors.IsNotFound(err)).To(BeTrue()) - }).WithContext(ctx).Should(Succeed()) - } if dnsProviderSecret != nil { err := k8sClient.Delete(ctx, dnsProviderSecret) Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) @@ -166,6 +142,7 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { BeforeEach(func(ctx SpecContext) { dnsPolicy = v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). WithTargetGateway(tests.GatewayName). WithRoutingStrategy(v1alpha1.SimpleRoutingStrategy) Expect(k8sClient.Create(ctx, dnsPolicy)).To(Succeed()) @@ -187,7 +164,7 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { g.Expect(err).NotTo(HaveOccurred()) g.Expect(dnsRecord.Name).To(Equal(recordName)) - g.Expect(dnsRecord.Spec.ManagedZoneRef.Name).To(Equal("mz-example-com")) + g.Expect(dnsRecord.Spec.ProviderRef.Name).To(Equal(dnsProviderSecret.Name)) g.Expect(dnsRecord.Spec.Endpoints).To(ConsistOf( PointTo(MatchFields(IgnoreExtras, Fields{ "DNSName": Equal(tests.HostOne(domain)), @@ -202,7 +179,7 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { g.Expect(tests.EndpointsTraversable(dnsRecord.Spec.Endpoints, tests.HostOne(domain), []string{tests.IPAddressOne, tests.IPAddressTwo})).To(BeTrue()) g.Expect(wildcardDnsRecord.Name).To(Equal(wildcardRecordName)) - g.Expect(wildcardDnsRecord.Spec.ManagedZoneRef.Name).To(Equal("mz-example-com")) + g.Expect(wildcardDnsRecord.Spec.ProviderRef.Name).To(Equal(dnsProviderSecret.Name)) g.Expect(wildcardDnsRecord.Spec.Endpoints).To(ConsistOf( PointTo(MatchFields(IgnoreExtras, Fields{ "DNSName": Equal(tests.HostWildcard(domain)), @@ -226,6 +203,7 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { BeforeEach(func(ctx SpecContext) { dnsPolicy = v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). WithTargetGateway(tests.GatewayName). WithRoutingStrategy(v1alpha1.LoadBalancedRoutingStrategy). WithLoadBalancingFor(120, nil, "IE") @@ -248,7 +226,7 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { g.Expect(err).NotTo(HaveOccurred()) g.Expect(dnsRecord.Name).To(Equal(recordName)) - g.Expect(dnsRecord.Spec.ManagedZoneRef.Name).To(Equal("mz-example-com")) + g.Expect(dnsRecord.Spec.ProviderRef.Name).To(Equal(dnsProviderSecret.Name)) g.Expect(dnsRecord.Spec.Endpoints).To(ConsistOf( PointTo(MatchFields(IgnoreExtras, Fields{ "DNSName": Equal(clusterOneIDHash + "-" + gwHash + ".klb.test." + domain), @@ -309,7 +287,7 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { g.Expect(tests.EndpointsTraversable(dnsRecord.Spec.Endpoints, tests.HostOne(domain), []string{tests.IPAddressOne, tests.IPAddressTwo})).To(BeTrue()) g.Expect(wildcardDnsRecord.Name).To(Equal(wildcardRecordName)) - g.Expect(wildcardDnsRecord.Spec.ManagedZoneRef.Name).To(Equal("mz-example-com")) + g.Expect(wildcardDnsRecord.Spec.ProviderRef.Name).To(Equal(dnsProviderSecret.Name)) g.Expect(wildcardDnsRecord.Spec.Endpoints).To(ConsistOf( PointTo(MatchFields(IgnoreExtras, Fields{ "DNSName": Equal(clusterOneIDHash + "-" + gwHash + ".klb." + domain), @@ -377,6 +355,7 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { BeforeEach(func(ctx SpecContext) { dnsPolicy = v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). WithTargetGateway(tests.GatewayName). WithRoutingStrategy(v1alpha1.LoadBalancedRoutingStrategy). WithLoadBalancingFor(120, []*v1alpha1.CustomWeight{ @@ -430,7 +409,7 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { g.Expect(err).NotTo(HaveOccurred()) g.Expect(dnsRecord.Name).To(Equal(recordName)) - g.Expect(dnsRecord.Spec.ManagedZoneRef.Name).To(Equal("mz-example-com")) + g.Expect(dnsRecord.Spec.ProviderRef.Name).To(Equal(dnsProviderSecret.Name)) g.Expect(dnsRecord.Spec.Endpoints).To(ConsistOf( PointTo(MatchFields(IgnoreExtras, Fields{ "DNSName": Equal(clusterTwoIDHash + "-" + gwHash + ".klb.test." + domain), @@ -499,7 +478,7 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { g.Expect(tests.EndpointsTraversable(dnsRecord.Spec.Endpoints, tests.HostOne(domain), []string{tests.IPAddressOne, tests.IPAddressTwo})).To(BeTrue()) g.Expect(wildcardDnsRecord.Name).To(Equal(wildcardRecordName)) - g.Expect(wildcardDnsRecord.Spec.ManagedZoneRef.Name).To(Equal("mz-example-com")) + g.Expect(wildcardDnsRecord.Spec.ProviderRef.Name).To(Equal(dnsProviderSecret.Name)) g.Expect(wildcardDnsRecord.Spec.Endpoints).To(ConsistOf( PointTo(MatchFields(IgnoreExtras, Fields{ "DNSName": Equal(clusterTwoIDHash + "-" + gwHash + ".klb." + domain), @@ -575,6 +554,7 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { BeforeEach(func(ctx SpecContext) { dnsPolicy = v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). WithTargetGateway(tests.GatewayName). WithRoutingStrategy(v1alpha1.LoadBalancedRoutingStrategy). WithLoadBalancingFor(120, nil, "cat") @@ -607,7 +587,7 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { g.Expect(err).NotTo(HaveOccurred()) g.Expect(dnsRecord.Name).To(Equal(recordName)) - g.Expect(dnsRecord.Spec.ManagedZoneRef.Name).To(Equal("mz-example-com")) + g.Expect(dnsRecord.Spec.ProviderRef.Name).To(Equal(dnsProviderSecret.Name)) g.Expect(dnsRecord.Spec.Endpoints).To(ConsistOf( PointTo(MatchFields(IgnoreExtras, Fields{ "DNSName": Equal(clusterOneIDHash + "-" + gwHash + ".klb.test." + domain), @@ -668,7 +648,7 @@ var _ = Describe("DNSPolicy Multi Cluster", func() { g.Expect(tests.EndpointsTraversable(dnsRecord.Spec.Endpoints, tests.HostOne(domain), []string{tests.IPAddressOne, tests.IPAddressTwo})).To(BeTrue()) g.Expect(wildcardDnsRecord.Name).To(Equal(wildcardRecordName)) - g.Expect(wildcardDnsRecord.Spec.ManagedZoneRef.Name).To(Equal("mz-example-com")) + g.Expect(wildcardDnsRecord.Spec.ProviderRef.Name).To(Equal(dnsProviderSecret.Name)) g.Expect(wildcardDnsRecord.Spec.Endpoints).To(ConsistOf( PointTo(MatchFields(IgnoreExtras, Fields{ "DNSName": Equal(clusterOneIDHash + "-" + gwHash + ".klb." + domain), diff --git a/tests/common/dnspolicy/dnspolicy_controller_single_cluster_test.go b/tests/common/dnspolicy/dnspolicy_controller_single_cluster_test.go index 14c7aae1a..dc37fa163 100644 --- a/tests/common/dnspolicy/dnspolicy_controller_single_cluster_test.go +++ b/tests/common/dnspolicy/dnspolicy_controller_single_cluster_test.go @@ -10,7 +10,6 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gstruct" - k8serrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/util/rand" externaldns "sigs.k8s.io/external-dns/endpoint" @@ -36,7 +35,6 @@ var _ = Describe("DNSPolicy Single Cluster", func() { var gatewayClass *gatewayapiv1.GatewayClass var dnsProviderSecret *corev1.Secret - var managedZone *kuadrantdnsv1alpha1.ManagedZone var testNamespace string var gateway *gatewayapiv1.Gateway var dnsPolicy *v1alpha1.DNSPolicy @@ -53,21 +51,8 @@ var _ = Describe("DNSPolicy Single Cluster", func() { gatewayClass = tests.BuildGatewayClass("gwc-"+testNamespace, "default", "kuadrant.io/bar") Expect(k8sClient.Create(ctx, gatewayClass)).To(Succeed()) - dnsProviderSecret = tests.BuildInMemoryCredentialsSecret("inmemory-credentials", testNamespace) - managedZone = tests.BuildManagedZone("mz-example-com", testNamespace, domain, dnsProviderSecret.Name) + dnsProviderSecret = tests.BuildInMemoryCredentialsSecret("inmemory-credentials", testNamespace, domain) Expect(k8sClient.Create(ctx, dnsProviderSecret)).To(Succeed()) - Expect(k8sClient.Create(ctx, managedZone)).To(Succeed()) - Eventually(func(g Gomega) { - err := k8sClient.Get(ctx, client.ObjectKeyFromObject(managedZone), managedZone) - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(managedZone.Status.Conditions).To( - ContainElement(MatchFields(IgnoreExtras, Fields{ - "Type": Equal(string(kuadrantdnsv1alpha1.ConditionTypeReady)), - "Status": Equal(metav1.ConditionTrue), - "ObservedGeneration": Equal(managedZone.Generation), - })), - ) - }, tests.TimeoutMedium, time.Second).Should(Succeed()) gateway = tests.NewGatewayBuilder(tests.GatewayName, gatewayClass.Name, testNamespace). WithHTTPListener("foo", fmt.Sprintf("foo.%s", domain)). @@ -130,7 +115,7 @@ var _ = Describe("DNSPolicy Single Cluster", func() { if dnsPolicy != nil { err := k8sClient.Delete(ctx, dnsPolicy) Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) - // Wait until dns records are finished deleting since it can't finish deleting without managed zone + // Wait until dns records are finished deleting since it can't finish deleting without the DNS provider secret Eventually(func(g Gomega) { dnsRecords := &kuadrantdnsv1alpha1.DNSRecordList{} err := k8sClient.List(ctx, dnsRecords, client.InNamespace(testNamespace)) @@ -139,15 +124,6 @@ var _ = Describe("DNSPolicy Single Cluster", func() { }).WithContext(ctx).Should(Succeed()) } - if managedZone != nil { - err := k8sClient.Delete(ctx, managedZone) - Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) - // Wait until managed zone is delete before deleting the provider secret - Eventually(func(g Gomega) { - err := k8sClient.Get(ctx, client.ObjectKeyFromObject(managedZone), managedZone) - g.Expect(k8serrors.IsNotFound(err)).To(BeTrue()) - }).WithContext(ctx).Should(Succeed()) - } if dnsProviderSecret != nil { err := k8sClient.Delete(ctx, dnsProviderSecret) Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) @@ -163,6 +139,7 @@ var _ = Describe("DNSPolicy Single Cluster", func() { BeforeEach(func(ctx SpecContext) { dnsPolicy = v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). WithTargetGateway(tests.GatewayName). WithRoutingStrategy(v1alpha1.SimpleRoutingStrategy) Expect(k8sClient.Create(ctx, dnsPolicy)).To(Succeed()) @@ -184,7 +161,7 @@ var _ = Describe("DNSPolicy Single Cluster", func() { g.Expect(err).NotTo(HaveOccurred()) g.Expect(dnsRecord.Name).To(Equal(recordName)) - g.Expect(dnsRecord.Spec.ManagedZoneRef.Name).To(Equal("mz-example-com")) + g.Expect(dnsRecord.Spec.ProviderRef.Name).To(Equal(dnsProviderSecret.Name)) g.Expect(dnsRecord.Spec.Endpoints).To(ConsistOf( PointTo(MatchFields(IgnoreExtras, Fields{ "DNSName": Equal(tests.HostOne(domain)), @@ -199,7 +176,7 @@ var _ = Describe("DNSPolicy Single Cluster", func() { g.Expect(tests.EndpointsTraversable(dnsRecord.Spec.Endpoints, tests.HostOne(domain), []string{tests.IPAddressOne, tests.IPAddressTwo})).To(BeTrue()) g.Expect(wildcardDnsRecord.Name).To(Equal(wildcardRecordName)) - g.Expect(wildcardDnsRecord.Spec.ManagedZoneRef.Name).To(Equal("mz-example-com")) + g.Expect(wildcardDnsRecord.Spec.ProviderRef.Name).To(Equal(dnsProviderSecret.Name)) g.Expect(wildcardDnsRecord.Spec.Endpoints).To(ConsistOf( PointTo(MatchFields(IgnoreExtras, Fields{ "DNSName": Equal(tests.HostWildcard(domain)), @@ -221,6 +198,7 @@ var _ = Describe("DNSPolicy Single Cluster", func() { BeforeEach(func(ctx SpecContext) { dnsPolicy = v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). WithTargetGateway(tests.GatewayName). WithRoutingStrategy(v1alpha1.LoadBalancedRoutingStrategy). WithLoadBalancingFor(120, nil, "IE") @@ -243,7 +221,7 @@ var _ = Describe("DNSPolicy Single Cluster", func() { g.Expect(err).NotTo(HaveOccurred()) g.Expect(dnsRecord.Name).To(Equal(recordName)) - g.Expect(dnsRecord.Spec.ManagedZoneRef.Name).To(Equal("mz-example-com")) + g.Expect(dnsRecord.Spec.ProviderRef.Name).To(Equal(dnsProviderSecret.Name)) g.Expect(dnsRecord.Spec.Endpoints).To(ConsistOf( PointTo(MatchFields(IgnoreExtras, Fields{ "DNSName": Equal(clusterHash + "-" + gwHash + "." + "klb.test." + domain), @@ -289,7 +267,7 @@ var _ = Describe("DNSPolicy Single Cluster", func() { g.Expect(tests.EndpointsTraversable(dnsRecord.Spec.Endpoints, tests.HostOne(domain), []string{tests.IPAddressOne, tests.IPAddressTwo})).To(BeTrue()) g.Expect(wildcardDnsRecord.Name).To(Equal(wildcardRecordName)) - g.Expect(wildcardDnsRecord.Spec.ManagedZoneRef.Name).To(Equal("mz-example-com")) + g.Expect(wildcardDnsRecord.Spec.ProviderRef.Name).To(Equal(dnsProviderSecret.Name)) g.Expect(wildcardDnsRecord.Spec.Endpoints).To(ContainElements( PointTo(MatchFields(IgnoreExtras, Fields{ "DNSName": Equal(clusterHash + "-" + gwHash + "." + "klb." + domain), diff --git a/tests/common/dnspolicy/dnspolicy_controller_test.go b/tests/common/dnspolicy/dnspolicy_controller_test.go index 5098a0cc4..bf52778cc 100644 --- a/tests/common/dnspolicy/dnspolicy_controller_test.go +++ b/tests/common/dnspolicy/dnspolicy_controller_test.go @@ -36,7 +36,6 @@ var _ = Describe("DNSPolicy controller", func() { var gatewayClass *gatewayapiv1.GatewayClass var dnsProviderSecret *corev1.Secret - var managedZone *kuadrantdnsv1alpha1.ManagedZone var testNamespace string var gateway *gatewayapiv1.Gateway var dnsPolicy *v1alpha1.DNSPolicy @@ -49,22 +48,8 @@ var _ = Describe("DNSPolicy controller", func() { gatewayClass = tests.BuildGatewayClass("gwc-"+testNamespace, "default", "kuadrant.io/bar") Expect(k8sClient.Create(ctx, gatewayClass)).To(Succeed()) - dnsProviderSecret = tests.BuildInMemoryCredentialsSecret("inmemory-credentials", testNamespace) - managedZone = tests.BuildManagedZone("mz-example-com", testNamespace, domain, dnsProviderSecret.Name) + dnsProviderSecret = tests.BuildInMemoryCredentialsSecret("inmemory-credentials", testNamespace, domain) Expect(k8sClient.Create(ctx, dnsProviderSecret)).To(Succeed()) - Expect(k8sClient.Create(ctx, managedZone)).To(Succeed()) - - Eventually(func(g Gomega) { - err := k8sClient.Get(ctx, client.ObjectKeyFromObject(managedZone), managedZone) - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(managedZone.Status.Conditions).To( - ContainElement(MatchFields(IgnoreExtras, Fields{ - "Type": Equal(string(kuadrantdnsv1alpha1.ConditionTypeReady)), - "Status": Equal(metav1.ConditionTrue), - "ObservedGeneration": Equal(managedZone.Generation), - })), - ) - }, tests.TimeoutMedium, time.Second).Should(Succeed()) }) AfterEach(func(ctx SpecContext) { @@ -75,7 +60,7 @@ var _ = Describe("DNSPolicy controller", func() { if dnsPolicy != nil { err := k8sClient.Delete(ctx, dnsPolicy) Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) - // Wait until dns records are finished deleting since it can't finish deleting without managed zone + // Wait until dns records are finished deleting since it can't finish deleting without the DNS provider secret Eventually(func(g Gomega) { dnsRecords := &kuadrantdnsv1alpha1.DNSRecordList{} err := k8sClient.List(ctx, dnsRecords, client.InNamespace(testNamespace)) @@ -83,15 +68,6 @@ var _ = Describe("DNSPolicy controller", func() { g.Expect(dnsRecords.Items).To(HaveLen(0)) }).WithContext(ctx).Should(Succeed()) } - if managedZone != nil { - err := k8sClient.Delete(ctx, managedZone) - Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) - // Wait until managed zone is delete before deleting the provider secret - Eventually(func(g Gomega) { - err := k8sClient.Get(ctx, client.ObjectKeyFromObject(managedZone), managedZone) - g.Expect(k8serrors.IsNotFound(err)).To(BeTrue()) - }).WithContext(ctx).Should(Succeed()) - } if dnsProviderSecret != nil { err := k8sClient.Delete(ctx, dnsProviderSecret) Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred()) @@ -110,6 +86,7 @@ var _ = Describe("DNSPolicy controller", func() { // simple should succeed dnsPolicy = v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). WithTargetGateway("test-gateway"). WithRoutingStrategy(v1alpha1.SimpleRoutingStrategy) Expect(k8sClient.Create(ctx, dnsPolicy)).To(Succeed()) @@ -127,18 +104,51 @@ var _ = Describe("DNSPolicy controller", func() { // loadbalanced missing loadbalancing field dnsPolicy = v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). WithTargetGateway("test-gateway"). WithRoutingStrategy(v1alpha1.LoadBalancedRoutingStrategy) Expect(k8sClient.Create(ctx, dnsPolicy)).To(MatchError(ContainSubstring("spec.loadBalancing is a required field when spec.routingStrategy == 'loadbalanced'"))) // loadbalanced should succeed dnsPolicy = v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). WithTargetGateway("test-gateway"). WithRoutingStrategy(v1alpha1.LoadBalancedRoutingStrategy). WithLoadBalancingFor(100, nil, "foo") Expect(k8sClient.Create(ctx, dnsPolicy)).To(Succeed()) }, testTimeOut) + It("should validate provider ref field correctly", func(ctx SpecContext) { + + gateway = tests.NewGatewayBuilder("test-gateway", gatewayClass.Name, testNamespace). + WithHTTPListener(tests.ListenerNameOne, tests.HostTwo(domain)).Gateway + + // should not allow an empty providerRef list + dnsPolicy = v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithTargetGateway("test-gateway"). + WithRoutingStrategy(v1alpha1.SimpleRoutingStrategy) + Expect(k8sClient.Create(ctx, dnsPolicy)).To(MatchError(ContainSubstring("spec.providerRefs: Required value"))) + + // should create with a single providerRef + dnsPolicy = v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). + WithTargetGateway("test-gateway"). + WithRoutingStrategy(v1alpha1.SimpleRoutingStrategy) + Expect(k8sClient.Create(ctx, dnsPolicy)).To(Succeed()) + + // should not allow adding another providerRef + Eventually(func(g Gomega) { + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsPolicy), dnsPolicy) + g.Expect(err).NotTo(HaveOccurred()) + dnsPolicy.Spec.ProviderRefs = append(dnsPolicy.Spec.ProviderRefs, kuadrantdnsv1alpha1.ProviderRef{ + Name: "some-other-provider-secret", + }) + err = k8sClient.Update(ctx, dnsPolicy) + g.Expect(err).To(HaveOccurred()) + g.Expect(err).To(MatchError(ContainSubstring("spec.providerRefs: Too many: 2: must have at most 1 items"))) + }, tests.TimeoutMedium, time.Second).Should(Succeed()) + }, testTimeOut) + It("should conflict DNS Policies of different strategy on the same host", func(ctx SpecContext) { // setting up two gateways that have the same host @@ -189,6 +199,7 @@ var _ = Describe("DNSPolicy controller", func() { // Create policy1 targeting gateway1 with simple routing strategy dnsPolicy1 := v1alpha1.NewDNSPolicy("test-dns-policy1", testNamespace). + WithProviderSecret(*dnsProviderSecret). WithTargetGateway("test-gateway1"). WithRoutingStrategy(v1alpha1.SimpleRoutingStrategy) Expect(k8sClient.Create(ctx, dnsPolicy1)).To(Succeed()) @@ -237,6 +248,7 @@ var _ = Describe("DNSPolicy controller", func() { // create policy2 targeting gateway2 with the load-balanced strategy dnsPolicy2 := v1alpha1.NewDNSPolicy("test-dns-policy2", testNamespace). + WithProviderSecret(*dnsProviderSecret). WithTargetGateway("test-gateway2"). WithRoutingStrategy(v1alpha1.LoadBalancedRoutingStrategy). WithLoadBalancingFor(100, nil, "foo") @@ -247,7 +259,7 @@ var _ = Describe("DNSPolicy controller", func() { "already exists with record type 'A'" // policy2 should fail: dns provider already has a record for this host from the gateway1+policy1 - // gateway2+policy2 configured correctly, but conflict with existing records in managed zone + // gateway2+policy2 configured correctly, but conflict with existing records in the zone Eventually(func(g Gomega) { g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsPolicy2), dnsPolicy2)).To(Succeed()) g.Expect(dnsPolicy2.Status.RecordConditions[tests.HostOne(domain)]).To( @@ -308,6 +320,7 @@ var _ = Describe("DNSPolicy controller", func() { Context("invalid target", func() { It("should have accepted condition with status false and correct reason", func(ctx SpecContext) { dnsPolicy = v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). WithTargetGateway("test-gateway"). WithRoutingStrategy(v1alpha1.SimpleRoutingStrategy) Expect(k8sClient.Create(ctx, dnsPolicy)).To(Succeed()) @@ -383,19 +396,21 @@ var _ = Describe("DNSPolicy controller", func() { // Create policy1 targeting gateway1 with simple routing strategy dnsPolicy1 := v1alpha1.NewDNSPolicy("test-dns-policy1", testNamespace). + WithProviderSecret(*dnsProviderSecret). WithTargetGateway("test-gateway1"). WithRoutingStrategy(v1alpha1.SimpleRoutingStrategy) Expect(k8sClient.Create(ctx, dnsPolicy1)).To(Succeed()) // create policy2 targeting gateway2 with the load-balanced strategy dnsPolicy2 := v1alpha1.NewDNSPolicy("test-dns-policy2", testNamespace). + WithProviderSecret(*dnsProviderSecret). WithTargetGateway("test-gateway2"). WithRoutingStrategy(v1alpha1.LoadBalancedRoutingStrategy). WithLoadBalancingFor(100, nil, "foo") Expect(k8sClient.Create(ctx, dnsPolicy2)).To(Succeed()) // policy2 should fail: dns provider already has a record for this host from the gateway1+policy1 - // gateway2+policy2 configured correctly, but conflict with existing records in managed zone + // gateway2+policy2 configured correctly, but conflict with existing records in the zone Eventually(func(g Gomega) { g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsPolicy2), dnsPolicy2)).To(Succeed()) // check that policy is not enforced with a correct message @@ -436,6 +451,7 @@ var _ = Describe("DNSPolicy controller", func() { WithHTTPListener(tests.ListenerNameOne, tests.HostTwo(domain)). Gateway dnsPolicy = v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). WithTargetGateway(testGatewayName). WithRoutingStrategy(v1alpha1.SimpleRoutingStrategy) @@ -497,6 +513,7 @@ var _ = Describe("DNSPolicy controller", func() { WithHTTPListener(tests.ListenerNameWildcard, tests.HostWildcard(domain)). Gateway dnsPolicy = v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). WithTargetGateway(tests.GatewayName). WithRoutingStrategy(v1alpha1.SimpleRoutingStrategy) @@ -678,8 +695,10 @@ var _ = Describe("DNSPolicy controller", func() { Context("cel validation", func() { It("should error targeting invalid group", func(ctx SpecContext) { - p := v1alpha1.NewTLSPolicy("test-tls-policy", testNamespace). - WithTargetGateway("gateway") + p := v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). + WithTargetGateway("gateway"). + WithRoutingStrategy(v1alpha1.SimpleRoutingStrategy) p.Spec.TargetRef.Group = "not-gateway.networking.k8s.io" err := k8sClient.Create(ctx, p) @@ -688,8 +707,10 @@ var _ = Describe("DNSPolicy controller", func() { }, testTimeOut) It("should error targeting invalid kind", func(ctx SpecContext) { - p := v1alpha1.NewTLSPolicy("test-tls-policy", testNamespace). - WithTargetGateway("gateway") + p := v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). + WithTargetGateway("gateway"). + WithRoutingStrategy(v1alpha1.SimpleRoutingStrategy) p.Spec.TargetRef.Kind = "TCPRoute" err := k8sClient.Create(ctx, p) diff --git a/tests/common/targetstatus/target_status_controller_test.go b/tests/common/targetstatus/target_status_controller_test.go index 90aa83804..8f36b739d 100644 --- a/tests/common/targetstatus/target_status_controller_test.go +++ b/tests/common/targetstatus/target_status_controller_test.go @@ -8,15 +8,12 @@ import ( "strings" "time" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - . "github.com/onsi/gomega/gstruct" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - certmanv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" certmanmetav1 "github.com/cert-manager/cert-manager/pkg/apis/meta/v1" authorinoapi "github.com/kuadrant/authorino/api/v1beta2" kuadrantdnsv1alpha1 "github.com/kuadrant/dns-operator/api/v1alpha1" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -516,45 +513,28 @@ var _ = Describe("Target status reconciler", func() { } var dnsProviderSecret *corev1.Secret - var managedZone *kuadrantdnsv1alpha1.ManagedZone BeforeEach(func(ctx SpecContext) { - dnsProviderSecret = tests.BuildInMemoryCredentialsSecret("inmemory-credentials", testNamespace) - managedZone = tests.BuildManagedZone("mz-toystore-com", testNamespace, strings.Replace(gwHost, "*.", "", 1), dnsProviderSecret.Name) + dnsProviderSecret = tests.BuildInMemoryCredentialsSecret("inmemory-credentials", testNamespace, strings.Replace(gwHost, "*.", "", 1)) Expect(k8sClient.Create(ctx, dnsProviderSecret)).To(Succeed()) - Expect(k8sClient.Create(ctx, managedZone)).To(Succeed()) - Eventually(func(g Gomega) { - err := k8sClient.Get(ctx, client.ObjectKeyFromObject(managedZone), managedZone) - g.Expect(err).NotTo(HaveOccurred()) - - g.Expect(managedZone.Status.Conditions).To( - ContainElement(MatchFields(IgnoreExtras, Fields{ - "Type": Equal(string(kuadrantdnsv1alpha1.ConditionTypeReady)), - "Status": Equal(metav1.ConditionTrue), - "ObservedGeneration": Equal(managedZone.Generation), - })), - ) - }, tests.TimeoutMedium, time.Second).Should(Succeed()) }) AfterEach(func(ctx SpecContext) { - // Wait until dns records are finished deleting since it can't finish deleting without managed zone + // Wait until dns records are finished deleting since it can't finish deleting without the DNS provider secret Eventually(func(g Gomega) { dnsRecords := &kuadrantdnsv1alpha1.DNSRecordList{} err := k8sClient.List(ctx, dnsRecords, client.InNamespace(testNamespace)) g.Expect(err).NotTo(HaveOccurred()) g.Expect(dnsRecords.Items).To(HaveLen(0)) }).WithContext(ctx).Should(Succeed()) - Expect(k8sClient.Delete(ctx, managedZone)).To(Succeed()) - // Wait until managed zone is delete before deleting the provider secret - Eventually(func(g Gomega) { - err := k8sClient.Get(ctx, client.ObjectKeyFromObject(managedZone), managedZone) - g.Expect(k8serrors.IsNotFound(err)).To(BeTrue()) - }).WithContext(ctx).Should(Succeed()) }, afterEachTimeOut) It("adds PolicyAffected status condition to the targeted gateway", func(ctx SpecContext) { - policy := policyFactory() + policy := policyFactory(func(policy *v1alpha1.DNSPolicy) { + policy.Spec.ProviderRefs = append(policy.Spec.ProviderRefs, kuadrantdnsv1alpha1.ProviderRef{ + Name: dnsProviderSecret.Name, + }) + }) defer k8sClient.Delete(ctx, policy) Expect(k8sClient.Create(ctx, policy)).To(Succeed()) // policy should not be enforced since DNS Record is not ready because of the missing secret on the MZ @@ -563,7 +543,11 @@ var _ = Describe("Target status reconciler", func() { }, testTimeOut) It("removes PolicyAffected status condition from the targeted gateway when the policy is deleted", func(ctx SpecContext) { - policy := policyFactory() + policy := policyFactory(func(policy *v1alpha1.DNSPolicy) { + policy.Spec.ProviderRefs = append(policy.Spec.ProviderRefs, kuadrantdnsv1alpha1.ProviderRef{ + Name: dnsProviderSecret.Name, + }) + }) defer k8sClient.Delete(ctx, policy) policyKey := client.ObjectKeyFromObject(policy) Expect(k8sClient.Create(ctx, policy)).To(Succeed()) diff --git a/tests/commons.go b/tests/commons.go index 990dcb348..9c3534343 100644 --- a/tests/commons.go +++ b/tests/commons.go @@ -25,9 +25,10 @@ import ( gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - kuadrantdnsv1alpha1 "github.com/kuadrant/dns-operator/api/v1alpha1" limitadorv1alpha1 "github.com/kuadrant/limitador-operator/api/v1alpha1" + kuadrantdnsv1alpha1 "github.com/kuadrant/dns-operator/api/v1alpha1" + kuadrantdnsbuilder "github.com/kuadrant/dns-operator/pkg/builder" kuadrantv1beta1 "github.com/kuadrant/kuadrant-operator/api/v1beta1" kuadrantv1beta2 "github.com/kuadrant/kuadrant-operator/api/v1beta2" kuadrantgatewayapi "github.com/kuadrant/kuadrant-operator/pkg/library/gatewayapi" @@ -457,31 +458,11 @@ func ObjectDoesNotExist(k8sClient client.Client, obj client.Object) func() bool // DNS -func BuildManagedZone(name, ns, domainName, secretName string) *kuadrantdnsv1alpha1.ManagedZone { - return &kuadrantdnsv1alpha1.ManagedZone{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: kuadrantdnsv1alpha1.ManagedZoneSpec{ - DomainName: domainName, - Description: domainName, - SecretRef: kuadrantdnsv1alpha1.ProviderRef{ - Name: secretName, - }, - }, - } -} - -func BuildInMemoryCredentialsSecret(name, ns string) *corev1.Secret { - return &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Data: map[string][]byte{}, - Type: "kuadrant.io/inmemory", - } +func BuildInMemoryCredentialsSecret(name, ns, initDomain string) *corev1.Secret { + return kuadrantdnsbuilder.NewProviderBuilder(name, ns). + For(kuadrantdnsv1alpha1.SecretTypeKuadrantInmemory). + WithZonesInitialisedFor(initDomain). + Build() } // EndpointsTraversable consumes an array of endpoints and returns a boolean