Skip to content
Permalink
Browse files

shipperctl checks if ValidatingWebhookService and ValidatingWebhookCo…

…nfiguration exists, and if so, it does not update these objects.

Following ValidatingWebhookSecret, CRD and Cluster create or update logic, shipperctl should create the service and the webhook configuration if they don't exists. If they do, shipperctl should update them.
  • Loading branch information...
hbarkal authored and icanhazbroccoli committed Sep 24, 2019
1 parent 4b17c3e commit a49ee22bffaff3751b3b813e9e9b43c24da91d9c
Showing with 232 additions and 11 deletions.
  1. +2 −2 cmd/shipperctl/cmd/apply.go
  2. +28 −9 cmd/shipperctl/configurator/cluster.go
  3. +202 −0 cmd/shipperctl/configurator/cluster_test.go
@@ -377,7 +377,7 @@ func createValidatingWebhookConfiguration(cmd *cobra.Command, configurator *conf
return err
}

if err := configurator.CreateValidatingWebhookConfiguration(caBundle, shipperSystemNamespace); err != nil {
if err := configurator.CreateOrUpdateValidatingWebhookConfiguration(caBundle, shipperSystemNamespace); err != nil {
if errors.IsAlreadyExists(err) {
cmd.Println("already exists. Skipping")
return nil
@@ -392,7 +392,7 @@ func createValidatingWebhookConfiguration(cmd *cobra.Command, configurator *conf

func createValidatingWebhookService(cmd *cobra.Command, configurator *configurator.Cluster) error {
cmd.Print("Creating a Service object for the validating webhook... ")
if err := configurator.CreateValidatingWebhookService(shipperSystemNamespace); err != nil {
if err := configurator.CreateOrUpdateValidatingWebhookService(shipperSystemNamespace); err != nil {
if errors.IsAlreadyExists(err) {
cmd.Println("already exists. Skipping")
return nil
@@ -39,9 +39,9 @@ const (
)

type Cluster struct {
KubeClient *kubernetes.Clientset
ShipperClient *shipperclientset.Clientset
ApiExtensionClient *apiextensionclientset.Clientset
KubeClient kubernetes.Interface
ShipperClient shipperclientset.Interface
ApiExtensionClient apiextensionclientset.Interface
Host string
}

@@ -422,7 +422,7 @@ func (c *Cluster) FetchKubernetesCABundle() ([]byte, error) {
return []byte(caBundle), nil
}

func (c *Cluster) CreateValidatingWebhookConfiguration(caBundle []byte, namespace string) error {
func (c *Cluster) CreateOrUpdateValidatingWebhookConfiguration(caBundle []byte, namespace string) error {
path := shipperValidatingWebhookServicePath
validatingWebhookConfiguration := &admissionregistrationv1beta1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
@@ -456,15 +456,22 @@ func (c *Cluster) CreateValidatingWebhookConfiguration(caBundle []byte, namespac
},
}

_, err := c.KubeClient.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Create(validatingWebhookConfiguration)
existingConfig, err := c.KubeClient.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Get(shipperValidatingWebhookName, metav1.GetOptions{})
if err != nil {
return err
if errors.IsNotFound(err) {
_, err = c.KubeClient.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Create(validatingWebhookConfiguration)
return err
} else {
return err
}
}

return nil
existingConfig.Webhooks = validatingWebhookConfiguration.Webhooks
_, err = c.KubeClient.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Update(existingConfig)
return err
}

func (c *Cluster) CreateValidatingWebhookService(namespace string) error {
func (c *Cluster) CreateOrUpdateValidatingWebhookService(namespace string) error {
service := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: shipperValidatingWebhookServiceName,
@@ -483,6 +490,18 @@ func (c *Cluster) CreateValidatingWebhookService(namespace string) error {
},
}

_, err := c.KubeClient.CoreV1().Services(namespace).Create(service)
existingSerivce, err := c.KubeClient.CoreV1().Services(namespace).Get(shipperValidatingWebhookServiceName, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
_, err = c.KubeClient.CoreV1().Services(namespace).Create(service)
return err
} else {
return err
}
}

existingSerivce.Spec.Selector = service.Spec.Selector
existingSerivce.Spec.Ports = service.Spec.Ports
_, err = c.KubeClient.CoreV1().Services(namespace).Update(existingSerivce)
return err
}
@@ -0,0 +1,202 @@
package configurator

import (
"testing"

corev1 "k8s.io/api/core/v1"
fakeapiextensionclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
kubefake "k8s.io/client-go/kubernetes/fake"
kubetesting "k8s.io/client-go/testing"

shipper "github.com/bookingcom/shipper/pkg/apis/shipper/v1alpha1"
shipperfake "github.com/bookingcom/shipper/pkg/client/clientset/versioned/fake"
shippertesting "github.com/bookingcom/shipper/pkg/testing"
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
)

const shipperSystemNamespace = "shipper-system"

func TestCreateValidatingWebhookConfiguration(t *testing.T) {
f := newFixture(t)
caBundle := []byte{}
if err := f.configurator.CreateOrUpdateValidatingWebhookConfiguration(caBundle, shipperSystemNamespace); err != nil {
t.Fatal(err)
}

operations := []admissionregistrationv1beta1.OperationType{
admissionregistrationv1beta1.Create,
admissionregistrationv1beta1.Update,
}
expectedConfiguration := f.newValidatingWebhookConfiguration(caBundle, shipperSystemNamespace, operations)
gvr := admissionregistrationv1beta1.SchemeGroupVersion.WithResource("validatingwebhookconfigurations")
getAction := kubetesting.NewGetAction(gvr, "", expectedConfiguration.Name)
createAction := kubetesting.NewCreateAction(gvr, "", expectedConfiguration)
f.actions = append(f.actions, getAction, createAction)

clientSet, ok := f.configurator.KubeClient.(*kubefake.Clientset)
if !ok {
t.Fatalf("not a *kubefake.Clientset: %#v", f.configurator.KubeClient)
}
actualActions := shippertesting.FilterActions(clientSet.Actions())
shippertesting.CheckActions(f.actions, actualActions, f.t)
}

func TestUpdateValidatingWebhookConfiguration(t *testing.T) {
f := newFixture(t)
caBundle := []byte{}

operations := []admissionregistrationv1beta1.OperationType{
admissionregistrationv1beta1.Create,
}
configuration := f.newValidatingWebhookConfiguration(caBundle, shipperSystemNamespace, operations)
if _, err := f.configurator.KubeClient.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Create(configuration); err != nil {
t.Fatal(err)
}
gvr := admissionregistrationv1beta1.SchemeGroupVersion.WithResource("validatingwebhookconfigurations")
createAction := kubetesting.NewCreateAction(gvr, "", configuration)
f.actions = append(f.actions, createAction)

if err := f.configurator.CreateOrUpdateValidatingWebhookConfiguration(caBundle, shipperSystemNamespace); err != nil {
t.Fatal(err)
}

operations = []admissionregistrationv1beta1.OperationType{
admissionregistrationv1beta1.Create,
admissionregistrationv1beta1.Update,
}
expectedConfiguration := f.newValidatingWebhookConfiguration(caBundle, shipperSystemNamespace, operations)
getAction := kubetesting.NewGetAction(gvr, "", expectedConfiguration.Name)
updateAction := kubetesting.NewUpdateAction(gvr, "", expectedConfiguration)
f.actions = append(f.actions, getAction, updateAction)

clientSet, ok := f.configurator.KubeClient.(*kubefake.Clientset)
if !ok {
t.Fatalf("not a *kubefake.Clientset: %#v", f.configurator.KubeClient)
}
actualActions := shippertesting.FilterActions(clientSet.Actions())
shippertesting.CheckActions(f.actions, actualActions, f.t)
}

func TestCreateValidatingWebhookService(t *testing.T) {
f := newFixture(t)
if err := f.configurator.CreateOrUpdateValidatingWebhookService(shipperSystemNamespace); err != nil {
t.Fatal(err)
}

expectedService := f.newValidatingWebhookService("shipper", shipperSystemNamespace)
gvr := corev1.SchemeGroupVersion.WithResource("services")
getAction := kubetesting.NewGetAction(gvr, shipperSystemNamespace, expectedService.Name)
createAction := kubetesting.NewCreateAction(gvr, shipperSystemNamespace, expectedService)
f.actions = append(f.actions, getAction, createAction)

clientSet, ok := f.configurator.KubeClient.(*kubefake.Clientset)
if !ok {
t.Fatalf("not a *kubefake.Clientset: %#v", f.configurator.KubeClient)
}
actualActions := shippertesting.FilterActions(clientSet.Actions())
shippertesting.CheckActions(f.actions, actualActions, f.t)
}

func TestUpdateValidatingWebhookService(t *testing.T) {
f := newFixture(t)
service := f.newValidatingWebhookService("Hello", shipperSystemNamespace)
if _, err := f.configurator.KubeClient.CoreV1().Services(shipperSystemNamespace).Create(service); err != nil {
t.Fatal(err)
}
gvr := corev1.SchemeGroupVersion.WithResource("services")
createAction := kubetesting.NewCreateAction(gvr, shipperSystemNamespace, service)
f.actions = append(f.actions, createAction)

if err := f.configurator.CreateOrUpdateValidatingWebhookService(shipperSystemNamespace); err != nil {
t.Fatal(err)
}

expectedService := f.newValidatingWebhookService("shipper", shipperSystemNamespace)
getAction := kubetesting.NewGetAction(gvr, shipperSystemNamespace, expectedService.Name)
updateAction := kubetesting.NewUpdateAction(gvr, shipperSystemNamespace, expectedService)
f.actions = append(f.actions, getAction, updateAction)

clientSet, ok := f.configurator.KubeClient.(*kubefake.Clientset)
if !ok {
t.Fatalf("not a *kubefake.Clientset: %#v", f.configurator.KubeClient)
}
actualActions := shippertesting.FilterActions(clientSet.Actions())
shippertesting.CheckActions(f.actions, actualActions, f.t)
}

type fixture struct {
t *testing.T
configurator *Cluster

actions []kubetesting.Action
}

func newFixture(t *testing.T) *fixture {
return &fixture{
t: t,
configurator: newCluster(),
}
}

func newCluster() *Cluster {
return &Cluster{
KubeClient: kubefake.NewSimpleClientset(),
ShipperClient: shipperfake.NewSimpleClientset(),
ApiExtensionClient: fakeapiextensionclientset.NewSimpleClientset(),
Host: "localhost:8080",
}
}

func (f *fixture) newValidatingWebhookConfiguration(caBundle []byte, namespace string, operations []admissionregistrationv1beta1.OperationType) *admissionregistrationv1beta1.ValidatingWebhookConfiguration {
path := shipperValidatingWebhookServicePath
return &admissionregistrationv1beta1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: shipperValidatingWebhookName,
},
Webhooks: []admissionregistrationv1beta1.ValidatingWebhook{
admissionregistrationv1beta1.ValidatingWebhook{
Name: shipperValidatingWebhookName,
ClientConfig: admissionregistrationv1beta1.WebhookClientConfig{
CABundle: caBundle,
Service: &admissionregistrationv1beta1.ServiceReference{
Name: shipperValidatingWebhookServiceName,
Namespace: namespace,
Path: &path,
},
},
Rules: []admissionregistrationv1beta1.RuleWithOperations{
admissionregistrationv1beta1.RuleWithOperations{
Operations: operations,
Rule: admissionregistrationv1beta1.Rule{
APIGroups: []string{shipper.SchemeGroupVersion.Group},
APIVersions: []string{shipper.SchemeGroupVersion.Version},
Resources: []string{"*"},
},
},
},
},
},
}
}

func (f *fixture) newValidatingWebhookService(appName, namespace string) *corev1.Service {
return &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: shipperValidatingWebhookServiceName,
Namespace: namespace,
},
Spec: corev1.ServiceSpec{
Selector: map[string]string{
"app": appName,
},
Ports: []corev1.ServicePort{
corev1.ServicePort{
Port: 443,
TargetPort: intstr.FromInt(9443),
},
},
},
}
}

0 comments on commit a49ee22

Please sign in to comment.
You can’t perform that action at this time.