Create 'predicate' package and standardise list and enqueue predicates

Signed-off-by: James Munnelly <james@munnelly.eu>
This commit is contained in:
James Munnelly
2020-04-16 14:28:52 +01:00
parent f3df90305c
commit c2aed0842e
17 changed files with 745 additions and 130 deletions
+8 -1
View File
@@ -2,19 +2,25 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["util.go"],
srcs = [
"informers.go",
"listers.go",
"util.go",
],
importpath = "github.com/jetstack/cert-manager/pkg/controller/expcertificates",
visibility = ["//visibility:public"],
deps = [
"//pkg/apis/certmanager/v1alpha2:go_default_library",
"//pkg/client/listers/certmanager/v1alpha2:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/controller/expcertificates/internal/predicate:go_default_library",
"//pkg/util:go_default_library",
"//pkg/util/pki:go_default_library",
"@com_github_go_logr_logr//: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/labels:go_default_library",
"@io_k8s_apimachinery//pkg/runtime:go_default_library",
"@io_k8s_client_go//util/workqueue:go_default_library",
],
)
@@ -30,6 +36,7 @@ filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/controller/expcertificates/internal/predicate:all-srcs",
"//pkg/controller/expcertificates/issuing:all-srcs",
"//pkg/controller/expcertificates/trigger:all-srcs",
],
@@ -0,0 +1,68 @@
/*
Copyright 2020 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 certificates
import (
"github.com/go-logr/logr"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/util/workqueue"
cmlisters "github.com/jetstack/cert-manager/pkg/client/listers/certmanager/v1alpha2"
controllerpkg "github.com/jetstack/cert-manager/pkg/controller"
"github.com/jetstack/cert-manager/pkg/controller/expcertificates/internal/predicate"
)
// EnqueueCertificatesForResourceUsingPredicates will return a function
// that can be used as an OnAdd handler for a SharedIndexInformer.
// It should be used as a handler for resources that are referenced
// in some way by Certificates.
// The namespace of the object being processed will be used in the List
// call when enqueuing Certificate resources.
// If no predicate constructors are given, all Certificate resources will be
// enqueued on every invocation.
func EnqueueCertificatesForResourceUsingPredicates(log logr.Logger, queue workqueue.Interface, lister cmlisters.CertificateLister, selector labels.Selector, predicateBuilders ...predicate.ExtractorFunc) func(obj interface{}) {
return func(obj interface{}) {
s, ok := obj.(metav1.Object)
if !ok {
log.Info("Non-Object type resource passed to EnqueueCertificatesForSecretUsingPredicates")
return
}
// 'Construct' the predicate functions using the given Secret
predicates := make(predicate.Funcs, len(predicateBuilders))
for i, b := range predicateBuilders {
predicates[i] = b(s.(runtime.Object))
}
certs, err := ListCertificatesMatchingPredicates(lister.Certificates(s.GetNamespace()), selector, predicates...)
if err != nil {
log.Error(err, "Failed listing Certificate resources")
return
}
for _, cert := range certs {
key, err := controllerpkg.KeyFunc(cert)
if err != nil {
log.Error(err, "Error determining 'key' for resource")
continue
}
queue.Add(key)
}
}
}
@@ -0,0 +1,51 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"certificate.go",
"certificaterequest.go",
"generic.go",
"predicate.go",
],
importpath = "github.com/jetstack/cert-manager/pkg/controller/expcertificates/internal/predicate",
visibility = ["//pkg/controller/expcertificates:__subpackages__"],
deps = [
"//pkg/apis/certmanager/v1alpha2:go_default_library",
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
"@io_k8s_apimachinery//pkg/runtime: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"],
)
go_test(
name = "go_default_test",
srcs = [
"certificate_test.go",
"certificaterequest_test.go",
"generic_test.go",
"predicate_test.go",
],
embed = [":go_default_library"],
deps = [
"//pkg/apis/certmanager/v1alpha2:go_default_library",
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
"@io_k8s_apimachinery//pkg/runtime:go_default_library",
"@io_k8s_apimachinery//pkg/runtime/schema:go_default_library",
"@io_k8s_apimachinery//pkg/types:go_default_library",
"@io_k8s_utils//pointer:go_default_library",
],
)
@@ -0,0 +1,46 @@
/*
Copyright 2020 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 predicate
import (
"k8s.io/apimachinery/pkg/runtime"
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
)
// CertificateSecretName returns a predicate that used to filter Certificates
// to only those with the given 'spec.secretName'.
func CertificateSecretName(name string) Func {
return func(obj runtime.Object) bool {
crt := obj.(*cmapi.Certificate)
return crt.Spec.SecretName == name
}
}
// CertificateSecretName returns a predicate that used to filter Certificates
// to only those with the given 'status.nextPrivateKeySecretName'.
// It is not possible to select Certificates with a 'nil' secret name using
// this predicate function.
func CertificateNextPrivateKeySecretName(name string) Func {
return func(obj runtime.Object) bool {
crt := obj.(*cmapi.Certificate)
if crt.Status.NextPrivateKeySecretName == nil {
return false
}
return *crt.Status.NextPrivateKeySecretName == name
}
}
@@ -0,0 +1,94 @@
/*
Copyright 2020 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 predicate
import (
"testing"
"k8s.io/utils/pointer"
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
)
func TestCertificateSecretName(t *testing.T) {
certWithSecretName := func(s string) *cmapi.Certificate {
return &cmapi.Certificate{
Spec: cmapi.CertificateSpec{SecretName: s},
}
}
tests := map[string]struct {
secretName string
cert *cmapi.Certificate
expected bool
}{
"returns true if secret name matches": {
secretName: "abc",
cert: certWithSecretName("abc"),
expected: true,
},
"returns false if secret name does not match": {
secretName: "abc",
cert: certWithSecretName("abcd"),
expected: false,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
got := CertificateSecretName(test.secretName)(test.cert)
if got != test.expected {
t.Errorf("unexpected response: got=%t, exp=%t", got, test.expected)
}
})
}
}
func TestCertificateNextPrivateKeySecretName(t *testing.T) {
certWithSecretName := func(s *string) *cmapi.Certificate {
return &cmapi.Certificate{
Status: cmapi.CertificateStatus{NextPrivateKeySecretName: s},
}
}
tests := map[string]struct {
secretName string
cert *cmapi.Certificate
expected bool
}{
"returns true if secret name matches": {
secretName: "abc",
cert: certWithSecretName(pointer.StringPtr("abc")),
expected: true,
},
"returns false if secret name does not match": {
secretName: "abc",
cert: certWithSecretName(pointer.StringPtr("abcd")),
expected: false,
},
"returns false if secret name is nil": {
secretName: "",
cert: certWithSecretName(nil),
expected: false,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
got := CertificateNextPrivateKeySecretName(test.secretName)(test.cert)
if got != test.expected {
t.Errorf("unexpected response: got=%t, exp=%t", got, test.expected)
}
})
}
}
@@ -0,0 +1,37 @@
/*
Copyright 2020 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 predicate
import (
"fmt"
"k8s.io/apimachinery/pkg/runtime"
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
)
// CertificateRequestRevision returns a predicate that used to filter
// CertificateRequest to only those with a given 'revision' number.
func CertificateRequestRevision(revision int) Func {
return func(obj runtime.Object) bool {
req := obj.(*cmapi.CertificateRequest)
if req.Annotations == nil {
return false
}
return req.Annotations[cmapi.CertificateRequestRevisionAnnotationKey] == fmt.Sprintf("%d", revision)
}
}
@@ -0,0 +1,78 @@
/*
Copyright 2020 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 predicate
import (
"fmt"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
)
func TestCertificateRequestRevision(t *testing.T) {
requestWithRevision := func(s int) *cmapi.CertificateRequest {
return &cmapi.CertificateRequest{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
cmapi.CertificateRequestRevisionAnnotationKey: fmt.Sprintf("%d", s),
},
},
}
}
tests := map[string]struct {
revision int
request *cmapi.CertificateRequest
expected bool
}{
"returns true if revision matches": {
revision: 30,
request: requestWithRevision(30),
expected: true,
},
"returns false if revision does not match": {
revision: 29,
request: requestWithRevision(30),
expected: false,
},
"returns false if revision is not set": {
revision: 0,
request: &cmapi.CertificateRequest{},
expected: false,
},
"returns false if revision is empty": {
revision: 0,
request: &cmapi.CertificateRequest{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
cmapi.CertificateRequestRevisionAnnotationKey: "",
},
},
},
expected: false,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
got := CertificateRequestRevision(test.revision)(test.request)
if got != test.expected {
t.Errorf("unexpected response: got=%t, exp=%t", got, test.expected)
}
})
}
}
@@ -0,0 +1,38 @@
/*
Copyright 2020 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 predicate
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// ResourceOwnedBy will filter returned results to only those with the
// given resource as an owner.
func ResourceOwnedBy(owner runtime.Object) Func {
return func(obj runtime.Object) bool {
return metav1.IsControlledBy(obj.(metav1.Object), owner.(metav1.Object))
}
}
// ResourceOwnerOf will filter returned results to only those that own the given
// resource.
func ResourceOwnerOf(obj runtime.Object) Func {
return func(ownerObj runtime.Object) bool {
return metav1.IsControlledBy(obj.(metav1.Object), ownerObj.(metav1.Object))
}
}
@@ -0,0 +1,104 @@
/*
Copyright 2020 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 predicate
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
)
func TestResourceOwnedBy(t *testing.T) {
baseGVK := cmapi.SchemeGroupVersion.WithKind("CertificateRequest")
request := func(name string) *cmapi.CertificateRequest {
return &cmapi.CertificateRequest{ObjectMeta: metav1.ObjectMeta{Name: name, UID: types.UID(name)}}
}
requestWithOwner := func(owner metav1.Object, gvk schema.GroupVersionKind) *cmapi.CertificateRequest {
return &cmapi.CertificateRequest{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(owner, gvk)},
},
}
}
tests := map[string]struct {
owner runtime.Object
obj runtime.Object
expected bool
}{
"returns true if resource does own the resource": {
owner: request("base"),
obj: requestWithOwner(request("base"), baseGVK),
expected: true,
},
"returns false if resource does not own the resource": {
owner: request("base"),
obj: requestWithOwner(request("notbase"), baseGVK),
expected: false,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
got := ResourceOwnedBy(test.owner)(test.obj)
if got != test.expected {
t.Errorf("unexpected response: got=%t, exp=%t", got, test.expected)
}
})
}
}
func TestResourceOwnerOf(t *testing.T) {
baseGVK := cmapi.SchemeGroupVersion.WithKind("CertificateRequest")
request := func(name string) *cmapi.CertificateRequest {
return &cmapi.CertificateRequest{ObjectMeta: metav1.ObjectMeta{Name: name, UID: types.UID(name)}}
}
requestWithOwner := func(owner metav1.Object, gvk schema.GroupVersionKind) *cmapi.CertificateRequest {
return &cmapi.CertificateRequest{
ObjectMeta: metav1.ObjectMeta{
OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(owner, gvk)},
},
}
}
tests := map[string]struct {
ownee runtime.Object
obj runtime.Object
expected bool
}{
"returns true if resource is owned by object": {
ownee: requestWithOwner(request("base"), baseGVK),
obj: request("base"),
expected: true,
},
"returns false if resource is not owned by object": {
ownee: requestWithOwner(request("notbase"), baseGVK),
obj: request("base"),
expected: false,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
got := ResourceOwnerOf(test.ownee)(test.obj)
if got != test.expected {
t.Errorf("unexpected response: got=%t, exp=%t", got, test.expected)
}
})
}
}
@@ -0,0 +1,58 @@
/*
Copyright 2020 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 predicate
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// Func is a generic function used to filter various types of resources.
type Func func(obj runtime.Object) bool
// Funcs is a list of predicates to be AND'd together.
type Funcs []Func
// Evaluate will evaluate all the predicate functions in order, AND'ing
// together the results.
func (f Funcs) Evaluate(obj runtime.Object) bool {
for _, fn := range f {
if !fn(obj) {
return false
}
}
return true
}
// An ExtractorFunc applies a transformation to a runtime.Object and creates a
// predicate function based on the result of the transformation.
// This can be used to apply complex lookup logic to determine which resources
// should be enqueued if another resource being watched changes, for example,
// enqueuing all Certificate resources that own a CertificateRequest that has
// been observed, or enqueuing all Certificate resources that specify
// `status.nextPrivateKeySecretName` as the name of the Secret being processed.
type ExtractorFunc func(obj runtime.Object) Func
// ExtractResourceName is a helper function used to extract a name from a
// metav1.Object being enqueued to construct a Func that is variadic
// based on a string value.
func ExtractResourceName(p func(s string) Func) ExtractorFunc {
return func(obj runtime.Object) Func {
metaObj := obj.(metav1.Object)
return p(metaObj.GetName())
}
}
@@ -0,0 +1,83 @@
/*
Copyright 2020 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 predicate
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
)
func TestFuncs_Evaluate(t *testing.T) {
falseFunc := func(_ runtime.Object) bool {
return false
}
trueFunc := func(_ runtime.Object) bool {
return true
}
tests := map[string]struct {
funcs Funcs
expected bool
}{
"returns false if one returns false": {
funcs: Funcs{falseFunc},
expected: false,
},
"returns false if at least one returns false": {
funcs: Funcs{falseFunc, trueFunc},
expected: false,
},
"returns false if at least one returns false (reversed)": {
funcs: Funcs{trueFunc, falseFunc},
expected: false,
},
"returns true if all return true": {
funcs: Funcs{trueFunc, trueFunc},
expected: true,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
got := test.funcs.Evaluate(nil)
if got != test.expected {
t.Errorf("unexpected response: got=%t, exp=%t", got, test.expected)
}
})
}
}
func TestExtractResourceName(t *testing.T) {
expectedValue := "expected-value"
called := false
fn := ExtractResourceName(func(s string) Func {
called = true
if s != expectedValue {
t.Errorf("function called with unexpected value: got=%s, exp=%s", s, expectedValue)
}
return nil
})
obj := &cmapi.Certificate{ObjectMeta: metav1.ObjectMeta{Name: expectedValue}}
fn(obj)
if !called {
t.Fatal("unexpected error - function not called!")
}
}
@@ -18,6 +18,7 @@ go_library(
"//pkg/client/listers/certmanager/v1alpha2:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/controller/expcertificates:go_default_library",
"//pkg/controller/expcertificates/internal/predicate:go_default_library",
"//pkg/logs:go_default_library",
"//pkg/util/kube:go_default_library",
"//pkg/util/pki:go_default_library",
@@ -42,6 +42,7 @@ import (
cmlisters "github.com/jetstack/cert-manager/pkg/client/listers/certmanager/v1alpha2"
controllerpkg "github.com/jetstack/cert-manager/pkg/controller"
certificates "github.com/jetstack/cert-manager/pkg/controller/expcertificates"
"github.com/jetstack/cert-manager/pkg/controller/expcertificates/internal/predicate"
logf "github.com/jetstack/cert-manager/pkg/logs"
utilkube "github.com/jetstack/cert-manager/pkg/util/kube"
utilpki "github.com/jetstack/cert-manager/pkg/util/pki"
@@ -94,12 +95,12 @@ func NewController(
certificateInformer.Informer().AddEventHandler(&controllerpkg.QueuingEventHandler{Queue: queue})
certificateRequestInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{
WorkFunc: controllerpkg.HandleOwnedResourceNamespacedFunc(log, queue, certificateGvk, certificates.CertificateGetFunc(certificateInformer.Lister())),
WorkFunc: certificates.EnqueueCertificatesForResourceUsingPredicates(log, queue, certificateInformer.Lister(), labels.Everything(), predicate.ResourceOwnerOf),
})
secretsInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{
// Issuer reconciles on changes to the Secret named `spec.nextPrivateKeySecretName`
WorkFunc: certificates.EnqueueCertificatesForSecretNameFunc(log, certificateInformer.Lister(), labels.Everything(),
certificates.WithNextPrivateKeySecretNamePredicateFunc, queue),
WorkFunc: certificates.EnqueueCertificatesForResourceUsingPredicates(log, queue, certificateInformer.Lister(), labels.Everything(),
predicate.ExtractResourceName(predicate.CertificateNextPrivateKeySecretName)),
})
// build a list of InformerSynced functions that will be returned by the Register method.
@@ -174,8 +175,8 @@ func (c *controller) ProcessItem(ctx context.Context, key string) error {
reqs, err := certificates.ListCertificateRequestsMatchingPredicates(c.certificateRequestLister.CertificateRequests(crt.Namespace),
labels.Everything(),
certificates.WithCertificateRevisionPredicateFunc(nextRevision),
certificates.WithCertificateRequestOwnerPredicateFunc(crt),
predicate.CertificateRequestRevision(nextRevision),
predicate.ResourceOwnedBy(crt),
)
if err != nil || len(reqs) != 1 {
// If error return.
@@ -238,7 +239,6 @@ func (c *controller) failIssueCertificate(ctx context.Context, log logr.Logger,
// certificate, and then store the certificate, CA and private key into the
// Secret in the appropriate format type.
func (c *controller) issueCertificate(ctx context.Context, log logr.Logger, nextRevision int, crt *cmapi.Certificate, req *cmapi.CertificateRequest) error {
csr, err := utilpki.DecodeX509CertificateRequestBytes(req.Spec.CSRPEM)
if err != nil {
return err
+63
View File
@@ -0,0 +1,63 @@
/*
Copyright 2020 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 certificates
import (
"k8s.io/apimachinery/pkg/labels"
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
cmlisters "github.com/jetstack/cert-manager/pkg/client/listers/certmanager/v1alpha2"
"github.com/jetstack/cert-manager/pkg/controller/expcertificates/internal/predicate"
)
// ListCertificateRequestsMatchingPredicates will list CertificateRequest
// resources using the provided lister, optionally applying the given predicate
// functions to filter the CertificateRequest resources returned.
func ListCertificateRequestsMatchingPredicates(lister cmlisters.CertificateRequestNamespaceLister, selector labels.Selector, predicates ...predicate.Func) ([]*cmapi.CertificateRequest, error) {
reqs, err := lister.List(selector)
if err != nil {
return nil, err
}
funcs := predicate.Funcs(predicates)
out := make([]*cmapi.CertificateRequest, 0)
for _, req := range reqs {
if funcs.Evaluate(req) {
out = append(out, req)
}
}
return out, nil
}
// ListCertificatesMatchingPredicates will list Certificate resources using
// the provided lister, optionally applying the given predicate functions to
// filter the Certificate resources returned.
func ListCertificatesMatchingPredicates(lister cmlisters.CertificateNamespaceLister, selector labels.Selector, predicates ...predicate.Func) ([]*cmapi.Certificate, error) {
reqs, err := lister.List(selector)
if err != nil {
return nil, err
}
funcs := predicate.Funcs(predicates)
out := make([]*cmapi.Certificate, 0)
for _, req := range reqs {
if funcs.Evaluate(req) {
out = append(out, req)
}
}
return out, nil
}
@@ -17,6 +17,7 @@ go_library(
"//pkg/client/listers/certmanager/v1alpha2:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/controller/expcertificates:go_default_library",
"//pkg/controller/expcertificates/internal/predicate:go_default_library",
"//pkg/logs:go_default_library",
"//pkg/util/pki:go_default_library",
"@com_github_go_logr_logr//:go_default_library",
@@ -41,6 +41,7 @@ import (
cmlisters "github.com/jetstack/cert-manager/pkg/client/listers/certmanager/v1alpha2"
controllerpkg "github.com/jetstack/cert-manager/pkg/controller"
certificates "github.com/jetstack/cert-manager/pkg/controller/expcertificates"
"github.com/jetstack/cert-manager/pkg/controller/expcertificates/internal/predicate"
logf "github.com/jetstack/cert-manager/pkg/logs"
)
@@ -86,13 +87,16 @@ func NewController(
secretsInformer := factory.Core().V1().Secrets()
certificateInformer.Informer().AddEventHandler(&controllerpkg.QueuingEventHandler{Queue: queue})
// When a CertificateRequest resource changes, enqueue the Certificate resource that owns it.
certificateRequestInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{
WorkFunc: controllerpkg.HandleOwnedResourceNamespacedFunc(log, queue, certificateGvk, certificates.CertificateGetFunc(certificateInformer.Lister())),
WorkFunc: certificates.EnqueueCertificatesForResourceUsingPredicates(log, queue, certificateInformer.Lister(), labels.Everything(), predicate.ResourceOwnerOf),
})
// When a Secret resource changes, enqueue any Certificate resources that name it as spec.secretName.
secretsInformer.Informer().AddEventHandler(&controllerpkg.BlockingEventHandler{
// Trigger reconciles on changes to the Secret named `spec.secretName`
WorkFunc: certificates.EnqueueCertificatesForSecretNameFunc(log, certificateInformer.Lister(), labels.Everything(),
certificates.WithSecretNamePredicateFunc, queue),
WorkFunc: certificates.EnqueueCertificatesForResourceUsingPredicates(log, queue, certificateInformer.Lister(), labels.Everything(),
predicate.ExtractResourceName(predicate.CertificateSecretName)),
})
// build a list of InformerSynced functions that will be returned by the Register method.
@@ -184,8 +188,8 @@ func (c *controller) buildPolicyInputForCertificate(ctx context.Context, crt *cm
if crt.Status.Revision != nil {
reqs, err := certificates.ListCertificateRequestsMatchingPredicates(c.certificateRequestLister.CertificateRequests(crt.Namespace),
labels.Everything(),
certificates.WithCertificateRequestOwnerPredicateFunc(crt),
certificates.WithCertificateRevisionPredicateFunc(*crt.Status.Revision),
predicate.ResourceOwnedBy(crt),
predicate.CertificateRequestRevision(*crt.Status.Revision),
)
if err != nil {
return PolicyData{}, err
-118
View File
@@ -17,133 +17,15 @@ limitations under the License.
package certificates
import (
"fmt"
"reflect"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/util/workqueue"
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
cmlisters "github.com/jetstack/cert-manager/pkg/client/listers/certmanager/v1alpha2"
controllerpkg "github.com/jetstack/cert-manager/pkg/controller"
"github.com/jetstack/cert-manager/pkg/util"
"github.com/jetstack/cert-manager/pkg/util/pki"
)
type GetFunc func(namespace, name string) (interface{}, error)
func CertificateGetFunc(lister cmlisters.CertificateLister) GetFunc {
return func(namespace, name string) (interface{}, error) {
return lister.Certificates(namespace).Get(name)
}
}
// EnqueueCertificatesForSecretNameFunc will enqueue Certificate resources that
// satisfy a CertificatePredicateFunc based upon the name of the Secret resource
// being processed.
// This is used to trigger Certificates to reconcile for changes to the Secret
// being managed.
func EnqueueCertificatesForSecretNameFunc(log logr.Logger, lister cmlisters.CertificateLister, selector labels.Selector,
secretNamePredicate WithCertificatePredicateFunc, queue workqueue.Interface) func(obj interface{}) {
return func(obj interface{}) {
s, ok := obj.(*corev1.Secret)
if !ok {
log.Info("Non-Secret type resource passed to EnqueueCertificatesForSecretFunc")
return
}
certs, err := ListCertificatesMatchingPredicate(lister.Certificates(s.Namespace), selector, secretNamePredicate(s.Name))
if err != nil {
log.Error(err, "Failed listing Certificate resources")
return
}
for _, cert := range certs {
key, err := controllerpkg.KeyFunc(cert)
if err != nil {
log.Error(err, "Error determining 'key' for resource")
continue
}
queue.Add(key)
}
}
}
type WithCertificatePredicateFunc func(string) CertificatePredicateFunc
type CertificatePredicateFunc func(*cmapi.Certificate) bool
func WithSecretNamePredicateFunc(name string) CertificatePredicateFunc {
return func(crt *cmapi.Certificate) bool {
return crt.Spec.SecretName == name
}
}
func WithNextPrivateKeySecretNamePredicateFunc(name string) CertificatePredicateFunc {
return func(crt *cmapi.Certificate) bool {
if crt.Status.NextPrivateKeySecretName == nil {
return false
}
return *crt.Status.NextPrivateKeySecretName == name
}
}
func ListCertificatesMatchingPredicate(lister cmlisters.CertificateNamespaceLister, selector labels.Selector, predicate CertificatePredicateFunc) ([]*cmapi.Certificate, error) {
crts, err := lister.List(selector)
if err != nil {
return nil, err
}
out := make([]*cmapi.Certificate, 0)
for _, crt := range crts {
if predicate(crt) {
out = append(out, crt)
}
}
return out, nil
}
type CertificateRequestPredicateFunc func(*cmapi.CertificateRequest) bool
func WithCertificateRevisionPredicateFunc(revision int) CertificateRequestPredicateFunc {
return func(req *cmapi.CertificateRequest) bool {
if req.Annotations == nil {
return false
}
return req.Annotations[cmapi.CertificateRequestRevisionAnnotationKey] == fmt.Sprintf("%d", revision)
}
}
func WithCertificateRequestOwnerPredicateFunc(owner metav1.Object) CertificateRequestPredicateFunc {
return func(req *cmapi.CertificateRequest) bool {
return metav1.IsControlledBy(req, owner)
}
}
func ListCertificateRequestsMatchingPredicates(lister cmlisters.CertificateRequestNamespaceLister, selector labels.Selector, predicates ...CertificateRequestPredicateFunc) ([]*cmapi.CertificateRequest, error) {
reqs, err := lister.List(selector)
if err != nil {
return nil, err
}
out := make([]*cmapi.CertificateRequest, 0)
for _, req := range reqs {
matches := true
for _, predicate := range predicates {
if !predicate(req) {
matches = false
break
}
}
if matches {
out = append(out, req)
}
}
return out, nil
}
// RequestMatchesSpec compares a CertificateRequest with a CertificateSpec
// and returns a list of field names on the Certificate that do not match their
// counterpart fields on the CertificateRequest.