Adds acme CertificateRequest controller with unit tests
Signed-off-by: JoshVanL <vleeuwenjoshua@gmail.com>
This commit is contained in:
parent
d4a675ee66
commit
e2b9b21766
@ -11,6 +11,7 @@ go_library(
|
||||
"//pkg/client/clientset/versioned/scheme:go_default_library",
|
||||
"//pkg/client/informers/externalversions:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/controller/certificaterequests/acme:go_default_library",
|
||||
"//pkg/controller/certificaterequests/ca:go_default_library",
|
||||
"//pkg/controller/certificaterequests/selfsigned:go_default_library",
|
||||
"//pkg/controller/certificaterequests/vault:go_default_library",
|
||||
|
||||
@ -42,6 +42,7 @@ import (
|
||||
intscheme "github.com/jetstack/cert-manager/pkg/client/clientset/versioned/scheme"
|
||||
informers "github.com/jetstack/cert-manager/pkg/client/informers/externalversions"
|
||||
"github.com/jetstack/cert-manager/pkg/controller"
|
||||
cracmecontroller "github.com/jetstack/cert-manager/pkg/controller/certificaterequests/acme"
|
||||
crcacontroller "github.com/jetstack/cert-manager/pkg/controller/certificaterequests/ca"
|
||||
crselfsignedcontroller "github.com/jetstack/cert-manager/pkg/controller/certificaterequests/selfsigned"
|
||||
crvaultcontroller "github.com/jetstack/cert-manager/pkg/controller/certificaterequests/vault"
|
||||
@ -80,6 +81,7 @@ func Run(opts *options.ControllerOptions, stopCh <-chan struct{}) {
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(feature.CertificateRequestControllers) {
|
||||
opts.EnabledControllers = append(opts.EnabledControllers, []string{
|
||||
cracmecontroller.CRControllerName,
|
||||
crcacontroller.CRControllerName,
|
||||
crselfsignedcontroller.CRControllerName,
|
||||
crvaultcontroller.CRControllerName,
|
||||
|
||||
@ -66,6 +66,7 @@ filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/controller/certificaterequests/acme:all-srcs",
|
||||
"//pkg/controller/certificaterequests/ca:all-srcs",
|
||||
"//pkg/controller/certificaterequests/fake:all-srcs",
|
||||
"//pkg/controller/certificaterequests/selfsigned:all-srcs",
|
||||
|
||||
56
pkg/controller/certificaterequests/acme/BUILD.bazel
Normal file
56
pkg/controller/certificaterequests/acme/BUILD.bazel
Normal file
@ -0,0 +1,56 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["acme.go"],
|
||||
importpath = "github.com/jetstack/cert-manager/pkg/controller/certificaterequests/acme",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/acme:go_default_library",
|
||||
"//pkg/api/util:go_default_library",
|
||||
"//pkg/apis/certmanager/v1alpha1:go_default_library",
|
||||
"//pkg/client/clientset/versioned/typed/certmanager/v1alpha1:go_default_library",
|
||||
"//pkg/client/listers/certmanager/v1alpha1:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/controller/certificaterequests:go_default_library",
|
||||
"//pkg/controller/certificaterequests/util:go_default_library",
|
||||
"//pkg/issuer:go_default_library",
|
||||
"//pkg/logs:go_default_library",
|
||||
"//pkg/util/pki:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["acme_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/apis/certmanager:go_default_library",
|
||||
"//pkg/apis/certmanager/v1alpha1:go_default_library",
|
||||
"//pkg/client/listers/certmanager/v1alpha1:go_default_library",
|
||||
"//pkg/controller/certificaterequests/test:go_default_library",
|
||||
"//pkg/controller/test:go_default_library",
|
||||
"//pkg/controller/test/fake:go_default_library",
|
||||
"//test/unit/gen:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/client-go/testing: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"],
|
||||
)
|
||||
194
pkg/controller/certificaterequests/acme/acme.go
Normal file
194
pkg/controller/certificaterequests/acme/acme.go
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
Copyright 2019 The Jetstack cert-manager contributors.
|
||||
|
||||
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 acme
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
|
||||
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/record"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/acme"
|
||||
apiutil "github.com/jetstack/cert-manager/pkg/api/util"
|
||||
"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
|
||||
cmclientset "github.com/jetstack/cert-manager/pkg/client/clientset/versioned/typed/certmanager/v1alpha1"
|
||||
cmlisters "github.com/jetstack/cert-manager/pkg/client/listers/certmanager/v1alpha1"
|
||||
controllerpkg "github.com/jetstack/cert-manager/pkg/controller"
|
||||
"github.com/jetstack/cert-manager/pkg/controller/certificaterequests"
|
||||
crutil "github.com/jetstack/cert-manager/pkg/controller/certificaterequests/util"
|
||||
issuerpkg "github.com/jetstack/cert-manager/pkg/issuer"
|
||||
logf "github.com/jetstack/cert-manager/pkg/logs"
|
||||
"github.com/jetstack/cert-manager/pkg/util/pki"
|
||||
)
|
||||
|
||||
const (
|
||||
CRControllerName = "certificaterequests-issuer-acme"
|
||||
)
|
||||
|
||||
type ACME struct {
|
||||
// used to record Events about resources to the API
|
||||
recorder record.EventRecorder
|
||||
issuerOptions controllerpkg.IssuerOptions
|
||||
|
||||
orderLister cmlisters.OrderLister
|
||||
cmClientV cmclientset.CertmanagerV1alpha1Interface
|
||||
helper issuerpkg.Helper
|
||||
}
|
||||
|
||||
func init() {
|
||||
// create certificate request controller for acme issuer
|
||||
controllerpkg.Register(CRControllerName, func(ctx *controllerpkg.Context) (controllerpkg.Interface, error) {
|
||||
acme := NewACME(ctx)
|
||||
|
||||
orderInformer := ctx.SharedInformerFactory.Certmanager().V1alpha1().Orders().Informer()
|
||||
controller := certificaterequests.New(apiutil.IssuerACME, acme, orderInformer)
|
||||
|
||||
c, err := controllerpkg.New(ctx, CRControllerName, controller)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.Run, nil
|
||||
})
|
||||
}
|
||||
|
||||
func NewACME(ctx *controllerpkg.Context) *ACME {
|
||||
ctx.CMClient.CertmanagerV1alpha1()
|
||||
return &ACME{
|
||||
recorder: ctx.Recorder,
|
||||
issuerOptions: ctx.IssuerOptions,
|
||||
orderLister: ctx.SharedInformerFactory.Certmanager().V1alpha1().Orders().Lister(),
|
||||
cmClientV: ctx.CMClient.CertmanagerV1alpha1(),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ACME) Sign(ctx context.Context, cr *v1alpha1.CertificateRequest, issuer v1alpha1.GenericIssuer) (*issuerpkg.IssueResponse, error) {
|
||||
log := logf.FromContext(ctx, "sign")
|
||||
reporter := crutil.NewReporter(cr, a.recorder)
|
||||
resourceNamespace := a.issuerOptions.ResourceNamespace(issuer)
|
||||
|
||||
// If we can't decode the CSR PEM we have to hard fail
|
||||
csr, err := pki.DecodeX509CertificateRequestBytes(cr.Spec.CSRPEM)
|
||||
if err != nil {
|
||||
reporter.Failed(err, "ErrorParsingCSR", "Failed to decode CSR in spec")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// If we fail to build the order we have to hard fail.
|
||||
expectedOrder, err := buildOrder(cr, csr)
|
||||
if err != nil {
|
||||
reporter.Failed(err, "ErrorBuildingOrder", "Failed to build order")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
order, err := a.orderLister.Orders(expectedOrder.Namespace).Get(expectedOrder.Name)
|
||||
if err != nil {
|
||||
if k8sErrors.IsNotFound(err) {
|
||||
// Failing to create the order here is most likely network related.
|
||||
// We should backoff and keep trying.
|
||||
order, err = a.cmClientV.Orders(resourceNamespace).Create(expectedOrder)
|
||||
if err != nil {
|
||||
reporter.Pending(err, "ErrorCreatingOrder",
|
||||
fmt.Sprintf("Failed create new order resource %s/%s", resourceNamespace, expectedOrder.Name))
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
} else {
|
||||
// We are probably in a network error here so we should backoff and retry
|
||||
reporter.Pending(err, "ErrorGettingOrder",
|
||||
fmt.Sprintf("Failed to get order resource %s/%s", resourceNamespace, expectedOrder.Name))
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// If the acme order has failed then so too does the CertificateRequest meet the same fate.
|
||||
if acme.IsFailureState(order.Status.State) {
|
||||
reporter.Failed(fmt.Errorf("order is in failure state %q", order.Status.State), "OrderFailed",
|
||||
fmt.Sprintf("Failed to resolve order resource %s/%s", resourceNamespace, expectedOrder.Name))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Order valid, return cert. The calling controller will update with ready if it's happy with the cert.
|
||||
if order.Status.State == v1alpha1.Valid {
|
||||
log.Info("certificate issued")
|
||||
|
||||
return &issuerpkg.IssueResponse{
|
||||
Certificate: order.Status.Certificate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// We update here to just normal pending while we wait for the order to be resolved.
|
||||
reporter.Pending(fmt.Errorf("order is currently pending: %q", order.Status.State), "OrderPending",
|
||||
fmt.Sprintf("Waiting on certificate issuance from order %s/%s", resourceNamespace, expectedOrder.Name))
|
||||
|
||||
log.Info("acme Order resource is not in a valid state, waiting...", "state", order.Status.State)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Build order. If we error here it is a terminating failure.
|
||||
func buildOrder(cr *v1alpha1.CertificateRequest, csr *x509.CertificateRequest) (*v1alpha1.Order, error) {
|
||||
spec := v1alpha1.OrderSpec{
|
||||
CSR: cr.Spec.CSRPEM,
|
||||
IssuerRef: cr.Spec.IssuerRef,
|
||||
CommonName: csr.Subject.CommonName,
|
||||
DNSNames: csr.DNSNames,
|
||||
}
|
||||
hash, err := hashOrder(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// truncate certificate name so final name will be <= 63 characters.
|
||||
// hash (uint32) will be at most 10 digits long, and we account for
|
||||
// the hyphen.
|
||||
return &v1alpha1.Order{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%.52s-%d", cr.Name, hash),
|
||||
Namespace: cr.Namespace,
|
||||
Labels: cr.Labels,
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
*metav1.NewControllerRef(cr, v1alpha1.SchemeGroupVersion.WithKind(v1alpha1.CertificateRequestKind)),
|
||||
},
|
||||
},
|
||||
Spec: spec,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func hashOrder(orderSpec v1alpha1.OrderSpec) (uint32, error) {
|
||||
// create a shallow copy of the OrderSpec so we can overwrite the CSR field
|
||||
orderSpec.CSR = nil
|
||||
|
||||
orderSpecBytes, err := json.Marshal(orderSpec)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
hashF := fnv.New32()
|
||||
_, err = hashF.Write(orderSpecBytes)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return hashF.Sum32(), nil
|
||||
}
|
||||
211
pkg/controller/certificaterequests/acme/acme_test.go
Normal file
211
pkg/controller/certificaterequests/acme/acme_test.go
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
Copyright 2019 The Jetstack cert-manager contributors.
|
||||
|
||||
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 acme
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
coretesting "k8s.io/client-go/testing"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/apis/certmanager"
|
||||
"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
|
||||
cmlisters "github.com/jetstack/cert-manager/pkg/client/listers/certmanager/v1alpha1"
|
||||
testcr "github.com/jetstack/cert-manager/pkg/controller/certificaterequests/test"
|
||||
testpkg "github.com/jetstack/cert-manager/pkg/controller/test"
|
||||
testfake "github.com/jetstack/cert-manager/pkg/controller/test/fake"
|
||||
"github.com/jetstack/cert-manager/test/unit/gen"
|
||||
)
|
||||
|
||||
func buildOrderMust(t *testing.T, cr *v1alpha1.CertificateRequest,
|
||||
csr *x509.CertificateRequest) *v1alpha1.Order {
|
||||
order, err := buildOrder(cr, csr)
|
||||
if err != nil {
|
||||
t.Errorf("failed to build order during testing: %s", err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
return order
|
||||
}
|
||||
|
||||
func TestSign(t *testing.T) {
|
||||
rsaPK := testcr.GenerateRSAPrivateKey(t)
|
||||
csr, csrPEM := testcr.GenerateCSR(t, rsaPK)
|
||||
|
||||
testCR := gen.CertificateRequest("test-cr",
|
||||
gen.SetCertificateRequestCSR(csrPEM),
|
||||
gen.SetCertificateRequestIsCA(true),
|
||||
gen.SetCertificateRequestDuration(&metav1.Duration{Duration: time.Hour * 24 * 60}),
|
||||
gen.SetCertificateRequestIssuer(v1alpha1.ObjectReference{
|
||||
Name: "acme-issuer",
|
||||
Group: certmanager.GroupName,
|
||||
Kind: "Issuer",
|
||||
}),
|
||||
)
|
||||
|
||||
_, rsaPEMCert := testcr.GenerateSelfSignedCertFromCR(t, testCR, rsaPK, time.Hour*24*60)
|
||||
testOrder := buildOrderMust(t, testCR, csr)
|
||||
|
||||
tests := map[string]testT{
|
||||
"a badly formed CSR should report failure": {
|
||||
issuer: gen.Issuer("acme-issuer"),
|
||||
certificateRequest: gen.CertificateRequestFrom(testCR,
|
||||
gen.SetCertificateRequestCSR([]byte("a bad csr")),
|
||||
),
|
||||
builder: &testpkg.Builder{
|
||||
KubeObjects: []runtime.Object{},
|
||||
CertManagerObjects: []runtime.Object{},
|
||||
ExpectedEvents: []string{
|
||||
"Warning ErrorParsingCSR Failed to decode CSR in spec: error decoding certificate request PEM block",
|
||||
},
|
||||
CheckFn: testcr.MustNoResponse,
|
||||
},
|
||||
expectedErr: false,
|
||||
},
|
||||
|
||||
//TODO: Think of a creative way to get `buildOrder` to fail :thinking_face:
|
||||
|
||||
"if order doesn't exist then attempt to create one": {
|
||||
issuer: gen.Issuer("acme-issuer"),
|
||||
certificateRequest: testCR,
|
||||
builder: &testpkg.Builder{
|
||||
CertManagerObjects: []runtime.Object{},
|
||||
ExpectedActions: []testpkg.Action{
|
||||
testpkg.NewAction(coretesting.NewCreateAction(
|
||||
v1alpha1.SchemeGroupVersion.WithResource("orders"),
|
||||
gen.DefaultTestNamespace,
|
||||
testOrder,
|
||||
)),
|
||||
},
|
||||
ExpectedEvents: []string{
|
||||
`Normal OrderPending Waiting on certificate issuance from order default-unit-test-ns/test-cr-3958469914: order is currently pending: ""`,
|
||||
},
|
||||
CheckFn: testcr.MustNoResponse,
|
||||
},
|
||||
expectedErr: false,
|
||||
},
|
||||
|
||||
"if we fail to get the order resource then we should report pending": {
|
||||
issuer: gen.Issuer("acme-issuer"),
|
||||
certificateRequest: testCR,
|
||||
builder: &testpkg.Builder{
|
||||
ExpectedEvents: []string{
|
||||
`Normal ErrorGettingOrder Failed to get order resource default-unit-test-ns/test-cr-3958469914: this is a network error`,
|
||||
},
|
||||
CheckFn: testcr.MustNoResponse,
|
||||
},
|
||||
expectedErr: true,
|
||||
FakeOrderLister: &testfake.FakeOrderLister{
|
||||
OrdersFn: func(namespace string) cmlisters.OrderNamespaceLister {
|
||||
return &testfake.FakeOrderNamespaceLister{
|
||||
GetFn: func(name string) (ret *v1alpha1.Order, err error) {
|
||||
return nil, errors.New("this is a network error")
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"if the order resource is in a failed state then we should report failure": {
|
||||
issuer: gen.Issuer("acme-issuer"),
|
||||
certificateRequest: testCR,
|
||||
builder: &testpkg.Builder{
|
||||
ExpectedEvents: []string{
|
||||
`Warning OrderFailed Failed to resolve order resource default-unit-test-ns/test-cr-3958469914: order is in failure state "invalid"`,
|
||||
},
|
||||
CertManagerObjects: []runtime.Object{gen.OrderFrom(testOrder,
|
||||
gen.SetOrderState(v1alpha1.Invalid),
|
||||
)},
|
||||
CheckFn: testcr.MustNoResponse,
|
||||
},
|
||||
expectedErr: false,
|
||||
},
|
||||
|
||||
"if the order is in a non failure state or valid, then report pending": {
|
||||
issuer: gen.Issuer("acme-issuer"),
|
||||
certificateRequest: testCR,
|
||||
builder: &testpkg.Builder{
|
||||
ExpectedEvents: []string{
|
||||
`Normal OrderPending Waiting on certificate issuance from order default-unit-test-ns/test-cr-3958469914: order is currently pending: ""`,
|
||||
},
|
||||
CertManagerObjects: []runtime.Object{gen.OrderFrom(testOrder,
|
||||
gen.SetOrderState(""),
|
||||
)},
|
||||
CheckFn: testcr.MustNoResponse,
|
||||
},
|
||||
expectedErr: false,
|
||||
},
|
||||
|
||||
"if the order is in Valid state then return the certificate as response": {
|
||||
issuer: gen.Issuer("acme-issuer"),
|
||||
certificateRequest: testCR,
|
||||
builder: &testpkg.Builder{
|
||||
ExpectedEvents: []string{},
|
||||
CertManagerObjects: []runtime.Object{gen.OrderFrom(testOrder,
|
||||
gen.SetOrderState(v1alpha1.Valid),
|
||||
gen.SetOrderCertificate(rsaPEMCert),
|
||||
)},
|
||||
CheckFn: testcr.NoPrivateKeyFieldsSetCheck(nil),
|
||||
},
|
||||
expectedErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
runTest(t, test)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type testT struct {
|
||||
builder *testpkg.Builder
|
||||
certificateRequest *v1alpha1.CertificateRequest
|
||||
issuer v1alpha1.GenericIssuer
|
||||
|
||||
checkFn func(*testpkg.Builder, ...interface{})
|
||||
expectedErr bool
|
||||
|
||||
FakeOrderLister *testfake.FakeOrderLister
|
||||
}
|
||||
|
||||
func runTest(t *testing.T, test testT) {
|
||||
test.builder.T = t
|
||||
test.builder.Start()
|
||||
defer test.builder.Stop()
|
||||
|
||||
a := NewACME(test.builder.Context)
|
||||
test.builder.Sync()
|
||||
|
||||
if test.FakeOrderLister != nil {
|
||||
a.orderLister = test.FakeOrderLister
|
||||
}
|
||||
|
||||
resp, err := a.Sign(context.Background(), test.certificateRequest, test.issuer)
|
||||
if err != nil && !test.expectedErr {
|
||||
t.Errorf("expected to not get an error, but got: %v", err)
|
||||
}
|
||||
if err == nil && test.expectedErr {
|
||||
t.Errorf("expected to get an error but did not get one")
|
||||
}
|
||||
test.builder.CheckAndFinish(resp, err)
|
||||
}
|
||||
@ -69,6 +69,11 @@ type Controller struct {
|
||||
issuerLister cmlisters.IssuerLister
|
||||
clusterIssuerLister cmlisters.ClusterIssuerLister
|
||||
|
||||
// Extra informers that should be watched by this certificate request
|
||||
// controller instance. These resources can be owned by certificate requests
|
||||
// that we resolve.
|
||||
extraInformers []cache.SharedIndexInformer
|
||||
|
||||
// Issuer to call sign function
|
||||
issuer Issuer
|
||||
|
||||
@ -78,7 +83,7 @@ type Controller struct {
|
||||
reporter *util.Reporter
|
||||
}
|
||||
|
||||
func New(issuerType string, issuer Issuer) *Controller {
|
||||
func New(issuerType string, issuer Issuer, extraInformers ...cache.SharedIndexInformer) *Controller {
|
||||
return &Controller{
|
||||
issuerType: issuerType,
|
||||
issuer: issuer,
|
||||
@ -103,10 +108,17 @@ func (c *Controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitin
|
||||
|
||||
// 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{
|
||||
|
||||
// Ensure we also catch all extra informers for this certificate controller instance
|
||||
var extraInformersMustSync []cache.InformerSynced
|
||||
for _, i := range c.extraInformers {
|
||||
extraInformersMustSync = append(extraInformersMustSync, i.HasSynced)
|
||||
}
|
||||
|
||||
mustSync := append([]cache.InformerSynced{
|
||||
certificateRequestInformer.Informer().HasSynced,
|
||||
issuerInformer.Informer().HasSynced,
|
||||
}
|
||||
}, extraInformersMustSync...)
|
||||
|
||||
// if scoped to a single namespace
|
||||
// if we are running in non-namespaced mode (i.e. --namespace=""), we also
|
||||
@ -124,6 +136,14 @@ func (c *Controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitin
|
||||
|
||||
// register handler functions
|
||||
certificateRequestInformer.Informer().AddEventHandler(&controllerpkg.QueuingEventHandler{Queue: c.queue})
|
||||
issuerInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{WorkFunc: c.handleGenericIssuer})
|
||||
|
||||
// Ensure we catch extra informers that are owned by certificate requests
|
||||
for _, i := range c.extraInformers {
|
||||
i.AddEventHandler(&controllerpkg.BlockingEventHandler{
|
||||
WorkFunc: controllerpkg.HandleOwnedResourceNamespacedFunc(c.log, c.queue, certificateRequestGvk, certificateRequestGetter(c.certificateRequestLister)),
|
||||
})
|
||||
}
|
||||
|
||||
// instantiate metrics interface with default metrics implementation
|
||||
c.metrics = metrics.Default
|
||||
@ -165,3 +185,9 @@ func (c *Controller) ProcessItem(ctx context.Context, key string) error {
|
||||
ctx = logf.NewContext(ctx, logf.WithResource(log, cr))
|
||||
return c.Sync(ctx, cr)
|
||||
}
|
||||
|
||||
func certificateRequestGetter(lister cmlisters.CertificateRequestLister) func(namespace, name string) (interface{}, error) {
|
||||
return func(namespace, name string) (interface{}, error) {
|
||||
return lister.CertificateRequests(namespace).Get(name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,9 @@ go_library(
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/apis/certmanager/v1alpha1:go_default_library",
|
||||
"//pkg/controller/test:go_default_library",
|
||||
"//pkg/issuer:go_default_library",
|
||||
"//pkg/util/pki:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@ -34,6 +34,10 @@ import (
|
||||
"github.com/jetstack/cert-manager/pkg/util/pki"
|
||||
)
|
||||
|
||||
var (
|
||||
certificateRequestGvk = v1alpha1.SchemeGroupVersion.WithKind("CertificateRequest")
|
||||
)
|
||||
|
||||
func (c *Controller) Sync(ctx context.Context, cr *v1alpha1.CertificateRequest) (err error) {
|
||||
c.metrics.IncrementSyncCallCount(ControllerName)
|
||||
|
||||
|
||||
@ -40,7 +40,10 @@ filegroup(
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/controller/test/fake:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
27
pkg/controller/test/fake/BUILD.bazel
Normal file
27
pkg/controller/test/fake/BUILD.bazel
Normal file
@ -0,0 +1,27 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["orderlister.go"],
|
||||
importpath = "github.com/jetstack/cert-manager/pkg/controller/test/fake",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/apis/certmanager/v1alpha1:go_default_library",
|
||||
"//pkg/client/listers/certmanager/v1alpha1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels: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"],
|
||||
)
|
||||
73
pkg/controller/test/fake/orderlister.go
Normal file
73
pkg/controller/test/fake/orderlister.go
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2019 The Jetstack cert-manager contributors.
|
||||
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 (
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
|
||||
cmlisters "github.com/jetstack/cert-manager/pkg/client/listers/certmanager/v1alpha1"
|
||||
)
|
||||
|
||||
var _ cmlisters.OrderLister = &FakeOrderLister{}
|
||||
var _ cmlisters.OrderNamespaceLister = &FakeOrderNamespaceLister{}
|
||||
|
||||
type FakeOrderLister struct {
|
||||
ListFn func(selector labels.Selector) (ret []*v1alpha1.Order, err error)
|
||||
OrdersFn func(namespace string) cmlisters.OrderNamespaceLister
|
||||
}
|
||||
|
||||
type FakeOrderNamespaceLister struct {
|
||||
ListFn func(selector labels.Selector) (ret []*v1alpha1.Order, err error)
|
||||
GetFn func(name string) (ret *v1alpha1.Order, err error)
|
||||
}
|
||||
|
||||
func NewFakeOrderLister() *FakeOrderLister {
|
||||
return &FakeOrderLister{
|
||||
ListFn: func(selector labels.Selector) (ret []*v1alpha1.Order, err error) {
|
||||
return nil, nil
|
||||
},
|
||||
|
||||
OrdersFn: func(namespace string) cmlisters.OrderNamespaceLister {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewFakeOrderNamespaceLister() *FakeOrderNamespaceLister {
|
||||
return &FakeOrderNamespaceLister{
|
||||
ListFn: func(selector labels.Selector) (ret []*v1alpha1.Order, err error) {
|
||||
return nil, nil
|
||||
},
|
||||
GetFn: func(name string) (ret *v1alpha1.Order, err error) {
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FakeOrderLister) List(selector labels.Selector) (ret []*v1alpha1.Order, err error) {
|
||||
return f.ListFn(selector)
|
||||
}
|
||||
|
||||
func (f *FakeOrderLister) Orders(namespace string) cmlisters.OrderNamespaceLister {
|
||||
return f.OrdersFn(namespace)
|
||||
}
|
||||
|
||||
func (f *FakeOrderNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Order, err error) {
|
||||
return f.ListFn(selector)
|
||||
}
|
||||
|
||||
func (f *FakeOrderNamespaceLister) Get(name string) (*v1alpha1.Order, error) {
|
||||
return f.GetFn(name)
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user