New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Detect different versions of cert-manager #1546
Changes from 2 commits
5c0d76d
4c24894
1ac91ca
473fe25
77840f5
a766e8a
d4d2005
45d5aa4
654b921
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ import ( | |
"fmt" | ||
"strings" | ||
|
||
"github.com/thoas/go-funk" | ||
admissionv1beta1 "k8s.io/api/admissionregistration/v1beta1" | ||
corev1 "k8s.io/api/core/v1" | ||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" | ||
|
@@ -28,41 +29,43 @@ import ( | |
var _ kudoinit.Step = &KudoWebHook{} | ||
|
||
type KudoWebHook struct { | ||
opts kudoinit.Options | ||
issuer unstructured.Unstructured | ||
certificate unstructured.Unstructured | ||
opts kudoinit.Options | ||
|
||
certManagerGroup string | ||
certManagerAPIVersion string | ||
|
||
issuer *unstructured.Unstructured | ||
certificate *unstructured.Unstructured | ||
} | ||
|
||
const ( | ||
certManagerAPIVersion = "v1alpha2" | ||
certManagerControllerVersion = "v0.12.0" | ||
certManagerOldGroup = "certmanager.k8s.io" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about a group -> api mapping instead like?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not necessarily a map... It might be a map of lists:
(at least I think "cert-manager.io" has "v1alpha1" as well in some version, it certainly has v1alpha2 and v1alpha3) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So even easier to check all permutatations 👍 |
||
certManagerNewGroup = "cert-manager.io" | ||
) | ||
|
||
var ( | ||
certManagerControllerImageSuffix = fmt.Sprintf("cert-manager-controller:%s", certManagerControllerVersion) | ||
certManagerAPIVersions = []string{"v1alpha2", "v1alpha1"} | ||
) | ||
|
||
func NewWebHookInitializer(options kudoinit.Options) KudoWebHook { | ||
return KudoWebHook{ | ||
opts: options, | ||
certificate: certificate(options.Namespace), | ||
issuer: issuer(options.Namespace), | ||
func NewWebHookInitializer(options kudoinit.Options) *KudoWebHook { | ||
return &KudoWebHook{ | ||
opts: options, | ||
} | ||
} | ||
|
||
func (k KudoWebHook) String() string { | ||
func (k *KudoWebHook) String() string { | ||
return "webhook" | ||
} | ||
|
||
func (k KudoWebHook) PreInstallVerify(client *kube.Client, result *verifier.Result) error { | ||
func (k *KudoWebHook) PreInstallVerify(client *kube.Client, result *verifier.Result) error { | ||
// skip verification if webhooks are not used or self-signed CA is used | ||
if k.opts.SelfSignedWebhookCA { | ||
return nil | ||
} | ||
return validateCertManagerInstallation(client, result) | ||
return k.validateCertManagerInstallation(client, result) | ||
} | ||
|
||
func (k KudoWebHook) installWithCertManager(client *kube.Client) error { | ||
func (k *KudoWebHook) installWithCertManager(client *kube.Client) error { | ||
if err := installUnstructured(client.DynamicClient, k.issuer); err != nil { | ||
return err | ||
} | ||
|
@@ -75,7 +78,7 @@ func (k KudoWebHook) installWithCertManager(client *kube.Client) error { | |
return nil | ||
} | ||
|
||
func (k KudoWebHook) installWithSelfSignedCA(client *kube.Client) error { | ||
func (k *KudoWebHook) installWithSelfSignedCA(client *kube.Client) error { | ||
iaw, s, err := k.resourcesWithSelfSignedCA() | ||
if err != nil { | ||
return nil | ||
|
@@ -92,14 +95,14 @@ func (k KudoWebHook) installWithSelfSignedCA(client *kube.Client) error { | |
return nil | ||
} | ||
|
||
func (k KudoWebHook) Install(client *kube.Client) error { | ||
func (k *KudoWebHook) Install(client *kube.Client) error { | ||
if k.opts.SelfSignedWebhookCA { | ||
return k.installWithSelfSignedCA(client) | ||
} | ||
return k.installWithCertManager(client) | ||
} | ||
|
||
func (k KudoWebHook) Resources() []runtime.Object { | ||
func (k *KudoWebHook) Resources() []runtime.Object { | ||
if k.opts.SelfSignedWebhookCA { | ||
iaw, s, err := k.resourcesWithSelfSignedCA() | ||
if err != nil { | ||
|
@@ -112,15 +115,19 @@ func (k KudoWebHook) Resources() []runtime.Object { | |
return k.resourcesWithCertManager() | ||
} | ||
|
||
func (k KudoWebHook) resourcesWithCertManager() []runtime.Object { | ||
func (k *KudoWebHook) resourcesWithCertManager() []runtime.Object { | ||
// We have to fall back to a default here as for a dry-run we can't detect the actual version of a cluster | ||
k.issuer = issuer(k.opts.Namespace, certManagerNewGroup, certManagerAPIVersions[0]) | ||
k.certificate = certificate(k.opts.Namespace, certManagerNewGroup, certManagerAPIVersions[0]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these lines of code... can we get rid of them? Issuer and Certificate do NOT seem to be installed with These should be complete removed which will resolve the remaining blocking issue. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, they are not used when With this refactoring they are detected in the They are then applied in the The problem with |
||
|
||
av := InstanceAdmissionWebhook(k.opts.Namespace) | ||
objs := []runtime.Object{&av} | ||
objs = append(objs, &k.issuer) | ||
objs = append(objs, &k.certificate) | ||
objs = append(objs, k.issuer) | ||
objs = append(objs, k.certificate) | ||
return objs | ||
} | ||
|
||
func (k KudoWebHook) resourcesWithSelfSignedCA() (*admissionv1beta1.MutatingWebhookConfiguration, *corev1.Secret, error) { | ||
func (k *KudoWebHook) resourcesWithSelfSignedCA() (*admissionv1beta1.MutatingWebhookConfiguration, *corev1.Secret, error) { | ||
tinyCA, err := NewTinyCA(kudoinit.DefaultServiceName, k.opts.Namespace) | ||
if err != nil { | ||
return nil, nil, fmt.Errorf("unable to set up webhook CA: %v", err) | ||
|
@@ -142,11 +149,63 @@ func (k KudoWebHook) resourcesWithSelfSignedCA() (*admissionv1beta1.MutatingWebh | |
return &iaw, &ws, nil | ||
} | ||
|
||
func validateCertManagerInstallation(client *kube.Client, result *verifier.Result) error { | ||
if err := validateCrdVersion(client.ExtClient, "certificates.cert-manager.io", certManagerAPIVersion, result); err != nil { | ||
func (k *KudoWebHook) detectCertManagerVersion(client *kube.Client, result *verifier.Result) error { | ||
extClient := client.ExtClient | ||
|
||
// Cert-Manager 0.11.0+ | ||
if err := k.detectCertManagerCRD(extClient, certManagerNewGroup); err != nil { | ||
return err | ||
} | ||
if k.certManagerGroup != "" { | ||
return nil | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is odd combo of funcs and state... this function needs intimate knowledge of the there is a lot of refactor here IMO...
|
||
|
||
// Cert-Manager 0.10.1 | ||
if err := k.detectCertManagerCRD(extClient, certManagerOldGroup); err != nil { | ||
return err | ||
} | ||
if k.certManagerGroup != "" { | ||
return nil | ||
} | ||
result.AddErrors(fmt.Sprintf("failed to detect any valid cert-manager CRDs. Make sure cert-manager is installed.")) | ||
return nil | ||
} | ||
|
||
func (k *KudoWebHook) detectCertManagerCRD(extClient clientset.Interface, group string) error { | ||
testCRD := fmt.Sprintf("certificates.%s", group) | ||
clog.V(4).Printf("Try to retrieve cert-manager CRD %s", testCRD) | ||
crd, err := extClient.ApiextensionsV1().CustomResourceDefinitions().Get(testCRD, metav1.GetOptions{}) | ||
if err == nil { | ||
clog.V(4).Printf("Got CRD. Group: %s, Version: %s", group, crd.Spec.Versions[0].Name) | ||
k.certManagerGroup = group | ||
k.certManagerAPIVersion = crd.Spec.Versions[0].Name | ||
return nil | ||
} | ||
if !kerrors.IsNotFound(err) { | ||
return fmt.Errorf("failed to detect cert manager CRD %s: %v", testCRD, err) | ||
} | ||
return nil | ||
} | ||
|
||
func (k *KudoWebHook) validateCertManagerInstallation(client *kube.Client, result *verifier.Result) error { | ||
if err := k.detectCertManagerVersion(client, result); err != nil { | ||
return err | ||
} | ||
if !result.IsValid() { | ||
return nil | ||
} | ||
clog.V(4).Printf("Detected cert-manager CRDs %s/%s", k.certManagerGroup, k.certManagerAPIVersion) | ||
|
||
if !funk.Contains(certManagerAPIVersions, k.certManagerAPIVersion) { | ||
result.AddWarnings(fmt.Sprintf("Detected cert-manager CRDs with version %s, only versions %v are fully supported. Certificates for webhooks may not work.", k.certManagerAPIVersion, certManagerAPIVersions)) | ||
} | ||
|
||
certificateCRD := fmt.Sprintf("certificates.%s", k.certManagerGroup) | ||
if err := validateCrdVersion(client.ExtClient, certificateCRD, k.certManagerAPIVersion, result); err != nil { | ||
return err | ||
} | ||
if err := validateCrdVersion(client.ExtClient, "issuers.cert-manager.io", certManagerAPIVersion, result); err != nil { | ||
issuerCRD := fmt.Sprintf("issuers.%s", k.certManagerGroup) | ||
if err := validateCrdVersion(client.ExtClient, issuerCRD, k.certManagerAPIVersion, result); err != nil { | ||
return err | ||
} | ||
|
||
|
@@ -155,6 +214,11 @@ func validateCertManagerInstallation(client *kube.Client, result *verifier.Resul | |
return nil | ||
} | ||
|
||
// Initialize the custom resources that we're going to install | ||
k.certificate = certificate(k.opts.Namespace, k.certManagerGroup, k.certManagerAPIVersion) | ||
k.issuer = issuer(k.opts.Namespace, k.certManagerGroup, k.certManagerAPIVersion) | ||
|
||
// A couple extra checks, these may fail because cert-manager can be installed in different namespaces | ||
deployment, err := client.KubeClient.AppsV1().Deployments("cert-manager").Get("cert-manager", metav1.GetOptions{}) | ||
if err != nil { | ||
if kerrors.IsNotFound(err) { | ||
|
@@ -167,10 +231,6 @@ func validateCertManagerInstallation(client *kube.Client, result *verifier.Resul | |
result.AddWarnings("failed to validate cert-manager controller deployment. Spec had no containers") | ||
return nil | ||
} | ||
if !strings.HasSuffix(deployment.Spec.Template.Spec.Containers[0].Image, certManagerControllerImageSuffix) { | ||
result.AddWarnings(fmt.Sprintf("cert-manager deployment had unexpected version. expected %s in controller image name but found %s", certManagerControllerVersion, deployment.Spec.Template.Spec.Containers[0].Image)) | ||
return nil | ||
} | ||
if err := health.IsHealthy(deployment); err != nil { | ||
result.AddWarnings("cert-manager seems not to be running correctly. Make sure cert-manager is working") | ||
return nil | ||
|
@@ -196,13 +256,13 @@ func validateCrdVersion(extClient clientset.Interface, crdName string, expectedV | |
} | ||
|
||
// installUnstructured accepts kubernetes resource as unstructured.Unstructured and installs it into cluster | ||
func installUnstructured(dynamicClient dynamic.Interface, item unstructured.Unstructured) error { | ||
func installUnstructured(dynamicClient dynamic.Interface, item *unstructured.Unstructured) error { | ||
gvk := item.GroupVersionKind() | ||
_, err := dynamicClient.Resource(schema.GroupVersionResource{ | ||
Group: gvk.Group, | ||
Version: gvk.Version, | ||
Resource: fmt.Sprintf("%ss", strings.ToLower(gvk.Kind)), // since we know what kinds are we dealing with here, this is OK | ||
}).Namespace(item.GetNamespace()).Create(&item, metav1.CreateOptions{}) | ||
}).Namespace(item.GetNamespace()).Create(item, metav1.CreateOptions{}) | ||
if kerrors.IsAlreadyExists(err) { | ||
clog.V(4).Printf("resource %s already registered", item.GetName()) | ||
} else if err != nil { | ||
|
@@ -281,10 +341,11 @@ func InstanceAdmissionWebhook(ns string) admissionv1beta1.MutatingWebhookConfigu | |
} | ||
} | ||
|
||
func issuer(ns string) unstructured.Unstructured { | ||
return unstructured.Unstructured{ | ||
func issuer(ns string, group string, apiVersion string) *unstructured.Unstructured { | ||
apiString := fmt.Sprintf("%s/%s", group, apiVersion) | ||
return &unstructured.Unstructured{ | ||
Object: map[string]interface{}{ | ||
"apiVersion": "cert-manager.io/v1alpha2", | ||
"apiVersion": apiString, | ||
"kind": "Issuer", | ||
"metadata": map[string]interface{}{ | ||
"name": "selfsigned-issuer", | ||
|
@@ -297,10 +358,11 @@ func issuer(ns string) unstructured.Unstructured { | |
} | ||
} | ||
|
||
func certificate(ns string) unstructured.Unstructured { | ||
return unstructured.Unstructured{ | ||
func certificate(ns string, group string, apiVersion string) *unstructured.Unstructured { | ||
apiString := fmt.Sprintf("%s/%s", group, apiVersion) | ||
return &unstructured.Unstructured{ | ||
Object: map[string]interface{}{ | ||
"apiVersion": "cert-manager.io/v1alpha2", | ||
"apiVersion": apiString, | ||
"kind": "Certificate", | ||
"metadata": map[string]interface{}{ | ||
"name": "kudo-webhook-server-certificate", | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't know why
crdOnly
is not part of theopts
. But that's another story