Skip to content

Commit

Permalink
Add cert verification
Browse files Browse the repository at this point in the history
  • Loading branch information
alenkacz committed Dec 27, 2020
1 parent 0d12947 commit 463154a
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 0 deletions.
11 changes: 11 additions & 0 deletions pkg/cmd/verify/verify.go → pkg/cmd/verify/deployments.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/alenkacz/cert-manager-verifier/pkg/verify"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"os"
)
Expand Down Expand Up @@ -62,6 +63,10 @@ func (o *Options) Execute() error {
if err != nil {
return fmt.Errorf("unable to get kubernetes client: %v", err)
}
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
return fmt.Errorf("unable to get kubernetes client: %v", err)
}

deployments := verify.DeploymentDefinitionDefault()
_, _ = fmt.Fprintf(o.Streams.Out, "Waiting for deployments in namespace %s:\n%s", deployments.Namespace, formatDeploymentNames(deployments.Names))
Expand All @@ -71,6 +76,12 @@ func (o *Options) Execute() error {
if !allReady(result) {
return fmt.Errorf("FAILED! Not all deployments are ready.")
}
err = verify.WaitForTestCertificate(dynamicClient)
if err != nil {
_, _ = fmt.Fprintf(o.Streams.Out, "error when waiting for certificate to be ready: %v", err)
return err
}
_, _ = fmt.Fprint(o.Streams.Out, "ヽ(•‿•)ノ Cert-manager is READY!")
return nil
}

Expand Down
142 changes: 142 additions & 0 deletions pkg/verify/certificate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package verify

import (
"context"
"fmt"
"strings"

"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/dynamic"
)

const (
group = "cert-manager.io"
version = "v1"
)

var namespace = &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "Namespace",
"metadata": map[string]interface{}{
"name": "cert-manager-test",
},
"spec": map[string]interface{}{
"selfSigned": map[string]interface{}{},
},
},
}

// TODO support also other API versions
// TODO make it possible to execute this inside namespace, not creating one
func WaitForTestCertificate(dynamicClient dynamic.Interface) error {
cert := certificate("cert-manager-test", group, version)
resources := []*unstructured.Unstructured{namespace, issuer("cert-manager-test", group, version), cert}
defer cleanupTestResources(resources)

for _, res := range resources {
err := createResource(dynamicClient, res)
if err != nil {
return err
}
}
poller := &certPoller{dynamicClient, cert}
return wait.PollImmediate(defaultPollInterval, defaultMaxWait, poller.certificateReady)
}

type certPoller struct {
dynamicClient dynamic.Interface
certificate *unstructured.Unstructured
}

func (p *certPoller) certificateReady() (bool, error) {
gvk := p.certificate.GroupVersionKind()
cert, err := p.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(p.certificate.GetNamespace()).Get(context.TODO(), p.certificate.GetName(), metav1.GetOptions{}, "status")
if err != nil {
return false, err
}
conditions, exists, err := unstructured.NestedSlice(cert.Object, "status", "conditions")
if !exists || err != nil {
return false, err
}
for _, c := range conditions {
reason, found, err := unstructured.NestedString(c.(map[string]interface{}), "type")
if !found || err != nil {
return false, err
}
if reason == "Ready" {
status, found, err := unstructured.NestedString(c.(map[string]interface{}), "status")
if !found || err != nil {
return false, err
}
return status == "True", nil
}
}
return false, nil
}

func createResource(dynamicClient dynamic.Interface, resource *unstructured.Unstructured) error {
gvk := resource.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(resource.GetNamespace()).Create(context.TODO(), resource, metav1.CreateOptions{})
if errors.IsAlreadyExists(err) {
fmt.Printf("resource %s already exists\n", resource.GetName())
} else if err != nil {
return fmt.Errorf("error when creating resource %s/%s. %v", resource.GetName(), resource.GetNamespace(), err)
}
return nil
}

func cleanupTestResources(resources []*unstructured.Unstructured) {
// TODO delete also the secret?
}

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": apiString,
"kind": "Issuer",
"metadata": map[string]interface{}{
"name": "test-selfsigned",
"namespace": ns,
},
"spec": map[string]interface{}{
"selfSigned": map[string]interface{}{},
},
},
}
}

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": apiString,
"kind": "Certificate",
"metadata": map[string]interface{}{
"name": "selfsigned-cert",
"namespace": ns,
},
"spec": map[string]interface{}{
"dnsNames": []string{"example.com"},
"issuerRef": map[string]interface{}{
"kind": "Issuer",
"name": "test-selfsigned",
},
"secretName": "selfsigned-cert-tls",
},
},
}
}

0 comments on commit 463154a

Please sign in to comment.