Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion api/v1/calico_node_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ type CalicoNodeDaemonSetPodSpec struct {
// +optional
Tolerations []v1.Toleration `json:"tolerations"`

// DNSPolicy is the DNS policy for the calico-node pods.
// DNSPolicy is the DNS policy for the calico-node pods. Defaults to Default, which uses the node's
// own resolver, since calico-node runs before cluster DNS is reachable.
// +kubebuilder:validation:Enum="";Default;ClusterFirst;ClusterFirstWithHostNet;None
// +optional
DNSPolicy *v1.DNSPolicy `json:"dnsPolicy,omitempty"`
Expand Down
13 changes: 3 additions & 10 deletions pkg/controller/installation/core_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -1441,15 +1441,8 @@ func (r *ReconcileInstallation) Reconcile(ctx context.Context, request reconcile
goldmaneRunning = goldmaneCR != nil
}

// Calico node DNS configuration and policy should be inherited from the tigera/operator Deployment by default since:
//
// - they are both host networked and run prior to CNI being installed (and thus beofre kube-dns is available)
// - they both need access to in-cluster serivces via kube-dns, as well as external services such as the API server.
//
// So, they will require the same DNS configuration.
//
// Users can override this with explicit configuration in the Installation resource, but using the operator as
// a baseline is a reasonable default.
// calico/node runs before CNI is installed, so cluster DNS is not yet reachable on the node. Use the
// node's own resolver, and inherit the operator's DNS settings only when it has an explicit dnsConfig.
operatorDeployment := &appsv1.Deployment{}
defaultDNSPolicy := corev1.DNSDefault
var defaultDNSConfig *corev1.PodDNSConfig
Expand All @@ -1459,7 +1452,7 @@ func (r *ReconcileInstallation) Reconcile(ctx context.Context, request reconcile
return reconcile.Result{}, err
}
reqLogger.Info("Operator Deployment not found, using default DNS configuration")
} else {
} else if operatorDeployment.Spec.Template.Spec.DNSConfig != nil {
defaultDNSPolicy = operatorDeployment.Spec.Template.Spec.DNSPolicy
defaultDNSConfig = operatorDeployment.Spec.Template.Spec.DNSConfig
}
Expand Down
60 changes: 60 additions & 0 deletions pkg/controller/installation/core_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,66 @@ var _ = Describe("Testing core-controller installation", func() {
})
})

Context("DNS configuration", func() {
operatorDeployment := func(policy corev1.DNSPolicy, dnsConfig *corev1.PodDNSConfig) *appsv1.Deployment {
return &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{Name: common.OperatorName(), Namespace: common.OperatorNamespace()},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{DNSPolicy: policy, DNSConfig: dnsConfig},
},
},
}
}

nodeDaemonSet := func() appsv1.DaemonSet {
_, err := r.Reconcile(ctx, reconcile.Request{})
Expect(err).ShouldNot(HaveOccurred())

ds := appsv1.DaemonSet{}
key := types.NamespacedName{Name: common.NodeDaemonSetName, Namespace: common.CalicoNamespace}
Expect(c.Get(ctx, key, &ds)).NotTo(HaveOccurred())
return ds
}

It("should use the node's resolver when the operator has no explicit dnsConfig", func() {
Expect(c.Create(ctx, operatorDeployment(corev1.DNSClusterFirstWithHostNet, nil))).NotTo(HaveOccurred())

ds := nodeDaemonSet()
Expect(ds.Spec.Template.Spec.DNSPolicy).To(Equal(corev1.DNSDefault))
Expect(ds.Spec.Template.Spec.DNSConfig).To(BeNil())
})

It("should inherit the operator's DNS settings when it has an explicit dnsConfig", func() {
dnsConfig := &corev1.PodDNSConfig{Nameservers: []string{"10.96.0.10", "169.254.169.253"}}
Expect(c.Create(ctx, operatorDeployment(corev1.DNSNone, dnsConfig))).NotTo(HaveOccurred())

ds := nodeDaemonSet()
Expect(ds.Spec.Template.Spec.DNSPolicy).To(Equal(corev1.DNSNone))
Expect(ds.Spec.Template.Spec.DNSConfig).To(Equal(dnsConfig))
})

It("should use the node's resolver when the operator Deployment is missing", func() {
ds := nodeDaemonSet()
Expect(ds.Spec.Template.Spec.DNSPolicy).To(Equal(corev1.DNSDefault))
Expect(ds.Spec.Template.Spec.DNSConfig).To(BeNil())
})
})

It("degrades with a configuration reason when the extension rejects the configuration", func() {
mockStatus.On("SetDegraded", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return()

port := 0
Expect(c.Create(ctx, &v3.FelixConfiguration{
ObjectMeta: metav1.ObjectMeta{Name: "default"},
Spec: v3.FelixConfigurationSpec{PrometheusReporterPort: &port},
})).NotTo(HaveOccurred())

_, err := r.Reconcile(ctx, reconcile.Request{})
Expect(err).To(HaveOccurred())
mockStatus.AssertCalled(GinkgoT(), "SetDegraded", operator.InvalidConfigurationError, "invalid metrics port: felixConfiguration prometheusReporterPort=0 not supported", mock.Anything, mock.Anything)
})

Context("image tests", func() {
It("should use builtin images", func() {
_, err := r.Reconcile(ctx, reconcile.Request{})
Expand Down
12 changes: 6 additions & 6 deletions pkg/crds/operator/operator.tigera.io_installations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2712,9 +2712,9 @@ spec:
x-kubernetes-list-type: atomic
type: object
dnsPolicy:
description:
DNSPolicy is the DNS policy for the calico-node
pods.
description: |-
DNSPolicy is the DNS policy for the calico-node pods. Defaults to Default, which uses the node's
own resolver, since calico-node runs before cluster DNS is reachable.
enum:
- ""
- Default
Expand Down Expand Up @@ -11560,9 +11560,9 @@ spec:
x-kubernetes-list-type: atomic
type: object
dnsPolicy:
description:
DNSPolicy is the DNS policy for the
calico-node pods.
description: |-
DNSPolicy is the DNS policy for the calico-node pods. Defaults to Default, which uses the node's
own resolver, since calico-node runs before cluster DNS is reachable.
enum:
- ""
- Default
Expand Down
2 changes: 1 addition & 1 deletion pkg/render/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ type NodeConfiguration struct {
func Node(cfg *NodeConfiguration) Component {
// Configure default values for any fields that might not be set.
if cfg.DefaultDNSPolicy == "" {
cfg.DefaultDNSPolicy = corev1.DNSClusterFirstWithHostNet
cfg.DefaultDNSPolicy = corev1.DNSDefault
}
return &nodeComponent{cfg: cfg}
}
Expand Down
11 changes: 11 additions & 0 deletions pkg/render/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3093,6 +3093,17 @@ var _ = Describe("Node rendering tests", func() {
},
}

It("should default to the node's own DNS resolver", func() {
component := render.Node(&cfg)
resources, _ := component.Objects()
dsResource := rtest.GetResource(resources, "calico-node", "calico-system", "apps", "v1", "DaemonSet")
Expect(dsResource).ToNot(BeNil())

ds := dsResource.(*appsv1.DaemonSet)
Expect(ds.Spec.Template.Spec.DNSPolicy).To(Equal(corev1.DNSDefault))
Expect(ds.Spec.Template.Spec.DNSConfig).To(BeNil())
})

It("should handle calicoNodeDaemonSet overrides", func() {
var minReadySeconds int32 = 20

Expand Down