Merge pull request #1803 from cheukwing/issue-1770

Add support for nodeSelector, tolerations & affinity in podTemplate
This commit is contained in:
jetstack-bot 2019-07-08 14:27:34 +01:00 committed by GitHub
commit 1b9b83a4b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 256 additions and 35 deletions

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
(function(){navData = {"toc":[{"section":"-strong-field-definitions-strong-","subsections":[{"section":"venafitpp-v1alpha1"},{"section":"venafiissuer-v1alpha1"},{"section":"venaficloud-v1alpha1"},{"section":"vaultissuer-v1alpha1"},{"section":"vaultauth-v1alpha1"},{"section":"vaultapprole-v1alpha1"},{"section":"time-v1"},{"section":"statusdetails-v1"},{"section":"statuscause-v1"},{"section":"status-v1"},{"section":"solverconfig-v1alpha1"},{"section":"selfsignedissuer-v1alpha1"},{"section":"secretkeyselector-v1alpha1"},{"section":"ownerreference-v1"},{"section":"objectreference-v1alpha1"},{"section":"objectmeta-v1"},{"section":"managedfieldsentry-v1"},{"section":"localobjectreference-v1alpha1"},{"section":"listmeta-v1"},{"section":"issuercondition-v1alpha1"},{"section":"initializers-v1"},{"section":"initializer-v1"},{"section":"http01solverconfig-v1alpha1"},{"section":"fields-v1"},{"section":"duration-v1"},{"section":"domainsolverconfig-v1alpha1"},{"section":"dns01solverconfig-v1alpha1"},{"section":"certificatednsnameselector-v1alpha1"},{"section":"certificatecondition-v1alpha1"},{"section":"caissuer-v1alpha1"},{"section":"acmeissuerhttp01config-v1alpha1"},{"section":"acmeissuerdns01providerwebhook-v1alpha1"},{"section":"acmeissuerdns01providerroute53-v1alpha1"},{"section":"acmeissuerdns01providerrfc2136-v1alpha1"},{"section":"acmeissuerdns01providerdigitalocean-v1alpha1"},{"section":"acmeissuerdns01providercloudflare-v1alpha1"},{"section":"acmeissuerdns01providerclouddns-v1alpha1"},{"section":"acmeissuerdns01providerazuredns-v1alpha1"},{"section":"acmeissuerdns01providerakamai-v1alpha1"},{"section":"acmeissuerdns01provideracmedns-v1alpha1"},{"section":"acmeissuerdns01provider-v1alpha1"},{"section":"acmeissuerdns01config-v1alpha1"},{"section":"acmeissuer-v1alpha1"},{"section":"acmechallengesolverhttp01ingresspodtemplate-v1alpha1"},{"section":"acmechallengesolverhttp01ingress-v1alpha1"},{"section":"acmechallengesolverhttp01-v1alpha1"},{"section":"acmechallengesolverdns01-v1alpha1"},{"section":"acmechallengesolver-v1alpha1"},{"section":"acmecertificateconfig-v1alpha1"}]},{"section":"-strong-old-api-versions-strong-","subsections":[]},{"section":"challenge-v1alpha1","subsections":[]},{"section":"order-v1alpha1","subsections":[]},{"section":"issuer-v1alpha1","subsections":[]},{"section":"clusterissuer-v1alpha1","subsections":[]},{"section":"certificate-v1alpha1","subsections":[]},{"section":"-strong-cert-manager-strong-","subsections":[]}],"flatToc":["venafitpp-v1alpha1","venafiissuer-v1alpha1","venaficloud-v1alpha1","vaultissuer-v1alpha1","vaultauth-v1alpha1","vaultapprole-v1alpha1","time-v1","statusdetails-v1","statuscause-v1","status-v1","solverconfig-v1alpha1","selfsignedissuer-v1alpha1","secretkeyselector-v1alpha1","ownerreference-v1","objectreference-v1alpha1","objectmeta-v1","managedfieldsentry-v1","localobjectreference-v1alpha1","listmeta-v1","issuercondition-v1alpha1","initializers-v1","initializer-v1","http01solverconfig-v1alpha1","fields-v1","duration-v1","domainsolverconfig-v1alpha1","dns01solverconfig-v1alpha1","certificatednsnameselector-v1alpha1","certificatecondition-v1alpha1","caissuer-v1alpha1","acmeissuerhttp01config-v1alpha1","acmeissuerdns01providerwebhook-v1alpha1","acmeissuerdns01providerroute53-v1alpha1","acmeissuerdns01providerrfc2136-v1alpha1","acmeissuerdns01providerdigitalocean-v1alpha1","acmeissuerdns01providercloudflare-v1alpha1","acmeissuerdns01providerclouddns-v1alpha1","acmeissuerdns01providerazuredns-v1alpha1","acmeissuerdns01providerakamai-v1alpha1","acmeissuerdns01provideracmedns-v1alpha1","acmeissuerdns01provider-v1alpha1","acmeissuerdns01config-v1alpha1","acmeissuer-v1alpha1","acmechallengesolverhttp01ingresspodtemplate-v1alpha1","acmechallengesolverhttp01ingress-v1alpha1","acmechallengesolverhttp01-v1alpha1","acmechallengesolverdns01-v1alpha1","acmechallengesolver-v1alpha1","acmecertificateconfig-v1alpha1","-strong-field-definitions-strong-","-strong-old-api-versions-strong-","challenge-v1alpha1","order-v1alpha1","issuer-v1alpha1","clusterissuer-v1alpha1","certificate-v1alpha1","-strong-cert-manager-strong-"]};})();
(function(){navData = {"toc":[{"section":"-strong-field-definitions-strong-","subsections":[{"section":"venafitpp-v1alpha1"},{"section":"venafiissuer-v1alpha1"},{"section":"venaficloud-v1alpha1"},{"section":"vaultissuer-v1alpha1"},{"section":"vaultauth-v1alpha1"},{"section":"vaultapprole-v1alpha1"},{"section":"time-v1"},{"section":"statusdetails-v1"},{"section":"statuscause-v1"},{"section":"status-v1"},{"section":"solverconfig-v1alpha1"},{"section":"selfsignedissuer-v1alpha1"},{"section":"secretkeyselector-v1alpha1"},{"section":"ownerreference-v1"},{"section":"objectreference-v1alpha1"},{"section":"objectmeta-v1"},{"section":"managedfieldsentry-v1"},{"section":"localobjectreference-v1alpha1"},{"section":"listmeta-v1"},{"section":"issuercondition-v1alpha1"},{"section":"initializers-v1"},{"section":"initializer-v1"},{"section":"http01solverconfig-v1alpha1"},{"section":"fields-v1"},{"section":"duration-v1"},{"section":"domainsolverconfig-v1alpha1"},{"section":"dns01solverconfig-v1alpha1"},{"section":"certificatednsnameselector-v1alpha1"},{"section":"certificatecondition-v1alpha1"},{"section":"caissuer-v1alpha1"},{"section":"acmeissuerhttp01config-v1alpha1"},{"section":"acmeissuerdns01providerwebhook-v1alpha1"},{"section":"acmeissuerdns01providerroute53-v1alpha1"},{"section":"acmeissuerdns01providerrfc2136-v1alpha1"},{"section":"acmeissuerdns01providerdigitalocean-v1alpha1"},{"section":"acmeissuerdns01providercloudflare-v1alpha1"},{"section":"acmeissuerdns01providerclouddns-v1alpha1"},{"section":"acmeissuerdns01providerazuredns-v1alpha1"},{"section":"acmeissuerdns01providerakamai-v1alpha1"},{"section":"acmeissuerdns01provideracmedns-v1alpha1"},{"section":"acmeissuerdns01provider-v1alpha1"},{"section":"acmeissuerdns01config-v1alpha1"},{"section":"acmeissuer-v1alpha1"},{"section":"acmechallengesolverhttp01ingresspodtemplate-v1alpha1"},{"section":"acmechallengesolverhttp01ingresspodspec-v1alpha1"},{"section":"acmechallengesolverhttp01ingress-v1alpha1"},{"section":"acmechallengesolverhttp01-v1alpha1"},{"section":"acmechallengesolverdns01-v1alpha1"},{"section":"acmechallengesolver-v1alpha1"},{"section":"acmecertificateconfig-v1alpha1"}]},{"section":"-strong-old-api-versions-strong-","subsections":[]},{"section":"challenge-v1alpha1","subsections":[]},{"section":"order-v1alpha1","subsections":[]},{"section":"issuer-v1alpha1","subsections":[]},{"section":"clusterissuer-v1alpha1","subsections":[]},{"section":"certificate-v1alpha1","subsections":[]},{"section":"-strong-cert-manager-strong-","subsections":[]}],"flatToc":["venafitpp-v1alpha1","venafiissuer-v1alpha1","venaficloud-v1alpha1","vaultissuer-v1alpha1","vaultauth-v1alpha1","vaultapprole-v1alpha1","time-v1","statusdetails-v1","statuscause-v1","status-v1","solverconfig-v1alpha1","selfsignedissuer-v1alpha1","secretkeyselector-v1alpha1","ownerreference-v1","objectreference-v1alpha1","objectmeta-v1","managedfieldsentry-v1","localobjectreference-v1alpha1","listmeta-v1","issuercondition-v1alpha1","initializers-v1","initializer-v1","http01solverconfig-v1alpha1","fields-v1","duration-v1","domainsolverconfig-v1alpha1","dns01solverconfig-v1alpha1","certificatednsnameselector-v1alpha1","certificatecondition-v1alpha1","caissuer-v1alpha1","acmeissuerhttp01config-v1alpha1","acmeissuerdns01providerwebhook-v1alpha1","acmeissuerdns01providerroute53-v1alpha1","acmeissuerdns01providerrfc2136-v1alpha1","acmeissuerdns01providerdigitalocean-v1alpha1","acmeissuerdns01providercloudflare-v1alpha1","acmeissuerdns01providerclouddns-v1alpha1","acmeissuerdns01providerazuredns-v1alpha1","acmeissuerdns01providerakamai-v1alpha1","acmeissuerdns01provideracmedns-v1alpha1","acmeissuerdns01provider-v1alpha1","acmeissuerdns01config-v1alpha1","acmeissuer-v1alpha1","acmechallengesolverhttp01ingresspodtemplate-v1alpha1","acmechallengesolverhttp01ingresspodspec-v1alpha1","acmechallengesolverhttp01ingress-v1alpha1","acmechallengesolverhttp01-v1alpha1","acmechallengesolverdns01-v1alpha1","acmechallengesolver-v1alpha1","acmecertificateconfig-v1alpha1","-strong-field-definitions-strong-","-strong-old-api-versions-strong-","challenge-v1alpha1","order-v1alpha1","issuer-v1alpha1","clusterissuer-v1alpha1","certificate-v1alpha1","-strong-cert-manager-strong-"]};})();

