diff --git a/docs/tasks/issuers/setup-acme/http01/index.rst b/docs/tasks/issuers/setup-acme/http01/index.rst index e75c79945..34a22df9f 100644 --- a/docs/tasks/issuers/setup-acme/http01/index.rst +++ b/docs/tasks/issuers/setup-acme/http01/index.rst @@ -74,9 +74,9 @@ podTemplate You may wish to change or add to the labels and annotations of solver pods. These can be configured under the ``metadata`` field under ``podTemplate``. -Similarly, you can set the node selector and tolerations of solver pods by -configuring under the ``spec`` field of the ``podTemplate``. No other spec fields -can be edited. +Similarly, you can set the nodeSelector, tolerations and affinity of solver +pods by configuring under the ``spec`` field of the ``podTemplate``. No other +spec fields can be edited. An example of how you could configure the template is as so: diff --git a/pkg/apis/certmanager/v1alpha1/types_issuer.go b/pkg/apis/certmanager/v1alpha1/types_issuer.go index 9dfe3b54c..4c9146ddd 100644 --- a/pkg/apis/certmanager/v1alpha1/types_issuer.go +++ b/pkg/apis/certmanager/v1alpha1/types_issuer.go @@ -298,16 +298,38 @@ type ACMEChallengeSolverHTTP01Ingress struct { Name string `json:"name,omitempty"` // Optional pod template used to configure the ACME challenge solver pods - // used for HTTP01 challenges. Only labels and annotations may be set and - // will be merged ontop of the defaults. PodTemplate labels and annotation - // fields will override fields with matching keys. + // used for HTTP01 challenges // +optional PodTemplate *ACMEChallengeSolverHTTP01IngressPodTemplate `json:"podTemplate,omitempty"` } type ACMEChallengeSolverHTTP01IngressPodTemplate struct { + // ObjectMeta overrides for the pod used to solve HTTP01 challenges. + // Only the 'labels' and 'annotations' fields may be set. + // If labels or annotations overlap with in-built value, the values here + // will override the in-built values. metav1.ObjectMeta `json:"metadata,omitempty"` - corev1.PodSpec `json:"spec,omitempty"` + + // PodSpec defines overrides for the HTTP01 challenge solver pod. + // Only the 'nodeSelector', 'affinity' and 'tolerations' fields are + // supported currently. All other fields will be ignored. + Spec ACMEChallengeSolverHTTP01IngressPodSpec `json:"spec,omitempty"` +} + +type ACMEChallengeSolverHTTP01IngressPodSpec struct { + // NodeSelector is a selector which must be true for the pod to fit on a node. + // Selector which must match a node's labels for the pod to be scheduled on that node. + // More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + // +optional + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + + // If specified, the pod's scheduling constraints + // +optional + Affinity *corev1.Affinity `json:"affinity,omitempty"` + + // If specified, the pod's tolerations. + // +optional + Tolerations []corev1.Toleration `json:"tolerations,omitempty"` } type ACMEChallengeSolverDNS01 struct { diff --git a/pkg/apis/certmanager/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/certmanager/v1alpha1/zz_generated.deepcopy.go index be17dc082..fe00dd5a9 100644 --- a/pkg/apis/certmanager/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/certmanager/v1alpha1/zz_generated.deepcopy.go @@ -21,8 +21,9 @@ limitations under the License. package v1alpha1 import ( + v1 "k8s.io/api/core/v1" v1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -188,11 +189,46 @@ func (in *ACMEChallengeSolverHTTP01Ingress) DeepCopy() *ACMEChallengeSolverHTTP0 return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ACMEChallengeSolverHTTP01IngressPodSpec) DeepCopyInto(out *ACMEChallengeSolverHTTP01IngressPodSpec) { + *out = *in + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(v1.Affinity) + (*in).DeepCopyInto(*out) + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEChallengeSolverHTTP01IngressPodSpec. +func (in *ACMEChallengeSolverHTTP01IngressPodSpec) DeepCopy() *ACMEChallengeSolverHTTP01IngressPodSpec { + if in == nil { + return nil + } + out := new(ACMEChallengeSolverHTTP01IngressPodSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ACMEChallengeSolverHTTP01IngressPodTemplate) DeepCopyInto(out *ACMEChallengeSolverHTTP01IngressPodTemplate) { *out = *in in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.PodSpec.DeepCopyInto(&out.PodSpec) + in.Spec.DeepCopyInto(&out.Spec) return } @@ -731,7 +767,7 @@ func (in *CertificateRequestSpec) DeepCopyInto(out *CertificateRequestSpec) { *out = *in if in.Duration != nil { in, out := &in.Duration, &out.Duration - *out = new(v1.Duration) + *out = new(metav1.Duration) (*in).DeepCopyInto(*out) } out.IssuerRef = in.IssuerRef @@ -796,12 +832,12 @@ func (in *CertificateSpec) DeepCopyInto(out *CertificateSpec) { } if in.Duration != nil { in, out := &in.Duration, &out.Duration - *out = new(v1.Duration) + *out = new(metav1.Duration) (*in).DeepCopyInto(*out) } if in.RenewBefore != nil { in, out := &in.RenewBefore, &out.RenewBefore - *out = new(v1.Duration) + *out = new(metav1.Duration) (*in).DeepCopyInto(*out) } if in.DNSNames != nil { diff --git a/pkg/apis/certmanager/validation/issuer.go b/pkg/apis/certmanager/validation/issuer.go index da5e9a107..f1049e754 100644 --- a/pkg/apis/certmanager/validation/issuer.go +++ b/pkg/apis/certmanager/validation/issuer.go @@ -156,13 +156,6 @@ func ValidateACMEIssuerChallengeSolverHTTP01IngressPodTemplateConfig(podTempl *v el = append(el, field.Invalid(fldPath.Child("metadata"), "", "only labels and annotations may be set on podTemplate metadata")) } - cpyPodTempl.NodeSelector = nil - cpyPodTempl.Tolerations = nil - - if !reflect.DeepEqual(cpyPodTempl.PodSpec, corev1.PodSpec{}) { - el = append(el, field.Invalid(fldPath.Child("spec"), "", "only nodeSelector and tolerations may be set on podTemplate spec")) - } - return el } diff --git a/pkg/apis/certmanager/validation/issuer_test.go b/pkg/apis/certmanager/validation/issuer_test.go index c45aa4c12..63dc28e3f 100644 --- a/pkg/apis/certmanager/validation/issuer_test.go +++ b/pkg/apis/certmanager/validation/issuer_test.go @@ -266,7 +266,7 @@ func TestValidateACMEIssuerConfig(t *testing.T) { HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ PodTemplate: &v1alpha1.ACMEChallengeSolverHTTP01IngressPodTemplate{ - PodSpec: corev1.PodSpec{ + Spec: v1alpha1.ACMEChallengeSolverHTTP01IngressPodSpec{ NodeSelector: map[string]string{ "valid_to_contain": "nodeSelector", }, @@ -288,33 +288,6 @@ func TestValidateACMEIssuerConfig(t *testing.T) { }, }, }, - "acme issue with invalid pod template PodSpec attributes": { - spec: &v1alpha1.ACMEIssuer{ - Email: "valid-email", - Server: "valid-server", - PrivateKey: validSecretKeyRef, - Solvers: []v1alpha1.ACMEChallengeSolver{ - { - HTTP01: &v1alpha1.ACMEChallengeSolverHTTP01{ - Ingress: &v1alpha1.ACMEChallengeSolverHTTP01Ingress{ - PodTemplate: &v1alpha1.ACMEChallengeSolverHTTP01IngressPodTemplate{ - PodSpec: corev1.PodSpec{ - NodeSelector: map[string]string{ - "valid_to_contain": "nodeSelector", - }, - NodeName: "unable-to-change-nodeName", - }, - }, - }, - }, - }, - }, - }, - errs: []*field.Error{ - field.Invalid(fldPath.Child("solver", "http01", "ingress", "podTemplate", "spec"), - "", "only nodeSelector and tolerations may be set on podTemplate spec"), - }, - }, "acme issue with valid pod template ObjectMeta and PodSpec attributes": { spec: &v1alpha1.ACMEIssuer{ Email: "valid-email", @@ -330,7 +303,7 @@ func TestValidateACMEIssuerConfig(t *testing.T) { "valid_to_contain": "labels", }, }, - PodSpec: corev1.PodSpec{ + Spec: v1alpha1.ACMEChallengeSolverHTTP01IngressPodSpec{ NodeSelector: map[string]string{ "valid_to_contain": "nodeSelector", }, diff --git a/pkg/issuer/acme/http/pod.go b/pkg/issuer/acme/http/pod.go index 293507044..94fe5d9d5 100644 --- a/pkg/issuer/acme/http/pod.go +++ b/pkg/issuer/acme/http/pod.go @@ -228,7 +228,7 @@ func (s *Solver) mergePodObjectMetaWithPodTemplate(pod *corev1.Pod, podTempl *v1 pod.Spec.NodeSelector = make(map[string]string) } - for k, v := range podTempl.NodeSelector { + for k, v := range podTempl.Spec.NodeSelector { pod.Spec.NodeSelector[k] = v } @@ -236,9 +236,13 @@ func (s *Solver) mergePodObjectMetaWithPodTemplate(pod *corev1.Pod, podTempl *v1 pod.Spec.Tolerations = []corev1.Toleration{} } - for _, t := range podTempl.Tolerations { + for _, t := range podTempl.Spec.Tolerations { pod.Spec.Tolerations = append(pod.Spec.Tolerations, t) } + if podTempl.Spec.Affinity != nil { + pod.Spec.Affinity = podTempl.Spec.Affinity + } + return pod }