diff --git a/test/e2e/framework/helper/featureset/featureset.go b/test/e2e/framework/helper/featureset/featureset.go index 31c2868c5..095eef464 100644 --- a/test/e2e/framework/helper/featureset/featureset.go +++ b/test/e2e/framework/helper/featureset/featureset.go @@ -32,9 +32,12 @@ func NewFeatureSet(feats ...Feature) FeatureSet { // just defines a grouping of features (i.e. a 'set'). type FeatureSet map[Feature]struct{} -// Add adds a feature to the set -func (fs FeatureSet) Add(f Feature) { - fs[f] = struct{}{} +// Add adds features to the set +func (fs FeatureSet) Add(f ...Feature) FeatureSet { + for _, feat := range f { + fs[feat] = struct{}{} + } + return fs } // Delete removes a feature from the set @@ -48,6 +51,25 @@ func (fs FeatureSet) Contains(f Feature) bool { return ok } +// Copy returns a new copy of an existing Feature Set. +// It is not safe to be called by multiple goroutines. +func (fs FeatureSet) Copy() FeatureSet { + new := make(FeatureSet) + for k, v := range fs { + new[k] = v + } + return new +} + +// List returns a slice of all features in the set. +func (fs FeatureSet) List() []Feature { + var ret []Feature + for k := range fs { + ret = append(ret, k) + } + return ret +} + // String returns this FeatureSet as a comma separated string func (fs FeatureSet) String() string { featsSlice := make([]string, len(fs)) @@ -131,4 +153,8 @@ const ( // IssueCAFeature denotes whether the target issuer is able to issue CA // certificates (i.e., certificates for which the CA basicConstraint is true) IssueCAFeature Feature = "IssueCA" + + // LongDomainFeatureSet denotes whether the target issuer is able to sign + // a certificate that defines a long domain + LongDomainFeatureSet Feature = "LongDomain" ) diff --git a/test/e2e/suite/conformance/certificates/acme/acme.go b/test/e2e/suite/conformance/certificates/acme/acme.go index acfe2aa34..ed9a6c328 100644 --- a/test/e2e/suite/conformance/certificates/acme/acme.go +++ b/test/e2e/suite/conformance/certificates/acme/acme.go @@ -19,6 +19,7 @@ package acme import ( "context" "encoding/base64" + "strings" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -70,6 +71,14 @@ func runACMEIssuerTests(eab *cmacme.ACMEExternalAccountBinding) { featureset.IssueCAFeature, ) + // UnsupportedPublicACMEServerFeatures are additional ACME features not supported by + // public ACME servers + var unsupportedPublicACMEServerFeatures = featureset.NewFeatureSet( + featureset.IPAddressFeature, + featureset.Ed25519FeatureSet, + featureset.LongDomainFeatureSet, + ) + provisionerHTTP01 := &acmeIssuerProvisioner{ eab: eab, } @@ -78,6 +87,10 @@ func runACMEIssuerTests(eab *cmacme.ACMEExternalAccountBinding) { eab: eab, } + provisionerPACMEHTTP01 := &acmeIssuerProvisioner{ + eab: nil, + } + (&certificates.Suite{ Name: "ACME HTTP01 Issuer", UseIngressIPAddress: true, @@ -109,6 +122,14 @@ func runACMEIssuerTests(eab *cmacme.ACMEExternalAccountBinding) { DeleteIssuerFunc: provisionerDNS01.delete, UnsupportedFeatures: unsupportedDNS01Features, }).Define() + + (&certificates.Suite{ + Name: "Public ACME Server HTTP01 Issuer", + UseIngressIPAddress: true, + CreateIssuerFunc: provisionerPACMEHTTP01.createPublicACMEServerStagingHTTP01Issuer, + DeleteIssuerFunc: provisionerPACMEHTTP01.delete, + UnsupportedFeatures: unsupportedHTTP01Features.Copy().Add(unsupportedPublicACMEServerFeatures.List()...), + }).Define() } type acmeIssuerProvisioner struct { @@ -155,6 +176,33 @@ func (a *acmeIssuerProvisioner) createHTTP01Issuer(f *framework.Framework) cmmet } } +func (a *acmeIssuerProvisioner) createPublicACMEServerStagingHTTP01Issuer(f *framework.Framework) cmmeta.ObjectReference { + By("Creating a Public ACME Server Staging HTTP01 Issuer") + + var PublicACMEServerStagingURL string + if strings.Contains(f.Config.Addons.ACMEServer.URL, "pebble") { + PublicACMEServerStagingURL = "https://acme-staging-v02.api.letsencrypt.org/directory" + } else { + PublicACMEServerStagingURL = f.Config.Addons.ACMEServer.URL + } + + issuer := &cmapi.Issuer{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "pacme-issuer-http01-", + }, + Spec: a.createHTTP01IssuerSpec(PublicACMEServerStagingURL), + } + + issuer, err := f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), issuer, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred(), "failed to create Public ACME Server Staging HTTP01 issuer") + + return cmmeta.ObjectReference{ + Group: cmapi.SchemeGroupVersion.Group, + Kind: cmapi.IssuerKind, + Name: issuer.Name, + } +} + func (a *acmeIssuerProvisioner) createHTTP01ClusterIssuer(f *framework.Framework) cmmeta.ObjectReference { a.ensureEABSecret(f, f.Config.Addons.CertManager.ClusterResourceNamespace) diff --git a/test/e2e/suite/conformance/certificates/tests.go b/test/e2e/suite/conformance/certificates/tests.go index 81f9bc988..1d16851dc 100644 --- a/test/e2e/suite/conformance/certificates/tests.go +++ b/test/e2e/suite/conformance/certificates/tests.go @@ -18,6 +18,7 @@ package certificates import ( "context" + "strings" "time" . "github.com/onsi/ginkgo" @@ -54,10 +55,15 @@ func (s *Suite) Define() { // Wrap this in a BeforeEach else flags will not have been parsed and // f.Config will not be populated at the time that this code is run. BeforeEach(func() { + // Special case Public ACME Servers against being run in the standard + // e2e tests. + if strings.Contains(s.Name, "Public ACME Server") && strings.Contains(f.Config.Addons.ACMEServer.URL, "pebble") { + Skip("Not running public ACME tests against local cluster.") + return + } if s.completed { return } - s.complete(f) if s.UseIngressIPAddress { @@ -840,7 +846,7 @@ func (s *Suite) Define() { By("Sanity-check the issued Certificate") err = f.Helper().ValidateCertificate(f.Namespace.Name, "testcert", validations...) Expect(err).NotTo(HaveOccurred()) - }, featureset.OnlySAN) + }, featureset.OnlySAN, featureset.LongDomainFeatureSet) s.it(f, "should allow updating an existing certificate with a new DNS Name", func(issuerRef cmmeta.ObjectReference) { testCertificate := &cmapi.Certificate{