View File

@ -72,31 +72,40 @@ 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`. No other
metadata fields can be edited. Below shows how an issuer that is configured to
add some labels and annotations to solver pods.
These can be configured under the ``metadata`` field under ``podTemplate``.
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:
.. code-block:: yaml
:linenos:
:emphasize-lines: 13-20
apiVersion: certmanager.k8s.io/v1alpha1
kind: Issuer
metadata:
apiVersion: certmanager.k8s.io/v1alpha1
kind: Issuer
metadata:
name: ...
spec:
acme:
server: ...
privateKeySecretRef:
name: ...
spec:
acme:
server: ...
privateKeySecretRef:
name: ...
solvers:
- http01:
ingress:
podTemplate:
metadata:
labels:
foo: "bar"
env: "prod"
annotations:
my: "annotation"
solvers:
- http01:
ingress:
podTemplate:
metadata:
labels:
foo: "bar"
env: "prod"
spec:
nodeSelector:
bar: baz
The added labels and annotations will merge on top of the cert-manager defaults,
overriding entries with the same key.
No other fields can be edited.

View File

@ -298,15 +298,40 @@ 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 values, the values here
// will override the in-built values.
// +optional
metav1.ObjectMeta `json:"metadata,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.
// +optional
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 {

View File

@ -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,10 +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.Spec.DeepCopyInto(&out.Spec)
return
}
@ -730,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
@ -795,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 {

View File

@ -153,7 +153,7 @@ func ValidateACMEIssuerChallengeSolverHTTP01IngressPodTemplateConfig(podTempl *v
cpyPodTempl.Annotations = nil
if !reflect.DeepEqual(cpyPodTempl.ObjectMeta, metav1.ObjectMeta{}) {
el = append(el, field.Invalid(fldPath.Child("metadata"), "", "only labels and annotations may be set on podTemplate"))
el = append(el, field.Invalid(fldPath.Child("metadata"), "", "only labels and annotations may be set on podTemplate metadata"))
}
return el

View File

@ -200,7 +200,7 @@ func TestValidateACMEIssuerConfig(t *testing.T) {
field.Invalid(fldPath.Child("http01", "serviceType"), corev1.ServiceType("InvalidServiceType"), "optional field serviceType must be one of [\"ClusterIP\" \"NodePort\"]"),
},
},
"acme issue with valid pod template attributes": {
"acme issue with valid pod template ObjectMeta attributes": {
spec: &v1alpha1.ACMEIssuer{
Email: "valid-email",
Server: "valid-server",
@ -253,7 +253,69 @@ func TestValidateACMEIssuerConfig(t *testing.T) {
},
errs: []*field.Error{
field.Invalid(fldPath.Child("solver", "http01", "ingress", "podTemplate", "metadata"),
"", "only labels and annotations may be set on podTemplate"),
"", "only labels and annotations may be set on podTemplate metadata"),
},
},
"acme issue with valid 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{
Spec: v1alpha1.ACMEChallengeSolverHTTP01IngressPodSpec{
NodeSelector: map[string]string{
"valid_to_contain": "nodeSelector",
},
Tolerations: []corev1.Toleration{
{
Key: "valid_key",
Operator: "Exists",
Effect: "NoSchedule",
},
},
},
},
},
},
},
},
HTTP01: &v1alpha1.ACMEIssuerHTTP01Config{
ServiceType: corev1.ServiceType("NodePort"),
},
},
},
"acme issue with valid pod template ObjectMeta and 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{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"valid_to_contain": "labels",
},
},
Spec: v1alpha1.ACMEChallengeSolverHTTP01IngressPodSpec{
NodeSelector: map[string]string{
"valid_to_contain": "nodeSelector",
},
},
},
},
},
},
},
HTTP01: &v1alpha1.ACMEIssuerHTTP01Config{
ServiceType: corev1.ServiceType("NodePort"),
},
},
},
}

