diff --git a/apis/druid/v1alpha1/druid_types.go b/apis/druid/v1alpha1/druid_types.go index 0fd457ca..0e97ae6d 100644 --- a/apis/druid/v1alpha1/druid_types.go +++ b/apis/druid/v1alpha1/druid_types.go @@ -290,6 +290,14 @@ type DruidSpec struct { // +optional Auth druidapi.Auth `json:"auth,omitempty"` + + // See v1.DNSPolicy for more details. + // +optional + DNSPolicy v1.DNSPolicy `json:"dnsPolicy,omitempty" protobuf:"bytes,6,opt,name=dnsPolicy,casttype=DNSPolicy"` + + // See v1.PodDNSConfig for more details. + // +optional + DNSConfig *v1.PodDNSConfig `json:"dnsConfig,omitempty" protobuf:"bytes,26,opt,name=dnsConfig"` } // DruidNodeSpec Specification of `Druid` Node type and its configurations. @@ -494,6 +502,14 @@ type DruidNodeSpec struct { // Dynamic Configurations for Druid. Applied through the dynamic configuration API. // +optional DynamicConfig runtime.RawExtension `json:"dynamicConfig,omitempty"` + + // See v1.DNSPolicy for more details. + // +optional + DNSPolicy v1.DNSPolicy `json:"dnsPolicy,omitempty" protobuf:"bytes,6,opt,name=dnsPolicy,casttype=DNSPolicy"` + + // See v1.PodDNSConfig for more details. + // +optional + DNSConfig *v1.PodDNSConfig `json:"dnsConfig,omitempty" protobuf:"bytes,26,opt,name=dnsConfig"` } // ZookeeperSpec IGNORED (Future API): In order to make Druid dependency setup extensible from within Druid operator. diff --git a/apis/druid/v1alpha1/zz_generated.deepcopy.go b/apis/druid/v1alpha1/zz_generated.deepcopy.go index 26253972..ea1550a2 100644 --- a/apis/druid/v1alpha1/zz_generated.deepcopy.go +++ b/apis/druid/v1alpha1/zz_generated.deepcopy.go @@ -511,6 +511,11 @@ func (in *DruidNodeSpec) DeepCopyInto(out *DruidNodeSpec) { } } in.DynamicConfig.DeepCopyInto(&out.DynamicConfig) + if in.DNSConfig != nil { + in, out := &in.DNSConfig, &out.DNSConfig + *out = new(v1.PodDNSConfig) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DruidNodeSpec. @@ -700,6 +705,11 @@ func (in *DruidSpec) DeepCopyInto(out *DruidSpec) { } in.DynamicConfig.DeepCopyInto(&out.DynamicConfig) out.Auth = in.Auth + if in.DNSConfig != nil { + in, out := &in.DNSConfig, &out.DNSConfig + *out = new(v1.PodDNSConfig) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DruidSpec. diff --git a/chart/crds/druid.apache.org_druids.yaml b/chart/crds/druid.apache.org_druids.yaml index 742756db..9bec0105 100644 --- a/chart/crds/druid.apache.org_druids.yaml +++ b/chart/crds/druid.apache.org_druids.yaml @@ -1501,6 +1501,46 @@ spec: description: DisablePVCDeletionFinalizer Whether PVCs shall be deleted on the deletion of the Druid cluster. type: boolean + dnsConfig: + description: See v1.PodDNSConfig for more details. + properties: + nameservers: + description: |- + A list of DNS name server IP addresses. + This will be appended to the base nameservers generated from DNSPolicy. + Duplicated nameservers will be removed. + items: + type: string + type: array + options: + description: |- + A list of DNS resolver options. + This will be merged with the base options generated from DNSPolicy. + Duplicated entries will be removed. Resolution options given in Options + will override those that appear in the base DNSPolicy. + items: + description: PodDNSConfigOption defines DNS resolver options + of a pod. + properties: + name: + description: Required. + type: string + value: + type: string + type: object + type: array + searches: + description: |- + A list of DNS search domains for host-name lookup. + This will be appended to the base search paths generated from DNSPolicy. + Duplicated search paths will be removed. + items: + type: string + type: array + type: object + dnsPolicy: + description: See v1.DNSPolicy for more details. + type: string dynamicConfig: description: Dynamic Configurations for Druid. Applied through the dynamic configuration API. @@ -3367,6 +3407,46 @@ spec: type: string type: object type: object + dnsConfig: + description: See v1.PodDNSConfig for more details. + properties: + nameservers: + description: |- + A list of DNS name server IP addresses. + This will be appended to the base nameservers generated from DNSPolicy. + Duplicated nameservers will be removed. + items: + type: string + type: array + options: + description: |- + A list of DNS resolver options. + This will be merged with the base options generated from DNSPolicy. + Duplicated entries will be removed. Resolution options given in Options + will override those that appear in the base DNSPolicy. + items: + description: PodDNSConfigOption defines DNS resolver options + of a pod. + properties: + name: + description: Required. + type: string + value: + type: string + type: object + type: array + searches: + description: |- + A list of DNS search domains for host-name lookup. + This will be appended to the base search paths generated from DNSPolicy. + Duplicated search paths will be removed. + items: + type: string + type: array + type: object + dnsPolicy: + description: See v1.DNSPolicy for more details. + type: string druid.port: description: DruidPort Used by the `Druid` process. format: int32 diff --git a/config/crd/bases/druid.apache.org_druids.yaml b/config/crd/bases/druid.apache.org_druids.yaml index 742756db..9bec0105 100644 --- a/config/crd/bases/druid.apache.org_druids.yaml +++ b/config/crd/bases/druid.apache.org_druids.yaml @@ -1501,6 +1501,46 @@ spec: description: DisablePVCDeletionFinalizer Whether PVCs shall be deleted on the deletion of the Druid cluster. type: boolean + dnsConfig: + description: See v1.PodDNSConfig for more details. + properties: + nameservers: + description: |- + A list of DNS name server IP addresses. + This will be appended to the base nameservers generated from DNSPolicy. + Duplicated nameservers will be removed. + items: + type: string + type: array + options: + description: |- + A list of DNS resolver options. + This will be merged with the base options generated from DNSPolicy. + Duplicated entries will be removed. Resolution options given in Options + will override those that appear in the base DNSPolicy. + items: + description: PodDNSConfigOption defines DNS resolver options + of a pod. + properties: + name: + description: Required. + type: string + value: + type: string + type: object + type: array + searches: + description: |- + A list of DNS search domains for host-name lookup. + This will be appended to the base search paths generated from DNSPolicy. + Duplicated search paths will be removed. + items: + type: string + type: array + type: object + dnsPolicy: + description: See v1.DNSPolicy for more details. + type: string dynamicConfig: description: Dynamic Configurations for Druid. Applied through the dynamic configuration API. @@ -3367,6 +3407,46 @@ spec: type: string type: object type: object + dnsConfig: + description: See v1.PodDNSConfig for more details. + properties: + nameservers: + description: |- + A list of DNS name server IP addresses. + This will be appended to the base nameservers generated from DNSPolicy. + Duplicated nameservers will be removed. + items: + type: string + type: array + options: + description: |- + A list of DNS resolver options. + This will be merged with the base options generated from DNSPolicy. + Duplicated entries will be removed. Resolution options given in Options + will override those that appear in the base DNSPolicy. + items: + description: PodDNSConfigOption defines DNS resolver options + of a pod. + properties: + name: + description: Required. + type: string + value: + type: string + type: object + type: array + searches: + description: |- + A list of DNS search domains for host-name lookup. + This will be appended to the base search paths generated from DNSPolicy. + Duplicated search paths will be removed. + items: + type: string + type: array + type: object + dnsPolicy: + description: See v1.DNSPolicy for more details. + type: string druid.port: description: DruidPort Used by the `Druid` process. format: int32 diff --git a/controllers/druid/handler.go b/controllers/druid/handler.go index c47032cc..8fc251dc 100644 --- a/controllers/druid/handler.go +++ b/controllers/druid/handler.go @@ -1122,6 +1122,8 @@ func makePodSpec(nodeSpec *v1alpha1.DruidNodeSpec, m *v1alpha1.Druid, nodeSpecUn SecurityContext: firstNonNilValue(nodeSpec.PodSecurityContext, m.Spec.PodSecurityContext).(*v1.PodSecurityContext), ServiceAccountName: firstNonEmptyStr(nodeSpec.ServiceAccountName, m.Spec.ServiceAccount), PriorityClassName: firstNonEmptyStr(nodeSpec.PriorityClassName, m.Spec.PriorityClassName), + DNSPolicy: v1.DNSPolicy(firstNonEmptyStr(string(nodeSpec.DNSPolicy), string(m.Spec.DNSPolicy))), + DNSConfig: firstNonNilValue(nodeSpec.DNSConfig, m.Spec.DNSConfig).(*v1.PodDNSConfig), } addAdditionalContainers(m, nodeSpec, &spec) diff --git a/controllers/druid/handler_test.go b/controllers/druid/handler_test.go index bde1a877..55887c9d 100644 --- a/controllers/druid/handler_test.go +++ b/controllers/druid/handler_test.go @@ -2,6 +2,8 @@ package druid import ( "io/ioutil" + "reflect" + "testing" "github.com/ghodss/yaml" . "github.com/onsi/ginkgo/v2" @@ -213,3 +215,139 @@ func readAndUnmarshallResource(file string, res interface{}) error { } return nil } + +func TestPodSpecDNSConfig(t *testing.T) { + tests := []struct { + name string + nodeDNSConfig *corev1.PodDNSConfig + specDNSConfig *corev1.PodDNSConfig + expected *corev1.PodDNSConfig + }{ + { + name: "Both nil", + nodeDNSConfig: nil, + specDNSConfig: nil, + expected: nil, + }, + { + name: "Only spec provided", + nodeDNSConfig: nil, + specDNSConfig: &corev1.PodDNSConfig{ + Nameservers: []string{"8.8.8.8"}, + Searches: []string{"example.com"}, + }, + expected: &corev1.PodDNSConfig{ + Nameservers: []string{"8.8.8.8"}, + Searches: []string{"example.com"}, + }, + }, + { + name: "Only node provided", + nodeDNSConfig: &corev1.PodDNSConfig{ + Nameservers: []string{"1.1.1.1"}, + Searches: []string{"node.local"}, + }, + specDNSConfig: nil, + expected: &corev1.PodDNSConfig{ + Nameservers: []string{"1.1.1.1"}, + Searches: []string{"node.local"}, + }, + }, + { + name: "Both provided, node wins", + nodeDNSConfig: &corev1.PodDNSConfig{ + Nameservers: []string{"1.1.1.1"}, + Searches: []string{"node.local"}, + }, + specDNSConfig: &corev1.PodDNSConfig{ + Nameservers: []string{"8.8.8.8"}, + Searches: []string{"example.com"}, + }, + expected: &corev1.PodDNSConfig{ + Nameservers: []string{"1.1.1.1"}, + Searches: []string{"node.local"}, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + m := &druidv1alpha1.Druid{ + Spec: druidv1alpha1.DruidSpec{ + DNSConfig: tc.specDNSConfig, + }, + } + nodeSpec := &druidv1alpha1.DruidNodeSpec{ + DNSConfig: tc.nodeDNSConfig, + } + podSpec := makePodSpec(nodeSpec, m, "unique", "dummySHA") + if !reflect.DeepEqual(podSpec.DNSConfig, tc.expected) { + t.Errorf("expected DNSConfig %v, got %v", tc.expected, podSpec.DNSConfig) + } + }) + } +} + +func TestPodSpecDNSConfigYAML(t *testing.T) { + m, err := readDruidClusterSpecFromFile("testdata/druid-test-cr.yaml") + if err != nil { + t.Fatalf("failed to read cluster spec: %v", err) + } + nodeSpec := m.Spec.Nodes["middlemanagers"] + podSpec := makePodSpec(&nodeSpec, m, "unique", "dummySHA") + expectedDNSConfig := &corev1.PodDNSConfig{ + Nameservers: []string{"10.0.0.53"}, + Searches: []string{"example.local"}, + } + if !reflect.DeepEqual(podSpec.DNSConfig, expectedDNSConfig) { + t.Errorf("expected DNSConfig %v, got %v", expectedDNSConfig, podSpec.DNSConfig) + } +} + +// TestPodSpecDNSPolicy verifies DNSPolicy resolution in makePodSpec. +func TestPodSpecDNSPolicy(t *testing.T) { + tests := []struct { + name string + nodeDNS string + specDNS string + expected corev1.DNSPolicy + }{ + {"Both empty", "", "", corev1.DNSPolicy("")}, + {"Only spec provided", "", "ClusterFirst", corev1.DNSPolicy("ClusterFirst")}, + {"Only node provided", "Default", "", corev1.DNSPolicy("Default")}, + {"Both provided, node wins", "Default", "ClusterFirst", corev1.DNSPolicy("Default")}, + } + + for _, tc := range tests { + tc := tc // capture current test case + t.Run(tc.name, func(t *testing.T) { + m := &druidv1alpha1.Druid{ + Spec: druidv1alpha1.DruidSpec{ + DNSPolicy: corev1.DNSPolicy(tc.specDNS), + }, + } + nodeSpec := &druidv1alpha1.DruidNodeSpec{ + DNSPolicy: corev1.DNSPolicy(tc.nodeDNS), + } + podSpec := makePodSpec(nodeSpec, m, "unique", "dummySHA") + if podSpec.DNSPolicy != tc.expected { + t.Errorf("expected DNSPolicy %q, got %q", tc.expected, podSpec.DNSPolicy) + } + }) + } +} + +// TestPodSpecDNSPolicyYAML validates that the generated PodSpec DNSPolicy matches the expected value, +// using the druid-test-cr.yaml as the single input file. +func TestPodSpecDNSPolicyYAML(t *testing.T) { + m, err := readDruidClusterSpecFromFile("testdata/druid-test-cr.yaml") + if err != nil { + t.Fatalf("failed to read cluster spec: %v", err) + } + nodeSpec := m.Spec.Nodes["middlemanagers"] + podSpec := makePodSpec(&nodeSpec, m, "unique", "dummySHA") + expectedDNSPolicy := corev1.DNSPolicy("ClusterFirst") + if podSpec.DNSPolicy != expectedDNSPolicy { + t.Errorf("expected DNSPolicy %q, got %q", expectedDNSPolicy, podSpec.DNSPolicy) + } +} diff --git a/controllers/druid/testdata/druid-test-cr.yaml b/controllers/druid/testdata/druid-test-cr.yaml index d3197408..0cdc8a23 100644 --- a/controllers/druid/testdata/druid-test-cr.yaml +++ b/controllers/druid/testdata/druid-test-cr.yaml @@ -355,3 +355,9 @@ spec: limits: memory: "3Gi" cpu: "4" + dnsPolicy: ClusterFirst + dnsConfig: + nameservers: + - 10.0.0.53 + searches: + - example.local diff --git a/docs/api_specifications/druid.md b/docs/api_specifications/druid.md index fd57ded3..993205d5 100644 --- a/docs/api_specifications/druid.md +++ b/docs/api_specifications/druid.md @@ -907,6 +907,34 @@ string

CoreSite Contents of core-site.xml.

+ + +dnsPolicy
+ + +Kubernetes core/v1.DNSPolicy + + + + +(Optional) +

See v1.DNSPolicy for more details.

+ + + + +dnsConfig
+ + +Kubernetes core/v1.PodDNSConfig + + + + +(Optional) +

See v1.PodDNSConfig for more details.

+ + @@ -1942,6 +1970,34 @@ Kubernetes autoscaling/v2.HorizontalPodAutoscalerSpec

Operator deploys the sidecar container based on these properties.

+ + +dnsPolicy
+ + +Kubernetes core/v1.DNSPolicy + + + + +(Optional) +

See v1.DNSPolicy for more details.

+ + + + +dnsConfig
+ + +Kubernetes core/v1.PodDNSConfig + + + + +(Optional) +

See v1.PodDNSConfig for more details.

+ + @@ -2626,6 +2682,34 @@ string

CoreSite Contents of core-site.xml.

+ + +dnsPolicy
+ + +Kubernetes core/v1.DNSPolicy + + + + +(Optional) +

See v1.DNSPolicy for more details.

+ + + + +dnsConfig
+ + +Kubernetes core/v1.PodDNSConfig + + + + +(Optional) +

See v1.PodDNSConfig for more details.

+ + diff --git a/e2e/configs/druid-cr.yaml b/e2e/configs/druid-cr.yaml index 977f8c89..0fff78ce 100644 --- a/e2e/configs/druid-cr.yaml +++ b/e2e/configs/druid-cr.yaml @@ -121,6 +121,12 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + dnsPolicy: ClusterFirst + dnsConfig: + nameservers: + - 10.0.0.53 + searches: + - example.local nodes: brokers: # Optionally specify for running broker as Deployment