Adds base CertificateSigningRequest cert-manager controller
Signed-off-by: joshvanl <vleeuwenjoshua@gmail.com>
This commit is contained in:
parent
b38519fe66
commit
c5c206cace
@ -6,6 +6,7 @@ go_library(
|
||||
"conditions.go",
|
||||
"duration.go",
|
||||
"issuers.go",
|
||||
"kube.go",
|
||||
"names.go",
|
||||
"usages.go",
|
||||
],
|
||||
@ -15,6 +16,7 @@ go_library(
|
||||
"//pkg/apis/certmanager/v1:go_default_library",
|
||||
"//pkg/apis/meta/v1:go_default_library",
|
||||
"//pkg/logs:go_default_library",
|
||||
"@io_k8s_api//certificates/v1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
|
||||
"@io_k8s_utils//clock:go_default_library",
|
||||
],
|
||||
|
||||
@ -52,6 +52,7 @@ filegroup(
|
||||
"//pkg/controller/cainjector:all-srcs",
|
||||
"//pkg/controller/certificaterequests:all-srcs",
|
||||
"//pkg/controller/certificates:all-srcs",
|
||||
"//pkg/controller/certificatesigningrequests:all-srcs",
|
||||
"//pkg/controller/clusterissuers:all-srcs",
|
||||
"//pkg/controller/ingress-shim:all-srcs",
|
||||
"//pkg/controller/issuers:all-srcs",
|
||||
|
||||
77
pkg/controller/certificatesigningrequests/BUILD.bazel
Normal file
77
pkg/controller/certificatesigningrequests/BUILD.bazel
Normal file
@ -0,0 +1,77 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"checks.go",
|
||||
"controller.go",
|
||||
"sync.go",
|
||||
],
|
||||
importpath = "github.com/jetstack/cert-manager/pkg/controller/certificatesigningrequests",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/api/util:go_default_library",
|
||||
"//pkg/apis/certmanager:go_default_library",
|
||||
"//pkg/apis/certmanager/v1:go_default_library",
|
||||
"//pkg/apis/meta/v1:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/controller/certificatesigningrequests/util:go_default_library",
|
||||
"//pkg/issuer:go_default_library",
|
||||
"//pkg/logs:go_default_library",
|
||||
"@com_github_go_logr_logr//:go_default_library",
|
||||
"@io_k8s_api//authorization/v1:go_default_library",
|
||||
"@io_k8s_api//certificates/v1:go_default_library",
|
||||
"@io_k8s_api//core/v1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/api/errors:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/labels:go_default_library",
|
||||
"@io_k8s_client_go//kubernetes/typed/authorization/v1:go_default_library",
|
||||
"@io_k8s_client_go//kubernetes/typed/certificates/v1:go_default_library",
|
||||
"@io_k8s_client_go//listers/certificates/v1:go_default_library",
|
||||
"@io_k8s_client_go//tools/cache:go_default_library",
|
||||
"@io_k8s_client_go//tools/record:go_default_library",
|
||||
"@io_k8s_client_go//util/workqueue:go_default_library",
|
||||
"@io_k8s_utils//clock:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["controller_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/api/util:go_default_library",
|
||||
"//pkg/apis/certmanager/v1:go_default_library",
|
||||
"//pkg/apis/meta/v1:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/controller/certificatesigningrequests/fake:go_default_library",
|
||||
"//pkg/controller/certificatesigningrequests/util:go_default_library",
|
||||
"//pkg/controller/test:go_default_library",
|
||||
"//test/unit/gen:go_default_library",
|
||||
"@io_k8s_api//authorization/v1:go_default_library",
|
||||
"@io_k8s_api//certificates/v1:go_default_library",
|
||||
"@io_k8s_api//core/v1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/runtime:go_default_library",
|
||||
"@io_k8s_client_go//testing:go_default_library",
|
||||
"@io_k8s_utils//clock/testing:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/controller/certificatesigningrequests/fake:all-srcs",
|
||||
"//pkg/controller/certificatesigningrequests/util:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
83
pkg/controller/certificatesigningrequests/checks.go
Normal file
83
pkg/controller/certificatesigningrequests/checks.go
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
Copyright 2021 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package certificatesigningrequests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
certificatesv1 "k8s.io/api/certificates/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/apis/certmanager"
|
||||
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
|
||||
"github.com/jetstack/cert-manager/pkg/controller/certificatesigningrequests/util"
|
||||
logf "github.com/jetstack/cert-manager/pkg/logs"
|
||||
)
|
||||
|
||||
func (c *Controller) handleGenericIssuer(obj interface{}) {
|
||||
log := c.log.WithName("handleGenericIssuer")
|
||||
|
||||
iss, ok := obj.(cmapi.GenericIssuer)
|
||||
if !ok {
|
||||
log.Error(nil, "object does not implement GenericIssuer")
|
||||
return
|
||||
}
|
||||
|
||||
log = logf.WithResource(log, iss)
|
||||
crs, err := c.certificateSigningRequestsForGenericIssuer(iss)
|
||||
if err != nil {
|
||||
log.Error(err, "error looking up certificates observing issuer or clusterissuer")
|
||||
return
|
||||
}
|
||||
for _, cr := range crs {
|
||||
log := logf.WithRelatedResource(log, cr)
|
||||
key, err := keyFunc(cr)
|
||||
if err != nil {
|
||||
log.Error(err, "error computing key for resource")
|
||||
continue
|
||||
}
|
||||
c.queue.Add(key)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) certificateSigningRequestsForGenericIssuer(iss cmapi.GenericIssuer) ([]*certificatesv1.CertificateSigningRequest, error) {
|
||||
csrs, err := c.csrLister.List(labels.NewSelector())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing certificates signing requests: %s", err.Error())
|
||||
}
|
||||
|
||||
_, isClusterIssuer := iss.(*cmapi.ClusterIssuer)
|
||||
|
||||
var affected []*certificatesv1.CertificateSigningRequest
|
||||
for _, csr := range csrs {
|
||||
ref, ok := util.SignerIssuerRefFromSignerName(csr.Spec.SignerName)
|
||||
|
||||
switch {
|
||||
case !ok,
|
||||
ref.Group != certmanager.GroupName,
|
||||
iss.GetNamespace() != ref.Namespace,
|
||||
iss.GetName() != ref.Name,
|
||||
isClusterIssuer && ref.Type != "clusterissuers",
|
||||
!isClusterIssuer && ref.Type != "issuers":
|
||||
continue
|
||||
}
|
||||
|
||||
affected = append(affected, csr)
|
||||
}
|
||||
|
||||
return affected, nil
|
||||
}
|
||||
158
pkg/controller/certificatesigningrequests/controller.go
Normal file
158
pkg/controller/certificatesigningrequests/controller.go
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
Copyright 2021 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package certificatesigningrequests
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
certificatesv1 "k8s.io/api/certificates/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
authzclient "k8s.io/client-go/kubernetes/typed/authorization/v1"
|
||||
certificatesclient "k8s.io/client-go/kubernetes/typed/certificates/v1"
|
||||
certificateslisters "k8s.io/client-go/listers/certificates/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"k8s.io/utils/clock"
|
||||
|
||||
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
|
||||
controllerpkg "github.com/jetstack/cert-manager/pkg/controller"
|
||||
"github.com/jetstack/cert-manager/pkg/issuer"
|
||||
logf "github.com/jetstack/cert-manager/pkg/logs"
|
||||
)
|
||||
|
||||
const (
|
||||
ControllerName = "certificatesigningrequests"
|
||||
)
|
||||
|
||||
var keyFunc = controllerpkg.KeyFunc
|
||||
|
||||
// Signer is an implementation of a Kubernetes CertificateSigningRequest
|
||||
// signer, backed by a cert-manager Issuer.
|
||||
type Signer interface {
|
||||
Sign(context.Context, *certificatesv1.CertificateSigningRequest, cmapi.GenericIssuer) error
|
||||
}
|
||||
|
||||
type Controller struct {
|
||||
helper issuer.Helper
|
||||
|
||||
// clientset used to update CertificateSigningRequest API resources
|
||||
certClient certificatesclient.CertificateSigningRequestInterface
|
||||
csrLister certificateslisters.CertificateSigningRequestLister
|
||||
sarClient authzclient.SubjectAccessReviewInterface
|
||||
|
||||
queue workqueue.RateLimitingInterface
|
||||
|
||||
// logger to be used by this controller
|
||||
log logr.Logger
|
||||
|
||||
// used to record Events about resources to the API
|
||||
recorder record.EventRecorder
|
||||
|
||||
// Signer to call sign function
|
||||
signer Signer
|
||||
|
||||
// the signer kind to react to when a certificate signing request is synced
|
||||
signerType string
|
||||
|
||||
// used for testing
|
||||
clock clock.Clock
|
||||
}
|
||||
|
||||
func New(signerType string, signer Signer) *Controller {
|
||||
return &Controller{
|
||||
signerType: signerType,
|
||||
signer: signer,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitingInterface, []cache.InformerSynced, error) {
|
||||
// construct a new named logger to be reused throughout the controller
|
||||
c.log = logf.FromContext(ctx.RootContext, ControllerName)
|
||||
|
||||
// create a queue used to queue up items to be processed
|
||||
c.queue = workqueue.NewNamedRateLimitingQueue(controllerpkg.DefaultItemBasedRateLimiter(), ControllerName)
|
||||
|
||||
c.sarClient = ctx.Client.AuthorizationV1().SubjectAccessReviews()
|
||||
|
||||
issuerInformer := ctx.SharedInformerFactory.Certmanager().V1().Issuers()
|
||||
|
||||
// obtain references to all the informers used by this controller
|
||||
csrInformer := ctx.KubeSharedInformerFactory.Certificates().V1().CertificateSigningRequests()
|
||||
|
||||
// build a list of InformerSynced functions that will be returned by the Register method.
|
||||
// the controller will only begin processing items once all of these informers have synced.
|
||||
mustSync := []cache.InformerSynced{
|
||||
csrInformer.Informer().HasSynced,
|
||||
issuerInformer.Informer().HasSynced,
|
||||
}
|
||||
|
||||
// if scoped to a single namespace
|
||||
// if we are running in non-namespaced mode (i.e. --namespace=""), we also
|
||||
// register event handlers and obtain a lister for clusterissuers.
|
||||
clusterIssuerInformer := ctx.SharedInformerFactory.Certmanager().V1().ClusterIssuers()
|
||||
if ctx.Namespace == "" {
|
||||
// register handler function for clusterissuer resources
|
||||
clusterIssuerInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{WorkFunc: c.handleGenericIssuer})
|
||||
mustSync = append(mustSync, clusterIssuerInformer.Informer().HasSynced)
|
||||
}
|
||||
|
||||
// set all the references to the listers for used by the Sync function
|
||||
c.csrLister = csrInformer.Lister()
|
||||
|
||||
// register handler functions
|
||||
csrInformer.Informer().AddEventHandler(&controllerpkg.QueuingEventHandler{Queue: c.queue})
|
||||
issuerInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{WorkFunc: c.handleGenericIssuer})
|
||||
|
||||
// create an issuer helper for reading generic issuers
|
||||
c.helper = issuer.NewHelper(issuerInformer.Lister(), clusterIssuerInformer.Lister())
|
||||
|
||||
c.clock = ctx.Clock
|
||||
// recorder records events about resources to the Kubernetes api
|
||||
c.recorder = ctx.Recorder
|
||||
c.certClient = ctx.Client.CertificatesV1().CertificateSigningRequests()
|
||||
|
||||
c.log.V(logf.DebugLevel).Info("new certificate signing request controller registered",
|
||||
"type", c.signerType)
|
||||
|
||||
return c.queue, mustSync, nil
|
||||
}
|
||||
|
||||
func (c *Controller) ProcessItem(ctx context.Context, key string) error {
|
||||
log := logf.FromContext(ctx)
|
||||
dbg := log.V(logf.DebugLevel)
|
||||
|
||||
_, name, err := cache.SplitMetaNamespaceKey(key)
|
||||
if err != nil {
|
||||
log.Error(err, "invalid resource key")
|
||||
return nil
|
||||
}
|
||||
|
||||
csr, err := c.csrLister.Get(name)
|
||||
if apierrors.IsNotFound(err) {
|
||||
dbg.Info("certificate request in work queue no longer exists", "error", err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx = logf.NewContext(ctx, logf.WithResource(log, csr))
|
||||
return c.Sync(ctx, csr)
|
||||
}
|
||||
674
pkg/controller/certificatesigningrequests/controller_test.go
Normal file
674
pkg/controller/certificatesigningrequests/controller_test.go
Normal file
@ -0,0 +1,674 @@
|
||||
/*
|
||||
Copyright 2021 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package certificatesigningrequests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
authzv1 "k8s.io/api/authorization/v1"
|
||||
certificatesv1 "k8s.io/api/certificates/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
coretesting "k8s.io/client-go/testing"
|
||||
fakeclock "k8s.io/utils/clock/testing"
|
||||
|
||||
apiutil "github.com/jetstack/cert-manager/pkg/api/util"
|
||||
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
|
||||
cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
|
||||
controllerpkg "github.com/jetstack/cert-manager/pkg/controller"
|
||||
"github.com/jetstack/cert-manager/pkg/controller/certificatesigningrequests/fake"
|
||||
"github.com/jetstack/cert-manager/pkg/controller/certificatesigningrequests/util"
|
||||
testpkg "github.com/jetstack/cert-manager/pkg/controller/test"
|
||||
"github.com/jetstack/cert-manager/test/unit/gen"
|
||||
)
|
||||
|
||||
func TestController(t *testing.T) {
|
||||
fixedClockStart := time.Now()
|
||||
fixedClock := fakeclock.NewFakeClock(fixedClockStart)
|
||||
metaFixedClockStart := metav1.NewTime(fixedClockStart)
|
||||
|
||||
signerExpectNoCall := func(t *testing.T) Signer {
|
||||
return &fake.Signer{
|
||||
FakeSign: func(context.Context, *certificatesv1.CertificateSigningRequest, cmapi.GenericIssuer) error {
|
||||
t.Fatal("unexpected sign call")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
sarReactionExpectNoCall := func(t *testing.T) coretesting.ReactionFunc {
|
||||
return func(_ coretesting.Action) (bool, runtime.Object, error) {
|
||||
t.Fatal("unexpected call")
|
||||
return true, nil, nil
|
||||
}
|
||||
}
|
||||
sarReactionAllow := func(t *testing.T) coretesting.ReactionFunc {
|
||||
return func(_ coretesting.Action) (bool, runtime.Object, error) {
|
||||
return true, &authzv1.SubjectAccessReview{
|
||||
Status: authzv1.SubjectAccessReviewStatus{
|
||||
Allowed: true,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
tests := map[string]struct {
|
||||
// key that should be passed to ProcessItem. If not set, the
|
||||
// 'namespace/name' of the 'CertificateSigningRequest' field will be used.
|
||||
// If neither is set, the key will be "".
|
||||
key string
|
||||
|
||||
// CertificateSigningRequest to be synced for the test. If not set, the
|
||||
// 'key' will be passed to ProcessItem instead.
|
||||
existingCSR *certificatesv1.CertificateSigningRequest
|
||||
|
||||
// If not nil, generic issuer object will be made available for the test
|
||||
existingIssuer runtime.Object
|
||||
|
||||
signerType string
|
||||
|
||||
signerImpl func(t *testing.T) Signer
|
||||
sarReaction func(t *testing.T) coretesting.ReactionFunc
|
||||
wantSARCreation []*authzv1.SubjectAccessReview
|
||||
|
||||
// wantEvent, if set, is an 'event string' that is expected to be fired.
|
||||
wantEvent string
|
||||
|
||||
// wantConditions is the expected set of conditions on the
|
||||
// CertificateSigningRequest resource if an Update is made.
|
||||
// If nil, no update is expected.
|
||||
// If empty, an update to the empty set/nil is expected.
|
||||
wantConditions []certificatesv1.CertificateSigningRequestCondition
|
||||
|
||||
wantErr bool
|
||||
}{
|
||||
"do nothing if an empty 'key' is used": {
|
||||
signerType: apiutil.IssuerCA,
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: sarReactionExpectNoCall,
|
||||
},
|
||||
"do nothing if an invalid 'key' is used": {
|
||||
key: "abc/def/ghi",
|
||||
signerType: apiutil.IssuerCA,
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: sarReactionExpectNoCall,
|
||||
},
|
||||
"do nothing if a key references a CertificateSigningRequest that does not exist": {
|
||||
key: "namespace/name",
|
||||
signerType: apiutil.IssuerCA,
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: sarReactionExpectNoCall,
|
||||
},
|
||||
"do nothing if a key references a CertificateSigningRequest that has a malformed SignerName for cert-manager.io": {
|
||||
signerType: apiutil.IssuerCA,
|
||||
existingCSR: gen.CertificateSigningRequest("csr-1",
|
||||
gen.SetCertificateSigningRequestSignerName("malformed.signer.name/"),
|
||||
),
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: sarReactionExpectNoCall,
|
||||
},
|
||||
"if CertificateSigningRequest references the cert-manager.io signer group but the type is not recognised, should ignore": {
|
||||
signerType: apiutil.IssuerCA,
|
||||
existingCSR: gen.CertificateSigningRequest("csr-1",
|
||||
gen.SetCertificateSigningRequestSignerName("foo.cert-manager.io/hello.world"),
|
||||
gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.CertificateApproved,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "ApprovedReason",
|
||||
Message: "Approved message",
|
||||
}),
|
||||
),
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: sarReactionExpectNoCall,
|
||||
},
|
||||
"do nothing if CertificateSigningRequest has a SignerName not for cert-manager.io": {
|
||||
signerType: apiutil.IssuerCA,
|
||||
existingCSR: gen.CertificateSigningRequest("csr-1",
|
||||
gen.SetCertificateSigningRequestSignerName("issuers.my-group.io/hello.world"),
|
||||
),
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: sarReactionExpectNoCall,
|
||||
},
|
||||
"do nothing if CertificateSigningRequest is marked as Failed": {
|
||||
signerType: apiutil.IssuerCA,
|
||||
existingCSR: gen.CertificateSigningRequest("csr-1",
|
||||
gen.SetCertificateSigningRequestSignerName("issuers.cert-manager.io/hello.world"),
|
||||
gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.CertificateFailed,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "FailedReason",
|
||||
Message: "Failed message",
|
||||
}),
|
||||
),
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: sarReactionExpectNoCall,
|
||||
},
|
||||
"do nothing if CertificateSigningRequest is no yet approved": {
|
||||
signerType: apiutil.IssuerCA,
|
||||
existingCSR: gen.CertificateSigningRequest("csr-1",
|
||||
gen.SetCertificateSigningRequestSignerName("issuers.cert-manager.io/hello.world"),
|
||||
),
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: sarReactionExpectNoCall,
|
||||
},
|
||||
"do nothing if CertificateSigningRequest already has a non empty Certificate present": {
|
||||
signerType: apiutil.IssuerCA,
|
||||
existingCSR: gen.CertificateSigningRequest("csr-1",
|
||||
gen.SetCertificateSigningRequestSignerName("issuers.cert-manager.io/hello.world"),
|
||||
gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.CertificateApproved,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "ApprovedReason",
|
||||
Message: "Approved message",
|
||||
}),
|
||||
gen.SetCertificateSigningRequestCertificate([]byte("non-empty-certificate")),
|
||||
),
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: sarReactionExpectNoCall,
|
||||
},
|
||||
"if CertificateSigningRequest references an Issuer that does not exist, should fire an event that it can't be found": {
|
||||
signerType: apiutil.IssuerCA,
|
||||
existingCSR: gen.CertificateSigningRequest("csr-1",
|
||||
gen.SetCertificateSigningRequestSignerName("issuers.cert-manager.io/hello.world"),
|
||||
gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.CertificateApproved,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "ApprovedReason",
|
||||
Message: "Approved message",
|
||||
}),
|
||||
),
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: sarReactionExpectNoCall,
|
||||
existingIssuer: nil,
|
||||
wantEvent: "Warning IssuerNotFound Referenced Issuer hello/world not found",
|
||||
},
|
||||
"if CertificateSigningRequest references an Issuer that does not yet have a type, should fire an event it doesn't have a type": {
|
||||
signerType: apiutil.IssuerCA,
|
||||
existingCSR: gen.CertificateSigningRequest("csr-1",
|
||||
gen.SetCertificateSigningRequestSignerName("issuers.cert-manager.io/hello.world"),
|
||||
gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.CertificateApproved,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "ApprovedReason",
|
||||
Message: "Approved message",
|
||||
}),
|
||||
),
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: sarReactionExpectNoCall,
|
||||
existingIssuer: gen.Issuer("world", gen.SetIssuerNamespace("hello")),
|
||||
wantEvent: "Warning IssuerTypeMissing Referenced Issuer hello/world is missing type",
|
||||
},
|
||||
"if CertificateSigningRequest references an Issuer which does not match the same signer type, should ignore": {
|
||||
signerType: apiutil.IssuerSelfSigned,
|
||||
existingCSR: gen.CertificateSigningRequest("csr-1",
|
||||
gen.SetCertificateSigningRequestSignerName("issuers.cert-manager.io/hello.world"),
|
||||
gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.CertificateApproved,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "ApprovedReason",
|
||||
Message: "Approved message",
|
||||
}),
|
||||
),
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: sarReactionExpectNoCall,
|
||||
existingIssuer: gen.Issuer("world", gen.SetIssuerNamespace("hello"),
|
||||
gen.SetIssuerCA(cmapi.CAIssuer{
|
||||
SecretName: "tls",
|
||||
}),
|
||||
),
|
||||
},
|
||||
"do nothing if CertificateSigningRequest references a signer that is not 'issuers' or 'clusterissuers'": {
|
||||
signerType: apiutil.IssuerCA,
|
||||
existingCSR: gen.CertificateSigningRequest("csr-1",
|
||||
gen.SetCertificateSigningRequestSignerName("not-issuers.cert-manager.io/hello.world"),
|
||||
gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.CertificateApproved,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "ApprovedReason",
|
||||
Message: "Approved message",
|
||||
}),
|
||||
),
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: sarReactionExpectNoCall,
|
||||
},
|
||||
"if CertificateSigningRequest references a issuers signer but the SubjectAccessReview errors, should error": {
|
||||
signerType: apiutil.IssuerCA,
|
||||
existingCSR: gen.CertificateSigningRequest("csr-1",
|
||||
gen.SetCertificateSigningRequestSignerName("issuers.cert-manager.io/hello.world"),
|
||||
gen.SetCertificateSigningRequestUsername("user-1"),
|
||||
gen.SetCertificateSigningRequestGroups([]string{"group-1", "group-2"}),
|
||||
gen.SetCertificateSigningRequestUID("uid-1"),
|
||||
gen.SetCertificateSigningRequestExtra(map[string]certificatesv1.ExtraValue{
|
||||
"extra": []string{"1", "2"},
|
||||
}),
|
||||
gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.CertificateApproved,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "ApprovedReason",
|
||||
Message: "Approved message",
|
||||
}),
|
||||
),
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: func(t *testing.T) coretesting.ReactionFunc {
|
||||
return func(_ coretesting.Action) (bool, runtime.Object, error) {
|
||||
return true, nil, errors.New("this is a simulated error")
|
||||
}
|
||||
},
|
||||
wantSARCreation: []*authzv1.SubjectAccessReview{
|
||||
{
|
||||
Spec: authzv1.SubjectAccessReviewSpec{
|
||||
User: "user-1",
|
||||
Groups: []string{"group-1", "group-2"},
|
||||
Extra: map[string]authzv1.ExtraValue{
|
||||
"extra": []string{"1", "2"},
|
||||
},
|
||||
UID: "uid-1",
|
||||
|
||||
ResourceAttributes: &authzv1.ResourceAttributes{
|
||||
Group: "cert-manager.io",
|
||||
Resource: "signers",
|
||||
Verb: "reference",
|
||||
Namespace: "hello",
|
||||
Name: "world",
|
||||
Version: "*",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
existingIssuer: gen.Issuer("world", gen.SetIssuerNamespace("hello"),
|
||||
gen.SetIssuerCA(cmapi.CAIssuer{
|
||||
SecretName: "tls",
|
||||
}),
|
||||
),
|
||||
wantErr: true,
|
||||
},
|
||||
"if CertificateSigningRequest references a issuers signer but the requesting user does not have permissions, should update Failed": {
|
||||
signerType: apiutil.IssuerCA,
|
||||
existingCSR: gen.CertificateSigningRequest("csr-1",
|
||||
gen.SetCertificateSigningRequestSignerName("issuers.cert-manager.io/hello.world"),
|
||||
gen.SetCertificateSigningRequestUsername("user-1"),
|
||||
gen.SetCertificateSigningRequestGroups([]string{"group-1", "group-2"}),
|
||||
gen.SetCertificateSigningRequestUID("uid-1"),
|
||||
gen.SetCertificateSigningRequestExtra(map[string]certificatesv1.ExtraValue{
|
||||
"extra": []string{"1", "2"},
|
||||
}),
|
||||
gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.CertificateApproved,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "ApprovedReason",
|
||||
Message: "Approved message",
|
||||
}),
|
||||
),
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: func(t *testing.T) coretesting.ReactionFunc {
|
||||
return func(_ coretesting.Action) (bool, runtime.Object, error) {
|
||||
return true, &authzv1.SubjectAccessReview{
|
||||
Status: authzv1.SubjectAccessReviewStatus{
|
||||
Allowed: false,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
},
|
||||
wantSARCreation: []*authzv1.SubjectAccessReview{
|
||||
{
|
||||
Spec: authzv1.SubjectAccessReviewSpec{
|
||||
User: "user-1",
|
||||
Groups: []string{"group-1", "group-2"},
|
||||
Extra: map[string]authzv1.ExtraValue{
|
||||
"extra": []string{"1", "2"},
|
||||
},
|
||||
UID: "uid-1",
|
||||
|
||||
ResourceAttributes: &authzv1.ResourceAttributes{
|
||||
Group: "cert-manager.io",
|
||||
Resource: "signers",
|
||||
Verb: "reference",
|
||||
Namespace: "hello",
|
||||
Name: "world",
|
||||
Version: "*",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Spec: authzv1.SubjectAccessReviewSpec{
|
||||
User: "user-1",
|
||||
Groups: []string{"group-1", "group-2"},
|
||||
Extra: map[string]authzv1.ExtraValue{
|
||||
"extra": []string{"1", "2"},
|
||||
},
|
||||
UID: "uid-1",
|
||||
|
||||
ResourceAttributes: &authzv1.ResourceAttributes{
|
||||
Group: "cert-manager.io",
|
||||
Resource: "signers",
|
||||
Verb: "reference",
|
||||
Namespace: "hello",
|
||||
Name: "*",
|
||||
Version: "*",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
existingIssuer: gen.Issuer("world", gen.SetIssuerNamespace("hello"),
|
||||
gen.SetIssuerCA(cmapi.CAIssuer{
|
||||
SecretName: "tls",
|
||||
}),
|
||||
),
|
||||
wantEvent: "Warning DeniedReference Requester may not reference Namespaced Issuer hello/world",
|
||||
wantConditions: []certificatesv1.CertificateSigningRequestCondition{
|
||||
{
|
||||
Type: certificatesv1.CertificateApproved,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "ApprovedReason",
|
||||
Message: "Approved message",
|
||||
},
|
||||
{
|
||||
Type: certificatesv1.CertificateFailed,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "DeniedReference",
|
||||
Message: "Requester may not reference Namespaced Issuer hello/world",
|
||||
LastTransitionTime: metaFixedClockStart,
|
||||
LastUpdateTime: metaFixedClockStart,
|
||||
},
|
||||
},
|
||||
},
|
||||
"if CertificateSigningRequest references a clusterissuers signer but the signer name contains a namespace, should update with failed": {
|
||||
signerType: apiutil.IssuerCA,
|
||||
existingCSR: gen.CertificateSigningRequest("csr-1",
|
||||
gen.SetCertificateSigningRequestSignerName("clusterissuers.cert-manager.io/hello.world"),
|
||||
gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.CertificateApproved,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "ApprovedReason",
|
||||
Message: "Approved message",
|
||||
}),
|
||||
),
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: sarReactionExpectNoCall,
|
||||
existingIssuer: gen.ClusterIssuer("world",
|
||||
gen.SetIssuerCA(cmapi.CAIssuer{
|
||||
SecretName: "tls",
|
||||
}),
|
||||
),
|
||||
wantEvent: "Warning BadSignerName Signer clusterissuers may not be referenced with namespace (hello)",
|
||||
wantConditions: []certificatesv1.CertificateSigningRequestCondition{
|
||||
{
|
||||
Type: certificatesv1.CertificateApproved,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "ApprovedReason",
|
||||
Message: "Approved message",
|
||||
},
|
||||
{
|
||||
Type: certificatesv1.CertificateFailed,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "BadSignerName",
|
||||
Message: "Signer clusterissuers may not be referenced with namespace (hello)",
|
||||
LastTransitionTime: metaFixedClockStart,
|
||||
LastUpdateTime: metaFixedClockStart,
|
||||
},
|
||||
},
|
||||
},
|
||||
"if CertificateSigningRequest references a issuers signer but the Issuer is not ready, fire event not Ready": {
|
||||
signerType: apiutil.IssuerCA,
|
||||
existingCSR: gen.CertificateSigningRequest("csr-1",
|
||||
gen.SetCertificateSigningRequestSignerName("issuers.cert-manager.io/hello.world"),
|
||||
gen.SetCertificateSigningRequestUsername("user-1"),
|
||||
gen.SetCertificateSigningRequestGroups([]string{"group-1", "group-2"}),
|
||||
gen.SetCertificateSigningRequestUID("uid-1"),
|
||||
gen.SetCertificateSigningRequestExtra(map[string]certificatesv1.ExtraValue{
|
||||
"extra": []string{"1", "2"},
|
||||
}),
|
||||
gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.CertificateApproved,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "ApprovedReason",
|
||||
Message: "Approved message",
|
||||
}),
|
||||
),
|
||||
signerImpl: signerExpectNoCall,
|
||||
sarReaction: sarReactionAllow,
|
||||
wantSARCreation: []*authzv1.SubjectAccessReview{
|
||||
{
|
||||
Spec: authzv1.SubjectAccessReviewSpec{
|
||||
User: "user-1",
|
||||
Groups: []string{"group-1", "group-2"},
|
||||
Extra: map[string]authzv1.ExtraValue{
|
||||
"extra": []string{"1", "2"},
|
||||
},
|
||||
UID: "uid-1",
|
||||
|
||||
ResourceAttributes: &authzv1.ResourceAttributes{
|
||||
Group: "cert-manager.io",
|
||||
Resource: "signers",
|
||||
Verb: "reference",
|
||||
Namespace: "hello",
|
||||
Name: "world",
|
||||
Version: "*",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantEvent: "Warning IssuerNotReady Referenced Issuer hello/world does not have a Ready status condition",
|
||||
existingIssuer: gen.Issuer("world", gen.SetIssuerNamespace("hello"),
|
||||
gen.SetIssuerCA(cmapi.CAIssuer{
|
||||
SecretName: "tls",
|
||||
}),
|
||||
),
|
||||
},
|
||||
"if CertificateSigningRequest called invoked sign but it errors, should return error": {
|
||||
signerType: apiutil.IssuerCA,
|
||||
existingCSR: gen.CertificateSigningRequest("csr-1",
|
||||
gen.SetCertificateSigningRequestSignerName("issuers.cert-manager.io/hello.world"),
|
||||
gen.SetCertificateSigningRequestUsername("user-1"),
|
||||
gen.SetCertificateSigningRequestGroups([]string{"group-1", "group-2"}),
|
||||
gen.SetCertificateSigningRequestUID("uid-1"),
|
||||
gen.SetCertificateSigningRequestExtra(map[string]certificatesv1.ExtraValue{
|
||||
"extra": []string{"1", "2"},
|
||||
}),
|
||||
gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.CertificateApproved,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "ApprovedReason",
|
||||
Message: "Approved message",
|
||||
}),
|
||||
),
|
||||
signerImpl: func(t *testing.T) Signer {
|
||||
return &fake.Signer{
|
||||
FakeSign: func(context.Context, *certificatesv1.CertificateSigningRequest, cmapi.GenericIssuer) error {
|
||||
return errors.New("this is a simulated error")
|
||||
},
|
||||
}
|
||||
},
|
||||
sarReaction: sarReactionAllow,
|
||||
wantSARCreation: []*authzv1.SubjectAccessReview{
|
||||
{
|
||||
Spec: authzv1.SubjectAccessReviewSpec{
|
||||
User: "user-1",
|
||||
Groups: []string{"group-1", "group-2"},
|
||||
Extra: map[string]authzv1.ExtraValue{
|
||||
"extra": []string{"1", "2"},
|
||||
},
|
||||
UID: "uid-1",
|
||||
|
||||
ResourceAttributes: &authzv1.ResourceAttributes{
|
||||
Group: "cert-manager.io",
|
||||
Resource: "signers",
|
||||
Verb: "reference",
|
||||
Namespace: "hello",
|
||||
Name: "world",
|
||||
Version: "*",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
existingIssuer: gen.Issuer("world", gen.SetIssuerNamespace("hello"),
|
||||
gen.SetIssuerCA(cmapi.CAIssuer{
|
||||
SecretName: "tls",
|
||||
}),
|
||||
gen.AddIssuerCondition(cmapi.IssuerCondition{
|
||||
Type: cmapi.IssuerConditionReady,
|
||||
Status: cmmeta.ConditionTrue,
|
||||
Reason: "IssuerReady",
|
||||
Message: "Issuer ready message",
|
||||
}),
|
||||
),
|
||||
wantErr: true,
|
||||
},
|
||||
"if CertificateSigningRequest called invoked sign and doesn't error, should return no error": {
|
||||
signerType: apiutil.IssuerCA,
|
||||
existingCSR: gen.CertificateSigningRequest("csr-1",
|
||||
gen.SetCertificateSigningRequestSignerName("issuers.cert-manager.io/hello.world"),
|
||||
gen.SetCertificateSigningRequestUsername("user-1"),
|
||||
gen.SetCertificateSigningRequestGroups([]string{"group-1", "group-2"}),
|
||||
gen.SetCertificateSigningRequestUID("uid-1"),
|
||||
gen.SetCertificateSigningRequestExtra(map[string]certificatesv1.ExtraValue{
|
||||
"extra": []string{"1", "2"},
|
||||
}),
|
||||
gen.SetCertificateSigningRequestStatusCondition(certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.CertificateApproved,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "ApprovedReason",
|
||||
Message: "Approved message",
|
||||
}),
|
||||
),
|
||||
signerImpl: func(t *testing.T) Signer {
|
||||
return &fake.Signer{
|
||||
FakeSign: func(context.Context, *certificatesv1.CertificateSigningRequest, cmapi.GenericIssuer) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
},
|
||||
sarReaction: sarReactionAllow,
|
||||
wantSARCreation: []*authzv1.SubjectAccessReview{
|
||||
{
|
||||
Spec: authzv1.SubjectAccessReviewSpec{
|
||||
User: "user-1",
|
||||
Groups: []string{"group-1", "group-2"},
|
||||
Extra: map[string]authzv1.ExtraValue{
|
||||
"extra": []string{"1", "2"},
|
||||
},
|
||||
UID: "uid-1",
|
||||
|
||||
ResourceAttributes: &authzv1.ResourceAttributes{
|
||||
Group: "cert-manager.io",
|
||||
Resource: "signers",
|
||||
Verb: "reference",
|
||||
Namespace: "hello",
|
||||
Name: "world",
|
||||
Version: "*",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
existingIssuer: gen.Issuer("world", gen.SetIssuerNamespace("hello"),
|
||||
gen.SetIssuerCA(cmapi.CAIssuer{
|
||||
SecretName: "tls",
|
||||
}),
|
||||
gen.AddIssuerCondition(cmapi.IssuerCondition{
|
||||
Type: cmapi.IssuerConditionReady,
|
||||
Status: cmmeta.ConditionTrue,
|
||||
Reason: "IssuerReady",
|
||||
Message: "Issuer ready message",
|
||||
}),
|
||||
),
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
util.Clock = fixedClock
|
||||
builder := &testpkg.Builder{
|
||||
T: t,
|
||||
Clock: fixedClock,
|
||||
}
|
||||
if test.existingIssuer != nil {
|
||||
builder.CertManagerObjects = append(builder.CertManagerObjects, test.existingIssuer)
|
||||
}
|
||||
if test.existingCSR != nil {
|
||||
builder.KubeObjects = append(builder.KubeObjects, test.existingCSR)
|
||||
}
|
||||
|
||||
for i := range test.wantSARCreation {
|
||||
builder.ExpectedActions = append(builder.ExpectedActions,
|
||||
testpkg.NewAction(coretesting.NewCreateAction(
|
||||
authzv1.SchemeGroupVersion.WithResource("subjectaccessreviews"),
|
||||
"",
|
||||
test.wantSARCreation[i],
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
builder.Init()
|
||||
|
||||
builder.FakeKubeClient().PrependReactor("create", "*", func(action coretesting.Action) (bool, runtime.Object, error) {
|
||||
if action.GetResource() != authzv1.SchemeGroupVersion.WithResource("subjectaccessreviews") {
|
||||
return false, nil, nil
|
||||
}
|
||||
return test.sarReaction(t)(action)
|
||||
})
|
||||
|
||||
controller := New(test.signerType, test.signerImpl(t))
|
||||
_, _, err := controller.Register(builder.Context)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if test.wantConditions != nil {
|
||||
if test.existingCSR == nil {
|
||||
t.Fatal("cannot expect an Update operation if test.existingCSR is nil")
|
||||
}
|
||||
expectedCSR := test.existingCSR.DeepCopy()
|
||||
expectedCSR.Status.Conditions = test.wantConditions
|
||||
builder.ExpectedActions = append(builder.ExpectedActions,
|
||||
testpkg.NewAction(coretesting.NewUpdateSubresourceAction(
|
||||
certificatesv1.SchemeGroupVersion.WithResource("certificatesigningrequests"),
|
||||
"status",
|
||||
"",
|
||||
expectedCSR,
|
||||
)),
|
||||
)
|
||||
}
|
||||
if test.wantEvent != "" {
|
||||
builder.ExpectedEvents = []string{test.wantEvent}
|
||||
}
|
||||
|
||||
builder.Start()
|
||||
defer builder.Stop()
|
||||
|
||||
key := test.key
|
||||
if key == "" && test.existingCSR != nil {
|
||||
key, err = controllerpkg.KeyFunc(test.existingCSR)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
gotErr := controller.ProcessItem(context.Background(), key)
|
||||
if test.wantErr != (gotErr != nil) {
|
||||
t.Errorf("got unexpected error, exp=%t got=%v",
|
||||
test.wantErr, gotErr)
|
||||
}
|
||||
|
||||
builder.CheckAndFinish()
|
||||
})
|
||||
}
|
||||
}
|
||||
26
pkg/controller/certificatesigningrequests/fake/BUILD.bazel
Normal file
26
pkg/controller/certificatesigningrequests/fake/BUILD.bazel
Normal file
@ -0,0 +1,26 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["fake.go"],
|
||||
importpath = "github.com/jetstack/cert-manager/pkg/controller/certificatesigningrequests/fake",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/apis/certmanager/v1:go_default_library",
|
||||
"@io_k8s_api//certificates/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
33
pkg/controller/certificatesigningrequests/fake/fake.go
Normal file
33
pkg/controller/certificatesigningrequests/fake/fake.go
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright 2021 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
certificatesv1 "k8s.io/api/certificates/v1"
|
||||
|
||||
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
|
||||
)
|
||||
|
||||
type Signer struct {
|
||||
FakeSign func(context.Context, *certificatesv1.CertificateSigningRequest, cmapi.GenericIssuer) error
|
||||
}
|
||||
|
||||
func (s *Signer) Sign(ctx context.Context, csr *certificatesv1.CertificateSigningRequest, issuerObj cmapi.GenericIssuer) error {
|
||||
return s.FakeSign(ctx, csr, issuerObj)
|
||||
}
|
||||
197
pkg/controller/certificatesigningrequests/sync.go
Normal file
197
pkg/controller/certificatesigningrequests/sync.go
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
Copyright 2021 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package certificatesigningrequests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
authzv1 "k8s.io/api/authorization/v1"
|
||||
certificatesv1 "k8s.io/api/certificates/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
apiutil "github.com/jetstack/cert-manager/pkg/api/util"
|
||||
"github.com/jetstack/cert-manager/pkg/apis/certmanager"
|
||||
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
|
||||
cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
|
||||
"github.com/jetstack/cert-manager/pkg/controller/certificatesigningrequests/util"
|
||||
logf "github.com/jetstack/cert-manager/pkg/logs"
|
||||
)
|
||||
|
||||
func (c *Controller) Sync(ctx context.Context, csr *certificatesv1.CertificateSigningRequest) error {
|
||||
log := logf.WithResource(logf.FromContext(ctx), csr).WithValues("signerName", csr.Spec.SignerName)
|
||||
dbg := log.V(logf.DebugLevel)
|
||||
|
||||
ref, ok := util.SignerIssuerRefFromSignerName(csr.Spec.SignerName)
|
||||
if !ok {
|
||||
dbg.Info("certificate signing request has malformed signer name,", "signerName", csr.Spec.SignerName)
|
||||
return nil
|
||||
}
|
||||
|
||||
if ref.Group != certmanager.GroupName {
|
||||
dbg.Info("certificate signing request signerName group does not match 'cert-manager.io' group so skipping processing")
|
||||
return nil
|
||||
}
|
||||
|
||||
if util.CertificateSigningRequestIsFailed(csr) {
|
||||
dbg.Info("certificate signing request has failed so skipping processing")
|
||||
return nil
|
||||
}
|
||||
if !util.CertificateSigningRequestIsApproved(csr) {
|
||||
dbg.Info("certificate signing request is not approved so skipping processing")
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(csr.Status.Certificate) > 0 {
|
||||
dbg.Info("certificate field is already set in status so skipping processing")
|
||||
return nil
|
||||
}
|
||||
|
||||
var kind string
|
||||
switch ref.Type {
|
||||
case "issuers":
|
||||
kind = cmapi.IssuerKind
|
||||
break
|
||||
|
||||
case "clusterissuers":
|
||||
kind = cmapi.ClusterIssuerKind
|
||||
break
|
||||
|
||||
default:
|
||||
dbg.Info("certificate signing request signerName type does not match 'issuers' or 'clusterissuers' so skipping processing")
|
||||
return nil
|
||||
}
|
||||
|
||||
issuerObj, err := c.helper.GetGenericIssuer(cmmeta.ObjectReference{
|
||||
Name: ref.Name,
|
||||
Kind: kind,
|
||||
Group: ref.Group,
|
||||
}, ref.Namespace)
|
||||
if apierrors.IsNotFound(err) {
|
||||
c.recorder.Eventf(csr, corev1.EventTypeWarning, "IssuerNotFound", "Referenced %s %s/%s not found", kind, ref.Namespace, ref.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error(err, "failed to get issuer")
|
||||
return err
|
||||
}
|
||||
|
||||
log = logf.WithRelatedResource(log, issuerObj)
|
||||
dbg.Info("ensuring issuer type matches this controller")
|
||||
|
||||
signerType, err := apiutil.NameForIssuer(issuerObj)
|
||||
if err != nil {
|
||||
c.recorder.Eventf(csr, corev1.EventTypeWarning, "IssuerTypeMissing", "Referenced %s %s/%s is missing type", kind, ref.Namespace, ref.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// This CertificateSigningRequest is not meant for us, ignore
|
||||
if signerType != c.signerType {
|
||||
dbg.WithValues(logf.RelatedResourceKindKey, signerType).Info("signer reference type does not match controller resource kind, ignoring")
|
||||
return nil
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case cmapi.IssuerKind:
|
||||
ok, err := c.userCanReferenceSigner(ctx, csr, ref.Namespace, ref.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
message := fmt.Sprintf("Requester may not reference Namespaced Issuer %s/%s", ref.Namespace, ref.Name)
|
||||
c.recorder.Event(csr, corev1.EventTypeWarning, "DeniedReference", message)
|
||||
util.CertificateSigningRequestSetFailed(csr, "DeniedReference", message)
|
||||
if _, err := c.certClient.UpdateStatus(ctx, csr, metav1.UpdateOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
case cmapi.ClusterIssuerKind:
|
||||
// Namespace not valid for a clusterissuer
|
||||
if len(ref.Namespace) > 0 {
|
||||
message := fmt.Sprintf("Signer clusterissuers may not be referenced with namespace (%s)", ref.Namespace)
|
||||
c.recorder.Event(csr, corev1.EventTypeWarning, "BadSignerName", message)
|
||||
util.CertificateSigningRequestSetFailed(csr, "BadSignerName", message)
|
||||
if _, err := c.certClient.UpdateStatus(ctx, csr, metav1.UpdateOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// check ready condition
|
||||
if !apiutil.IssuerHasCondition(issuerObj, cmapi.IssuerCondition{
|
||||
Type: cmapi.IssuerConditionReady,
|
||||
Status: cmmeta.ConditionTrue,
|
||||
}) {
|
||||
c.recorder.Eventf(csr, corev1.EventTypeWarning, "IssuerNotReady", "Referenced %s %s/%s does not have a Ready status condition",
|
||||
kind, issuerObj.GetNamespace(), issuerObj.GetName())
|
||||
return nil
|
||||
}
|
||||
|
||||
dbg.Info("invoking sign function as existing certificate does not exist")
|
||||
|
||||
return c.signer.Sign(ctx, csr, issuerObj)
|
||||
}
|
||||
|
||||
// userCanReferenceSigner will return true if the CSR requester has a bound
|
||||
// role that allows them to reference a given Namespaced signer. The user must
|
||||
// have the permissions:
|
||||
// group: cert-manager.io
|
||||
// resource: signers
|
||||
// verb: reference
|
||||
// namespace: <referenced signer namespace>
|
||||
// name: <either the name of the signer or '*' for all signer names in that namespace>
|
||||
func (c *Controller) userCanReferenceSigner(ctx context.Context, csr *certificatesv1.CertificateSigningRequest, issuerNamespace, issuerName string) (bool, error) {
|
||||
extra := make(map[string]authzv1.ExtraValue)
|
||||
for k, v := range csr.Spec.Extra {
|
||||
extra[k] = authzv1.ExtraValue(v)
|
||||
}
|
||||
|
||||
for _, name := range []string{issuerName, "*"} {
|
||||
resp, err := c.sarClient.Create(ctx, &authzv1.SubjectAccessReview{
|
||||
Spec: authzv1.SubjectAccessReviewSpec{
|
||||
User: csr.Spec.Username,
|
||||
Groups: csr.Spec.Groups,
|
||||
Extra: extra,
|
||||
UID: csr.Spec.UID,
|
||||
|
||||
ResourceAttributes: &authzv1.ResourceAttributes{
|
||||
Group: certmanager.GroupName,
|
||||
Resource: "signers",
|
||||
Verb: "reference",
|
||||
Namespace: issuerNamespace,
|
||||
Name: name,
|
||||
Version: "*",
|
||||
},
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if resp.Status.Allowed {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
38
pkg/controller/certificatesigningrequests/util/BUILD.bazel
Normal file
38
pkg/controller/certificatesigningrequests/util/BUILD.bazel
Normal file
@ -0,0 +1,38 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"conditions.go",
|
||||
"signername.go",
|
||||
],
|
||||
importpath = "github.com/jetstack/cert-manager/pkg/controller/certificatesigningrequests/util",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/logs:go_default_library",
|
||||
"@io_k8s_api//certificates/v1:go_default_library",
|
||||
"@io_k8s_api//core/v1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
|
||||
"@io_k8s_utils//clock:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["signername_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
65
pkg/controller/certificatesigningrequests/util/conditions.go
Normal file
65
pkg/controller/certificatesigningrequests/util/conditions.go
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
Copyright 2021 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
certificatesv1 "k8s.io/api/certificates/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/clock"
|
||||
|
||||
logf "github.com/jetstack/cert-manager/pkg/logs"
|
||||
)
|
||||
|
||||
// Clock is defined as a package var so it can be stubbed out during tests.
|
||||
var Clock clock.Clock = clock.RealClock{}
|
||||
|
||||
func CertificateSigningRequestIsApproved(csr *certificatesv1.CertificateSigningRequest) bool {
|
||||
for _, cond := range csr.Status.Conditions {
|
||||
if cond.Type == certificatesv1.CertificateApproved {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CertificateSigningRequestIsFailed(csr *certificatesv1.CertificateSigningRequest) bool {
|
||||
for _, cond := range csr.Status.Conditions {
|
||||
if cond.Type == certificatesv1.CertificateFailed {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CertificateSigningRequestSetFailed(csr *certificatesv1.CertificateSigningRequest, reason, message string) {
|
||||
nowTime := metav1.NewTime(Clock.Now())
|
||||
|
||||
// Since we only ever set this condition once (enforced by the API), we
|
||||
// needn't need to check whether the condition is already set.
|
||||
csr.Status.Conditions = append(csr.Status.Conditions, certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.CertificateFailed,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
LastTransitionTime: nowTime,
|
||||
LastUpdateTime: nowTime,
|
||||
})
|
||||
|
||||
logf.V(logf.InfoLevel).Infof("Setting lastTransitionTime for CertificateSigningRequest %s/%s condition Failed to %v",
|
||||
csr.Namespace, csr.Name, nowTime.Time)
|
||||
}
|
||||
63
pkg/controller/certificatesigningrequests/util/signername.go
Normal file
63
pkg/controller/certificatesigningrequests/util/signername.go
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright 2021 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SignerIssuerRef struct {
|
||||
Namespace, Name string
|
||||
Type, Group string
|
||||
}
|
||||
|
||||
// SignerIssuerRefFromSignerName will return a SignerIssuerRef from a
|
||||
// CertificateSigningRequests.SignerName
|
||||
func SignerIssuerRefFromSignerName(name string) (SignerIssuerRef, bool) {
|
||||
split := strings.Split(name, "/")
|
||||
if len(split) != 2 {
|
||||
return SignerIssuerRef{}, false
|
||||
}
|
||||
|
||||
signerTypeSplit := strings.SplitN(split[0], ".", 2)
|
||||
signerNameSplit := strings.Split(split[1], ".")
|
||||
|
||||
if len(signerTypeSplit) < 2 || signerNameSplit[0] == "" {
|
||||
return SignerIssuerRef{}, false
|
||||
}
|
||||
|
||||
switch len(signerNameSplit) {
|
||||
case 1:
|
||||
return SignerIssuerRef{
|
||||
Namespace: "",
|
||||
Name: signerNameSplit[0],
|
||||
Type: signerTypeSplit[0],
|
||||
Group: signerTypeSplit[1],
|
||||
}, true
|
||||
|
||||
case 2:
|
||||
return SignerIssuerRef{
|
||||
Namespace: signerNameSplit[0],
|
||||
Name: signerNameSplit[1],
|
||||
Type: signerTypeSplit[0],
|
||||
Group: signerTypeSplit[1],
|
||||
}, true
|
||||
|
||||
default:
|
||||
return SignerIssuerRef{}, false
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
/*
|
||||
Copyright 2021 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIssuerRefFromSignerName(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
inpName string
|
||||
expSignerIssuerRef SignerIssuerRef
|
||||
expOK bool
|
||||
}{
|
||||
"an empty name should return false": {
|
||||
inpName: "",
|
||||
expSignerIssuerRef: SignerIssuerRef{},
|
||||
expOK: false,
|
||||
},
|
||||
"a reference without a name should return false": {
|
||||
inpName: "foo.bar",
|
||||
expSignerIssuerRef: SignerIssuerRef{},
|
||||
expOK: false,
|
||||
},
|
||||
"a reference with a '/' but no name should return false": {
|
||||
inpName: "foo.bar/",
|
||||
expSignerIssuerRef: SignerIssuerRef{},
|
||||
expOK: false,
|
||||
},
|
||||
"a reference with no host should return false": {
|
||||
inpName: "/foo.bar",
|
||||
expSignerIssuerRef: SignerIssuerRef{},
|
||||
expOK: false,
|
||||
},
|
||||
"a reference with only one domain should return false": {
|
||||
inpName: "abc/hello-world",
|
||||
expSignerIssuerRef: SignerIssuerRef{},
|
||||
expOK: false,
|
||||
},
|
||||
"a reference with too many names should return false": {
|
||||
inpName: "foo.bar/hello.world.123",
|
||||
expSignerIssuerRef: SignerIssuerRef{},
|
||||
expOK: false,
|
||||
},
|
||||
"a reference with 2 domains and 2 names should return namespaced issuer": {
|
||||
inpName: "foo.bar/hello.world",
|
||||
expSignerIssuerRef: SignerIssuerRef{
|
||||
Namespace: "hello",
|
||||
Name: "world",
|
||||
Type: "foo",
|
||||
Group: "bar",
|
||||
},
|
||||
expOK: true,
|
||||
},
|
||||
"a reference with 4 domains and 2 names should return namespaced issuer": {
|
||||
inpName: "foo.bar.abc.dbc/hello.world",
|
||||
expSignerIssuerRef: SignerIssuerRef{
|
||||
Namespace: "hello",
|
||||
Name: "world",
|
||||
Type: "foo",
|
||||
Group: "bar.abc.dbc",
|
||||
},
|
||||
expOK: true,
|
||||
},
|
||||
"a reference with 2 domains and one name should return cluster issuer": {
|
||||
inpName: "foo.bar/hello-world",
|
||||
expSignerIssuerRef: SignerIssuerRef{
|
||||
Namespace: "",
|
||||
Name: "hello-world",
|
||||
Type: "foo",
|
||||
Group: "bar",
|
||||
},
|
||||
expOK: true,
|
||||
},
|
||||
"a reference with 4 domains and 1 name should return cluster issuer": {
|
||||
inpName: "foo.bar.abc.dbc/hello-world",
|
||||
expSignerIssuerRef: SignerIssuerRef{
|
||||
Namespace: "",
|
||||
Name: "hello-world",
|
||||
Type: "foo",
|
||||
Group: "bar.abc.dbc",
|
||||
},
|
||||
expOK: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
ref, ok := SignerIssuerRefFromSignerName(test.inpName)
|
||||
if ok != test.expOK {
|
||||
t.Errorf("unexpected ok, exp=%t got=%t",
|
||||
test.expOK, ok)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(ref, test.expSignerIssuerRef) {
|
||||
t.Errorf("unexpected SignerIssuerRef, exp=%v got=%v",
|
||||
test.expSignerIssuerRef, ref)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ go_library(
|
||||
"csr.go",
|
||||
"generate.go",
|
||||
"keyusage.go",
|
||||
"kube.go",
|
||||
"parse.go",
|
||||
],
|
||||
importpath = "github.com/jetstack/cert-manager/pkg/util/pki",
|
||||
@ -13,7 +14,9 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/api/util:go_default_library",
|
||||
"//pkg/apis/certmanager/v1:go_default_library",
|
||||
"//pkg/apis/experimental/v1alpha1:go_default_library",
|
||||
"//pkg/util/errors:go_default_library",
|
||||
"@io_k8s_api//certificates/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ go_library(
|
||||
srcs = [
|
||||
"certificate.go",
|
||||
"certificaterequest.go",
|
||||
"certificatesigningrequest.go",
|
||||
"challenge.go",
|
||||
"conditions.go",
|
||||
"csr.go",
|
||||
@ -19,8 +20,10 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/apis/acme/v1:go_default_library",
|
||||
"//pkg/apis/certmanager/v1:go_default_library",
|
||||
"//pkg/apis/experimental/v1alpha1:go_default_library",
|
||||
"//pkg/apis/meta/v1:go_default_library",
|
||||
"//pkg/util/pki:go_default_library",
|
||||
"@io_k8s_api//certificates/v1:go_default_library",
|
||||
"@io_k8s_api//core/v1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
|
||||
"@io_k8s_apimachinery//pkg/types:go_default_library",
|
||||
|
||||
142
test/unit/gen/certificatesigningrequest.go
Normal file
142
test/unit/gen/certificatesigningrequest.go
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
Copyright 2021 The cert-manager Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gen
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"strconv"
|
||||
|
||||
certificatesv1 "k8s.io/api/certificates/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
experimentalapi "github.com/jetstack/cert-manager/pkg/apis/experimental/v1alpha1"
|
||||
)
|
||||
|
||||
type CertificateSigningRequestModifier func(*certificatesv1.CertificateSigningRequest)
|
||||
|
||||
func CertificateSigningRequest(name string, mods ...CertificateSigningRequestModifier) *certificatesv1.CertificateSigningRequest {
|
||||
c := &certificatesv1.CertificateSigningRequest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Annotations: make(map[string]string),
|
||||
Labels: make(map[string]string),
|
||||
},
|
||||
}
|
||||
for _, mod := range mods {
|
||||
mod(c)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func CertificateSigningRequestFrom(cr *certificatesv1.CertificateSigningRequest, mods ...CertificateSigningRequestModifier) *certificatesv1.CertificateSigningRequest {
|
||||
cr = cr.DeepCopy()
|
||||
for _, mod := range mods {
|
||||
mod(cr)
|
||||
}
|
||||
return cr
|
||||
}
|
||||
|
||||
func SetCertificateSigningRequestIsCA(isCA bool) CertificateSigningRequestModifier {
|
||||
return AddCertificateSigningRequestAnnotations(map[string]string{
|
||||
experimentalapi.CertificateSigningRequestIsCAAnnotationKey: strconv.FormatBool(isCA),
|
||||
})
|
||||
}
|
||||
|
||||
func SetCertificateSigningRequestRequest(request []byte) CertificateSigningRequestModifier {
|
||||
return func(csr *certificatesv1.CertificateSigningRequest) {
|
||||
csr.Spec.Request = request
|
||||
}
|
||||
}
|
||||
|
||||
func AddCertificateSigningRequestAnnotations(annotations map[string]string) CertificateSigningRequestModifier {
|
||||
return func(csr *certificatesv1.CertificateSigningRequest) {
|
||||
// Make sure to do a merge here with new annotations overriding.
|
||||
annotationsNew := csr.GetAnnotations()
|
||||
if annotationsNew == nil {
|
||||
annotationsNew = make(map[string]string)
|
||||
}
|
||||
for k, v := range annotations {
|
||||
annotationsNew[k] = v
|
||||
}
|
||||
csr.SetAnnotations(annotationsNew)
|
||||
}
|
||||
}
|
||||
|
||||
func SetCertificateSigningRequestSignerName(signerName string) CertificateSigningRequestModifier {
|
||||
return func(csr *certificatesv1.CertificateSigningRequest) {
|
||||
csr.Spec.SignerName = signerName
|
||||
}
|
||||
}
|
||||
|
||||
func SetCertificateSigningRequestDuration(duration string) CertificateSigningRequestModifier {
|
||||
return AddCertificateSigningRequestAnnotations(map[string]string{
|
||||
experimentalapi.CertificateSigningRequestDurationAnnotationKey: duration,
|
||||
})
|
||||
}
|
||||
|
||||
func SetCertificateSigningRequestCertificate(cert []byte) CertificateSigningRequestModifier {
|
||||
return func(csr *certificatesv1.CertificateSigningRequest) {
|
||||
csr.Status.Certificate = cert
|
||||
}
|
||||
}
|
||||
|
||||
func SetCertificateSigningRequestCA(ca []byte) CertificateSigningRequestModifier {
|
||||
return AddCertificateSigningRequestAnnotations(map[string]string{
|
||||
experimentalapi.CertificateSigningRequestCAAnnotationKey: base64.StdEncoding.EncodeToString(ca),
|
||||
})
|
||||
}
|
||||
|
||||
func SetCertificateSigningRequestStatusCondition(c certificatesv1.CertificateSigningRequestCondition) CertificateSigningRequestModifier {
|
||||
return func(csr *certificatesv1.CertificateSigningRequest) {
|
||||
if len(csr.Status.Conditions) == 0 {
|
||||
csr.Status.Conditions = []certificatesv1.CertificateSigningRequestCondition{c}
|
||||
return
|
||||
}
|
||||
|
||||
for i, existingC := range csr.Status.Conditions {
|
||||
if existingC.Type == c.Type {
|
||||
csr.Status.Conditions[i] = c
|
||||
return
|
||||
}
|
||||
}
|
||||
csr.Status.Conditions = append(csr.Status.Conditions, c)
|
||||
}
|
||||
}
|
||||
|
||||
func SetCertificateSigningRequestUsername(username string) CertificateSigningRequestModifier {
|
||||
return func(csr *certificatesv1.CertificateSigningRequest) {
|
||||
csr.Spec.Username = username
|
||||
}
|
||||
}
|
||||
|
||||
func SetCertificateSigningRequestGroups(groups []string) CertificateSigningRequestModifier {
|
||||
return func(csr *certificatesv1.CertificateSigningRequest) {
|
||||
csr.Spec.Groups = groups
|
||||
}
|
||||
}
|
||||
|
||||
func SetCertificateSigningRequestUID(uid string) CertificateSigningRequestModifier {
|
||||
return func(csr *certificatesv1.CertificateSigningRequest) {
|
||||
csr.Spec.UID = uid
|
||||
}
|
||||
}
|
||||
|
||||
func SetCertificateSigningRequestExtra(extra map[string]certificatesv1.ExtraValue) CertificateSigningRequestModifier {
|
||||
return func(csr *certificatesv1.CertificateSigningRequest) {
|
||||
csr.Spec.Extra = extra
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user