View File

@ -224,5 +224,25 @@ func (s *Solver) mergePodObjectMetaWithPodTemplate(pod *corev1.Pod, podTempl *v1
pod.Annotations[k] = v
}
if pod.Spec.NodeSelector == nil {
pod.Spec.NodeSelector = make(map[string]string)
}
for k, v := range podTempl.Spec.NodeSelector {
pod.Spec.NodeSelector[k] = v
}
if pod.Spec.Tolerations == nil {
pod.Spec.Tolerations = []corev1.Toleration{}
}
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
}

View File

@ -278,6 +278,18 @@ func TestMergePodObjectMetaWithPodTemplate(t *testing.T) {
"foo": "bar",
},
},
Spec: v1alpha1.ACMEChallengeSolverHTTP01IngressPodSpec{
NodeSelector: map[string]string{
"node": "selector",
},
Tolerations: []v1.Toleration{
{
Key: "key",
Operator: "Exists",
Effect: "NoSchedule",
},
},
},
},
},
},
@ -296,6 +308,16 @@ func TestMergePodObjectMetaWithPodTemplate(t *testing.T) {
"sidecar.istio.io/inject": "true",
"foo": "bar",
}
resultingPod.Spec.NodeSelector = map[string]string{
"node": "selector",
}
resultingPod.Spec.Tolerations = []v1.Toleration{
{
Key: "key",
Operator: "Exists",
Effect: "NoSchedule",
},
}
s.testResources[createdPodKey] = resultingPod
s.Builder.Sync()