Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add secretTemplate to Certificate resources created by ingress-shim #6839

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions pkg/apis/certmanager/v1/types.go
Expand Up @@ -145,6 +145,10 @@ const (
// controller only processes Ingresses with this annotation either unset, or
// set to either the configured value or the empty string.
IngressClassAnnotationKey = "kubernetes.io/ingress.class"

// IngressSecretTemplate can be used to set the secretTemplate field in the generated Certificate.
// The value is a JSON representation of secretTemplate and must not have any unknown fields.
IngressSecretTemplate = "cert-manager.io/secret-template"
)

// Annotation names for CertificateRequests
Expand Down
19 changes: 19 additions & 0 deletions pkg/controller/certificate-shim/helper.go
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package shimhelper

import (
"encoding/json"
"errors"
"fmt"
"reflect"
Expand Down Expand Up @@ -268,5 +269,23 @@ func translateAnnotations(crt *cmapi.Certificate, ingLikeAnnotations map[string]
}
}

if secretTemplateJson, found := ingLikeAnnotations[cmapi.IngressSecretTemplate]; found {
decoder := json.NewDecoder(strings.NewReader(secretTemplateJson))
decoder.DisallowUnknownFields()

var secretTemplate = new(cmapi.CertificateSecretTemplate)
if err := decoder.Decode(secretTemplate); err != nil {
return fmt.Errorf("%w %q: error parsing secret template JSON: %v", errInvalidIngressAnnotation, cmapi.IngressSecretTemplate, err)
}
for annotationKey := range secretTemplate.Annotations {
if strings.HasPrefix(annotationKey, "cert-manager.io/") {
return fmt.Errorf("%w %q: secretTemplate must not have cert-manager.io/ annotations: %q", errInvalidIngressAnnotation, cmapi.IngressSecretTemplate, annotationKey)
}
}
if len(secretTemplate.Annotations) > 0 || len(secretTemplate.Labels) > 0 {
crt.Spec.SecretTemplate = secretTemplate
}
}

return nil
}
99 changes: 99 additions & 0 deletions pkg/controller/certificate-shim/sync_test.go
Expand Up @@ -536,6 +536,105 @@ func TestSync(t *testing.T) {
},
},
},
{
Name: "return a single HTTP01 Certificate for an ingress with a single valid TLS entry and valid secret template annotation",
Issuer: acmeClusterIssuer,
IngressLike: &networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "ingress-name",
Namespace: gen.DefaultTestNamespace,
Annotations: map[string]string{
cmapi.IngressClusterIssuerNameAnnotationKey: "issuer-name",
cmapi.IngressSecretTemplate: `{ "annotations": { "example-annotation" : "dummy-value" }, "labels": { "example-label" : "dummy-value" } }`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it will be easier to read and create these annotations if there was a separate annotation for SecretTemplateAnnotations and SecretTemplateLabels.

},
UID: types.UID("ingress-name"),
},
Spec: networkingv1.IngressSpec{
TLS: []networkingv1.IngressTLS{
{
Hosts: []string{"example.com", "www.example.com"},
SecretName: "example-com-tls",
},
},
},
},
ClusterIssuerLister: []runtime.Object{acmeClusterIssuer},
ExpectedEvents: []string{`Normal CreateCertificate Successfully created Certificate "example-com-tls"`},
ExpectedCreate: []*cmapi.Certificate{
{
ObjectMeta: metav1.ObjectMeta{
Name: "example-com-tls",
Namespace: gen.DefaultTestNamespace,
OwnerReferences: buildIngressOwnerReferences("ingress-name", gen.DefaultTestNamespace),
},
Spec: cmapi.CertificateSpec{
DNSNames: []string{"example.com", "www.example.com"},
SecretName: "example-com-tls",
SecretTemplate: &cmapi.CertificateSecretTemplate{
Annotations: map[string]string{
"example-annotation": "dummy-value",
},
Labels: map[string]string{
"example-label": "dummy-value",
},
},
IssuerRef: cmmeta.ObjectReference{
Name: "issuer-name",
Kind: "ClusterIssuer",
},
Usages: cmapi.DefaultKeyUsages(),
},
},
},
},
{
Name: "secret template annotation should not allow cert-manager.io/ annotations",
Issuer: acmeClusterIssuer,
IngressLike: &networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "ingress-name",
Namespace: gen.DefaultTestNamespace,
Annotations: map[string]string{
cmapi.IngressClusterIssuerNameAnnotationKey: "issuer-name",
cmapi.IngressSecretTemplate: `{ "annotations": { "cert-manager.io/disallowed-annotation" : "dummy-value" } }`,
},
UID: types.UID("ingress-name"),
},
Spec: networkingv1.IngressSpec{
TLS: []networkingv1.IngressTLS{
{
Hosts: []string{"example.com", "www.example.com"},
SecretName: "example-com-tls",
},
},
},
},
Err: true,
},
{
Name: "secret template annotation should not allow unknown fields",
Issuer: acmeClusterIssuer,
IngressLike: &networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "ingress-name",
Namespace: gen.DefaultTestNamespace,
Annotations: map[string]string{
cmapi.IngressClusterIssuerNameAnnotationKey: "issuer-name",
cmapi.IngressSecretTemplate: `{ "unknown-field": "true" }`,
},
UID: types.UID("ingress-name"),
},
Spec: networkingv1.IngressSpec{
TLS: []networkingv1.IngressTLS{
{
Hosts: []string{"example.com", "www.example.com"},
SecretName: "example-com-tls",
},
},
},
},
Err: true,
},
{
Name: "edit-in-place set to false should not trigger editing the ingress in-place",
Issuer: acmeClusterIssuer,
Expand Down