mirror of
https://github.com/wahyd4/cert-manager.git
synced 2026-08-12 06:36:30 +10:00
Create 'predicate' package and standardise list and enqueue predicates
Signed-off-by: James Munnelly <james@munnelly.eu>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user