Merge pull request #4205 from inteon/kubectl_check_api

Add kubectl 'cert-manager check api' command
This commit is contained in:
jetstack-bot
2021-07-16 14:43:15 +01:00
committed by GitHub
13 changed files with 664 additions and 0 deletions
+1
View File
@@ -34,6 +34,7 @@ filegroup(
":package-srcs",
"//cmd/ctl/cmd:all-srcs",
"//cmd/ctl/pkg/approve:all-srcs",
"//cmd/ctl/pkg/check:all-srcs",
"//cmd/ctl/pkg/convert:all-srcs",
"//cmd/ctl/pkg/create:all-srcs",
"//cmd/ctl/pkg/deny:all-srcs",
+1
View File
@@ -7,6 +7,7 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//cmd/ctl/pkg/approve:go_default_library",
"//cmd/ctl/pkg/check:go_default_library",
"//cmd/ctl/pkg/convert:go_default_library",
"//cmd/ctl/pkg/create:go_default_library",
"//cmd/ctl/pkg/deny:go_default_library",
+2
View File
@@ -31,6 +31,7 @@ import (
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"github.com/jetstack/cert-manager/cmd/ctl/pkg/approve"
"github.com/jetstack/cert-manager/cmd/ctl/pkg/check"
"github.com/jetstack/cert-manager/cmd/ctl/pkg/convert"
"github.com/jetstack/cert-manager/cmd/ctl/pkg/create"
"github.com/jetstack/cert-manager/cmd/ctl/pkg/deny"
@@ -74,6 +75,7 @@ kubectl cert-manager is a CLI tool manage and configure cert-manager resources f
cmds.AddCommand(inspect.NewCmdInspect(ctx, ioStreams, factory))
cmds.AddCommand(approve.NewCmdApprove(ctx, ioStreams, factory))
cmds.AddCommand(deny.NewCmdDeny(ctx, ioStreams, factory))
cmds.AddCommand(check.NewCmdCheck(ctx, ioStreams, factory))
// Experimental features
cmds.AddCommand(experimental.NewCmdExperimental(ctx, ioStreams, factory))
+1
View File
@@ -33,5 +33,6 @@ func main() {
if err := cmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
}
+31
View File
@@ -0,0 +1,31 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["check.go"],
importpath = "github.com/jetstack/cert-manager/cmd/ctl/pkg/check",
visibility = ["//visibility:public"],
deps = [
"//cmd/ctl/pkg/check/api:go_default_library",
"@com_github_spf13_cobra//:go_default_library",
"@io_k8s_cli_runtime//pkg/genericclioptions:go_default_library",
"@io_k8s_kubectl//pkg/cmd/util:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//cmd/ctl/pkg/check/api:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
+32
View File
@@ -0,0 +1,32 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["api.go"],
importpath = "github.com/jetstack/cert-manager/cmd/ctl/pkg/check/api",
visibility = ["//visibility:public"],
deps = [
"//pkg/util/cmapichecker:go_default_library",
"@com_github_spf13_cobra//:go_default_library",
"@io_k8s_apimachinery//pkg/util/wait:go_default_library",
"@io_k8s_cli_runtime//pkg/genericclioptions:go_default_library",
"@io_k8s_kubectl//pkg/cmd/util:go_default_library",
"@io_k8s_kubectl//pkg/scheme:go_default_library",
"@io_k8s_kubectl//pkg/util/i18n:go_default_library",
"@io_k8s_kubectl//pkg/util/templates: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"],
)
+157
View File
@@ -0,0 +1,157 @@
/*
Copyright 2021 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package api
import (
"context"
"errors"
"fmt"
"log"
"os"
"time"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/cli-runtime/pkg/genericclioptions"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/jetstack/cert-manager/pkg/util/cmapichecker"
)
// Options is a struct to support check api command
type Options struct {
// APIChecker is used to check that the cert-manager CRDs have been installed on the K8S
// API server and that the cert-manager webhooks are all working
APIChecker cmapichecker.Interface
// Time before timeout when waiting
Wait time.Duration
// Time between checks when waiting
Interval time.Duration
// Namespace that is used to dry-run create the certificate resource in
Namespace string
// Print details regarding encountered errors
Verbose bool
genericclioptions.IOStreams
}
var checkApiDesc = templates.LongDesc(i18n.T(`
This check attempts to perform a dry-run create of a cert-manager *v1alpha2*
Certificate resource in order to verify that CRDs are installed and all the
required webhooks are reachable by the K8S API server.
We use v1alpha2 API to ensure that the API server has also connected to the
cert-manager conversion webhook.`))
// NewOptions returns initialized Options
func NewOptions(ioStreams genericclioptions.IOStreams) *Options {
return &Options{
IOStreams: ioStreams,
}
}
// Complete takes the command arguments and factory and infers any remaining options.
func (o *Options) Complete(factory cmdutil.Factory) error {
var err error
o.Namespace, _, err = factory.ToRawKubeConfigLoader().Namespace()
if err != nil {
return fmt.Errorf("Error: cannot get the namespace: %v", err)
}
restConfig, err := factory.ToRESTConfig()
if err != nil {
return fmt.Errorf("Error: cannot create the REST config: %v", err)
}
// We pass the scheme that is used in the RESTConfig's NegotiatedSerializer,
// this makes sure that the cmapi is also added to NegotiatedSerializer's scheme
// see: https://github.com/jetstack/cert-manager/pull/4205#discussion_r668660271
o.APIChecker, err = cmapichecker.New(restConfig, scheme.Scheme, o.Namespace)
if err != nil {
return fmt.Errorf("Error: %v", err)
}
return nil
}
// NewCmdCheckApi returns a cobra command for checking creating cert-manager resources against the K8S API server
func NewCmdCheckApi(ctx context.Context, ioStreams genericclioptions.IOStreams, factory cmdutil.Factory) *cobra.Command {
o := NewOptions(ioStreams)
cmd := &cobra.Command{
Use: "api",
Short: "Check if the cert-manager API is ready",
Long: checkApiDesc,
RunE: func(cmd *cobra.Command, args []string) error {
if err := o.Complete(factory); err != nil {
return err
}
o.Run(ctx)
return nil
},
SilenceUsage: true,
SilenceErrors: true,
}
cmd.Flags().DurationVar(&o.Wait, "wait", 0, "Wait until the cert-manager API is ready (default 0s)")
cmd.Flags().DurationVar(&o.Interval, "interval", 5*time.Second, "Time between checks when waiting, must include unit, e.g. 1m or 10m")
cmd.Flags().BoolVarP(&o.Verbose, "verbose", "v", false, "Print detailed error messages")
return cmd
}
// Run executes check api command
func (o *Options) Run(ctx context.Context) {
if !o.Verbose {
log.SetFlags(0) // Disable prefixing logs with timestamps.
}
log.SetOutput(o.ErrOut) // Log all intermediate errors to stderr
pollContext, cancel := context.WithTimeout(ctx, o.Wait)
defer cancel()
pollErr := wait.PollImmediateUntil(o.Interval, func() (done bool, err error) {
if err := o.APIChecker.Check(ctx); err != nil {
if !o.Verbose && errors.Unwrap(err) != nil {
err = errors.Unwrap(err)
}
log.Printf("Not ready: %v", err)
return false, nil
}
return true, nil
}, pollContext.Done())
log.SetOutput(o.Out) // Log conclusion to stdout
if pollErr != nil {
if errors.Is(pollContext.Err(), context.DeadlineExceeded) && o.Wait > 0 {
log.Printf("Timed out after %s", o.Wait)
}
os.Exit(1)
}
log.Printf("The cert-manager API is ready")
}
+43
View File
@@ -0,0 +1,43 @@
/*
Copyright 2021 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package check
import (
"context"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"github.com/jetstack/cert-manager/cmd/ctl/pkg/check/api"
)
func NewCmdCheck(ctx context.Context, ioStreams genericclioptions.IOStreams, factory cmdutil.Factory) *cobra.Command {
cmds := NewCmdCreateBare()
cmds.AddCommand(api.NewCmdCheckApi(ctx, ioStreams, factory))
return cmds
}
// Create a bare Create Command, without any subcommands
func NewCmdCreateBare() *cobra.Command {
return &cobra.Command{
Use: "check",
Short: "Check cert-manager components",
Long: `Check cert-manager components`,
}
}
+6
View File
@@ -31,6 +31,7 @@ SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")
# Require kubectl & helm available on PATH
check_tool kubectl
check_tool kubectl-cert_manager
check_tool helm
# Use the current timestamp as the APP_VERSION so a rolling update will be
@@ -52,6 +53,9 @@ kubectl get namespace "${NAMESPACE}" || kubectl create namespace "${NAMESPACE}"
# Build the Helm chart package .tgz
bazel build //deploy/charts/cert-manager
# Pre-compile the kubectl plugin, so it can quickly check the api status
bazel build //hack/bin:kubectl-cert_manager
# Upgrade or install cert-manager
helm upgrade \
--install \
@@ -65,3 +69,5 @@ helm upgrade \
--set "extraArgs={--dns01-recursive-nameservers=${SERVICE_IP_PREFIX}.16:53,--dns01-recursive-nameservers-only=true,--controllers=*\,gateway-shim}" \
"$RELEASE_NAME" \
"$REPO_ROOT/bazel-bin/deploy/charts/cert-manager/cert-manager.tgz"
kubectl cert-manager check api --wait=1m -v
+1
View File
@@ -33,6 +33,7 @@ filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/util/cmapichecker:all-srcs",
"//pkg/util/cmd:all-srcs",
"//pkg/util/coverage:all-srcs",
"//pkg/util/errors:all-srcs",
+43
View File
@@ -0,0 +1,43 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["cmapichecker.go"],
importpath = "github.com/jetstack/cert-manager/pkg/util/cmapichecker",
visibility = ["//visibility:public"],
deps = [
"//pkg/apis/certmanager/v1alpha2:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
"@io_k8s_apimachinery//pkg/runtime:go_default_library",
"@io_k8s_client_go//rest:go_default_library",
"@io_k8s_sigs_controller_runtime//pkg/client: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 = ["cmapichecker_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/apis/certmanager/v1alpha2:go_default_library",
"@io_k8s_apimachinery//pkg/runtime:go_default_library",
"@io_k8s_sigs_controller_runtime//pkg/client:go_default_library",
"@io_k8s_sigs_controller_runtime//pkg/client/fake:go_default_library",
],
)
+159
View File
@@ -0,0 +1,159 @@
/*
Copyright 2021 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmapichecker
import (
"context"
"fmt"
"regexp"
errors "github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
// Use v1alpha2 API to ensure that the API server has also connected to the
// cert-manager conversion webhook.
// TODO(wallrj): Only change this when the old deprecated APIs are removed,
// at which point the conversion webhook may be removed anyway.
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
)
var (
ErrCertManagerCRDsNotFound = errors.New("the cert-manager CRDs are not yet installed on the Kubernetes API server")
ErrWebhookServiceFailure = errors.New("the cert-manager webhook service is not created yet")
ErrWebhookDeploymentFailure = errors.New("the cert-manager webhook deployment is not ready yet")
ErrWebhookCertificateFailure = errors.New("the cert-manager webhook CA bundle is not injected yet")
)
const (
crdsMappingError = `error finding the scope of the object: failed to get restmapping: no matches for kind "Certificate" in group "cert-manager.io"`
crdsNotFoundError = `the server could not find the requested resource (post certificates.cert-manager.io)`
)
var (
regexErrCertManagerCRDsNotFound = regexp.MustCompile(`^(` + regexp.QuoteMeta(crdsMappingError) + `|` + regexp.QuoteMeta(crdsNotFoundError) + `)$`)
regexErrWebhookServiceFailure = regexp.MustCompile(`Post "(.*)": service "(.*)-webhook" not found`)
regexErrWebhookDeploymentFailure = regexp.MustCompile(`Post "(.*)": (.*): connect: connection refused`)
regexErrWebhookCertificateFailure = regexp.MustCompile(`Post "(.*)": x509: certificate signed by unknown authority`)
)
// Interface is used to check that the cert-manager CRDs have been installed and are usable.
type Interface interface {
Check(context.Context) error
}
type cmapiChecker struct {
client client.Client
}
// New returns a cert-manager API checker
func New(restcfg *rest.Config, scheme *runtime.Scheme, namespace string) (Interface, error) {
if err := cmapi.AddToScheme(scheme); err != nil {
return nil, errors.Wrap(err, "while configuring scheme")
}
cl, err := client.New(restcfg, client.Options{
Scheme: scheme,
})
if err != nil {
return nil, errors.Wrap(err, "while creating client")
}
return &cmapiChecker{
client: client.NewNamespacedClient(client.NewDryRunClient(cl), namespace),
}, nil
}
// Check attempts to perform a dry-run create of a cert-manager *v1alpha2*
// Certificate resource in order to verify that CRDs are installed and all the
// required webhooks are reachable by the K8S API server.
// We use v1alpha2 API to ensure that the API server has also connected to the
// cert-manager conversion webhook.
func (o *cmapiChecker) Check(ctx context.Context) error {
cert := &cmapi.Certificate{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "cmapichecker-",
},
Spec: cmapi.CertificateSpec{
DNSNames: []string{"cmapichecker.example"},
SecretName: "cmapichecker",
IssuerRef: cmmeta.ObjectReference{
Name: "cmapichecker",
},
},
}
if err := o.client.Create(ctx, cert); err != nil {
return &ApiCheckError{
SimpleError: translateToSimpleError(err),
UnderlyingError: err,
}
}
return nil
}
type ApiCheckError struct {
SimpleError error
UnderlyingError error
}
func (e *ApiCheckError) Error() string {
// If no simple error exists, print underlying error
if e.SimpleError == nil {
return e.UnderlyingError.Error()
}
return fmt.Sprintf("%v (%v)", e.SimpleError.Error(), e.UnderlyingError.Error())
}
// If no simple error exists, this function will return nil
// which indicates that the error is not unwrappable
func (e *ApiCheckError) Unwrap() error {
return e.SimpleError
}
// This translateToSimpleError function detects errors based on the error message.
// It tries to map these error messages to a better understandable error message that
// explains what is wrong. If it cannot create a simple error, it will return nil.
// ErrCertManagerCRDsNotFound:
// - error finding the scope of the object: failed to get restmapping: no matches for kind "Certificate" in group "cert-manager.io"
// ErrWebhookServiceFailure:
// - Internal error occurred: failed calling webhook "webhook.cert-manager.io": Post "https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=10s": service "cert-manager-webhook" not found
// - conversion webhook for cert-manager.io/v1alpha2, Kind=Certificate failed: Post "https://cert-manager-webhook.cert-manager.svc:443/convert?timeout=30s": service "cert-manager-webhook" not found
// ErrWebhookDeploymentFailure:
// - Internal error occurred: failed calling webhook "webhook.cert-manager.io": Post "https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=10s": dial tcp 10.96.38.90:443: connect: connection refused
// - conversion webhook for cert-manager.io/v1alpha2, Kind=Certificate failed: Post "https://cert-manager-webhook.cert-manager.svc:443/convert?timeout=30s": dial tcp 10.96.38.90:443: connect: connection refused
// ErrWebhookCertificateFailure:
// - Internal error occurred: failed calling webhook "webhook.cert-manager.io": Post "https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=10s": x509: certificate signed by unknown authority (possibly because of "x509: ECDSA verification failure" while trying to verify candidate authority certificate "cert-manager-webhook-ca")
// - conversion webhook for cert-manager.io/v1alpha2, Kind=Certificate failed: Post "https://cert-manager-webhook.cert-manager.svc:443/convert?timeout=30s": x509: certificate signed by unknown authority
func translateToSimpleError(err error) error {
s := err.Error()
if regexErrCertManagerCRDsNotFound.MatchString(s) {
return ErrCertManagerCRDsNotFound
} else if regexErrWebhookServiceFailure.MatchString(s) {
return ErrWebhookServiceFailure
} else if regexErrWebhookDeploymentFailure.MatchString(s) {
return ErrWebhookDeploymentFailure
} else if regexErrWebhookCertificateFailure.MatchString(s) {
return ErrWebhookCertificateFailure
}
return nil
}
+187
View File
@@ -0,0 +1,187 @@
/*
Copyright 2021 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmapichecker
import (
"context"
"errors"
"fmt"
"testing"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
)
type fakeErrorClient struct {
client.Client
createError error
}
func (cl *fakeErrorClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
if cl.createError != nil {
return cl.createError
}
return cl.Client.Create(ctx, obj, opts...)
}
func newFakeCmapiChecker() (*fakeErrorClient, Interface, error) {
scheme := runtime.NewScheme()
if err := cmapi.AddToScheme(scheme); err != nil {
return nil, nil, err
}
cl := fake.NewClientBuilder().WithScheme(scheme).Build()
errorClient := &fakeErrorClient{
Client: cl,
createError: nil,
}
return errorClient, &cmapiChecker{
client: errorClient,
}, nil
}
const (
errCertManagerCRDsMapping = `error finding the scope of the object: failed to get restmapping: no matches for kind "Certificate" in group "cert-manager.io"`
errCertManagerCRDsNotFound = `the server could not find the requested resource (post certificates.cert-manager.io)`
errMutatingWebhookServiceFailure = `Internal error occurred: failed calling webhook "webhook.cert-manager.io": Post "https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=10s": service "cert-manager-webhook" not found`
errMutatingWebhookDeploymentFailure = `Internal error occurred: failed calling webhook "webhook.cert-manager.io": Post "https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=10s": dial tcp 10.96.38.90:443: connect: connection refused`
errMutatingWebhookCertificateFailure = `Internal error occurred: failed calling webhook "webhook.cert-manager.io": Post "https://cert-manager-webhook.cert-manager.svc:443/mutate?timeout=10s": x509: certificate signed by unknown authority (possibly because of "x509: ECDSA verification failure" while trying to verify candidate authority certificate "cert-manager-webhook-ca"`
errConversionWebhookServiceFailure = `conversion webhook for cert-manager.io/v1alpha2, Kind=Certificate failed: Post "https://cert-manager-webhook.cert-manager.svc:443/convert?timeout=30s": service "cert-manager-webhook" not found`
errConversionWebhookDeploymentFailure = `conversion webhook for cert-manager.io/v1alpha2, Kind=Certificate failed: Post "https://cert-manager-webhook.cert-manager.svc:443/convert?timeout=30s": dial tcp 10.96.38.90:443: connect: connection refused`
errConversionWebhookCertificateFailure = `conversion webhook for cert-manager.io/v1alpha2, Kind=Certificate failed: Post "https://cert-manager-webhook.cert-manager.svc:443/convert?timeout=30s": x509: certificate signed by unknown authority`
)
func TestCmapiChecker(t *testing.T) {
tests := map[string]testT{
"check API without errors": {
createError: nil,
expectedSimpleError: "",
expectedVerboseError: "",
},
"check API without CRDs installed 1": {
createError: errors.New(errCertManagerCRDsMapping),
expectedSimpleError: ErrCertManagerCRDsNotFound.Error(),
expectedVerboseError: fmt.Sprintf("%s (%s)", ErrCertManagerCRDsNotFound.Error(), errCertManagerCRDsMapping),
},
"check API without CRDs installed 2": {
createError: errors.New(errCertManagerCRDsNotFound),
expectedSimpleError: ErrCertManagerCRDsNotFound.Error(),
expectedVerboseError: fmt.Sprintf("%s (%s)", ErrCertManagerCRDsNotFound.Error(), errCertManagerCRDsNotFound),
},
"check API with mutating webhook service not ready": {
createError: errors.New(errMutatingWebhookServiceFailure),
expectedSimpleError: ErrWebhookServiceFailure.Error(),
expectedVerboseError: fmt.Sprintf("%s (%s)", ErrWebhookServiceFailure.Error(), errMutatingWebhookServiceFailure),
},
"check API with conversion webhook service not ready": {
createError: errors.New(errConversionWebhookServiceFailure),
expectedSimpleError: ErrWebhookServiceFailure.Error(),
expectedVerboseError: fmt.Sprintf("%s (%s)", ErrWebhookServiceFailure.Error(), errConversionWebhookServiceFailure),
},
"check API with mutating webhook pod not accepting connections": {
createError: errors.New(errMutatingWebhookDeploymentFailure),
expectedSimpleError: ErrWebhookDeploymentFailure.Error(),
expectedVerboseError: fmt.Sprintf("%s (%s)", ErrWebhookDeploymentFailure.Error(), errMutatingWebhookDeploymentFailure),
},
"check API with conversion webhook pod not accepting connections": {
createError: errors.New(errConversionWebhookDeploymentFailure),
expectedSimpleError: ErrWebhookDeploymentFailure.Error(),
expectedVerboseError: fmt.Sprintf("%s (%s)", ErrWebhookDeploymentFailure.Error(), errConversionWebhookDeploymentFailure),
},
"check API with webhook certificate not updated in mutation webhook resource definitions": {
createError: errors.New(errMutatingWebhookCertificateFailure),
expectedSimpleError: ErrWebhookCertificateFailure.Error(),
expectedVerboseError: fmt.Sprintf("%s (%s)", ErrWebhookCertificateFailure.Error(), errMutatingWebhookCertificateFailure),
},
"check API with webhook certificate not updated in conversion webhook resource definitions": {
createError: errors.New(errConversionWebhookCertificateFailure),
expectedSimpleError: ErrWebhookCertificateFailure.Error(),
expectedVerboseError: fmt.Sprintf("%s (%s)", ErrWebhookCertificateFailure.Error(), errConversionWebhookCertificateFailure),
},
"unexpected error": {
createError: errors.New("unexpected error"),
expectedSimpleError: "",
expectedVerboseError: "unexpected error",
},
}
for n, test := range tests {
t.Run(n, func(t *testing.T) {
runTest(t, test)
})
}
}
type testT struct {
createError error
expectedSimpleError string
expectedVerboseError string
}
func runTest(t *testing.T, test testT) {
errorClient, checker, err := newFakeCmapiChecker()
if err != nil {
t.Error(err)
}
errorClient.createError = test.createError
var unwrappedErr error
err = checker.Check(context.TODO())
if err != nil {
if err.Error() != test.expectedVerboseError {
t.Errorf("error differs from expected error:\n%s\n vs \n%s", err.Error(), test.expectedVerboseError)
}
unwrappedErr = errors.Unwrap(err)
} else {
if test.expectedVerboseError != "" {
t.Errorf("expected error did not occure:\n%s", test.expectedVerboseError)
}
}
if unwrappedErr != nil {
if unwrappedErr.Error() != test.expectedSimpleError {
t.Errorf("simple error differs from expected error:\n%s\n vs \n%s", unwrappedErr.Error(), test.expectedSimpleError)
}
} else {
if test.expectedSimpleError != "" {
t.Errorf("expected simple error did not occure:\n%s", test.expectedSimpleError)
}
}
}