Skip to content
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

Use semantic equality in client ca post start hook #60771

Merged
merged 2 commits into from
Apr 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions pkg/master/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ go_library(
"//staging/src/k8s.io/api/storage/v1:go_default_library",
"//staging/src/k8s.io/api/storage/v1alpha1:go_default_library",
"//staging/src/k8s.io/api/storage/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
Expand Down
4 changes: 2 additions & 2 deletions pkg/master/client_ca_hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ package master
import (
"encoding/json"
"fmt"
"reflect"
"time"

corev1 "k8s.io/api/core/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
Expand Down Expand Up @@ -132,7 +132,7 @@ func writeConfigMap(client corev1client.ConfigMapsGetter, name string, data map[
return err
}

if !reflect.DeepEqual(existing.Data, data) {
if !apiequality.Semantic.DeepEqual(existing.Data, data) {
existing.Data = data
_, err = client.ConfigMaps(metav1.NamespaceSystem).Update(existing)
}
Expand Down
3 changes: 3 additions & 0 deletions test/integration/apiserver/admissionwebhook/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go_test(
name = "go_default_test",
srcs = [
"admission_test.go",
"broken_webhook_test.go",
"main_test.go",
],
tags = [
Expand All @@ -12,8 +13,10 @@ go_test(
],
deps = [
"//cmd/kube-apiserver/app/options:go_default_library",
"//cmd/kube-apiserver/app/testing:go_default_library",
"//staging/src/k8s.io/api/admission/v1beta1:go_default_library",
"//staging/src/k8s.io/api/admissionregistration/v1beta1:go_default_library",
"//staging/src/k8s.io/api/apps/v1:go_default_library",
"//staging/src/k8s.io/api/apps/v1beta1:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/api/extensions/v1beta1:go_default_library",
Expand Down
183 changes: 183 additions & 0 deletions test/integration/apiserver/admissionwebhook/broken_webhook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*
Copyright 2018 The Kubernetes 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 admissionwebhook

import (
"fmt"
"testing"
"time"

admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
"k8s.io/kubernetes/test/integration/framework"
)

var (
brokenWebhookName = "integration-broken-webhook-test-webhook-config"
deploymentNamePrefix = "integration-broken-webhook-test-deployment"
)

func TestBrokenWebhook(t *testing.T) {
var tearDownFn kubeapiservertesting.TearDownFunc
defer func() {
if tearDownFn != nil {
tearDownFn()
}
}()

etcdConfig := framework.SharedEtcd()
server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, etcdConfig)
tearDownFn = server.TearDownFn

client, err := kubernetes.NewForConfig(server.ClientConfig)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

t.Logf("Creating Deployment to ensure apiserver is functional")
_, err = client.AppsV1().Deployments("default").Create(exampleDeployment(generateDeploymentName(0)))
if err != nil {
t.Fatalf("Failed to create deployment: %v", err)
}

t.Logf("Creating Broken Webhook that will block all operations on all objects")
_, err = client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Create(brokenWebhookConfig(brokenWebhookName))
if err != nil {
t.Fatalf("Failed to register broken webhook: %v", err)
}

// There is no guarantee on how long it takes the apiserver to honor the configuration and there is
// no API to determine if the configuration is being honored, so we will just wait 10s, which is long enough
// in most cases.
time.Sleep(10 * time.Second)

// test whether the webhook blocks requests
t.Logf("Attempt to create Deployment which should fail due to the webhook")
_, err = client.AppsV1().Deployments("default").Create(exampleDeployment(generateDeploymentName(1)))
if err == nil {
t.Fatalf("Expected the broken webhook to cause creating a deployment to fail, but it succeeded.")
}

t.Logf("Restarting apiserver")
tearDownFn = nil
server.TearDownFn()
server = kubeapiservertesting.StartTestServerOrDie(t, nil, nil, etcdConfig)
tearDownFn = server.TearDownFn

client, err = kubernetes.NewForConfig(server.ClientConfig)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

// test whether the webhook still blocks requests after restarting
t.Logf("Attempt again to create Deployment which should fail due to the webhook")
_, err = client.AppsV1().Deployments("default").Create(exampleDeployment(generateDeploymentName(2)))
if err == nil {
t.Fatalf("Expected the broken webhook to cause creating a deployment to fail, but it succeeded.")
}

t.Logf("Deleting the broken webhook to fix the cluster")
err = client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Delete(brokenWebhookName, nil)
if err != nil {
t.Fatalf("Failed to delete broken webhook: %v", err)
}

// The webhook deletion is honored in 10s.
time.Sleep(10 * time.Second)

// test if the deleted webhook no longer blocks requests
t.Logf("Creating Deployment to ensure webhook is deleted")
_, err = client.AppsV1().Deployments("default").Create(exampleDeployment(generateDeploymentName(3)))
if err != nil {
t.Fatalf("Failed to create deployment: %v", err)
}
}

func generateDeploymentName(suffix int) string {
return fmt.Sprintf("%v-%v", deploymentNamePrefix, suffix)
}

func exampleDeployment(name string) *appsv1.Deployment {
var replicas int32 = 1
return &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: name,
},
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"foo": "bar"},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"foo": "bar"},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "foo",
Image: "foo",
},
},
},
},
},
}
}

func brokenWebhookConfig(name string) *admissionregistrationv1beta1.ValidatingWebhookConfiguration {
var path string
var failurePolicy admissionregistrationv1beta1.FailurePolicyType = admissionregistrationv1beta1.Fail
return &admissionregistrationv1beta1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Webhooks: []admissionregistrationv1beta1.Webhook{
{
Name: "broken-webhook.k8s.io",
Rules: []admissionregistrationv1beta1.RuleWithOperations{{
Operations: []admissionregistrationv1beta1.OperationType{admissionregistrationv1beta1.OperationAll},
Rule: admissionregistrationv1beta1.Rule{
APIGroups: []string{"*"},
APIVersions: []string{"*"},
Resources: []string{"*/*"},
},
}},
// This client config references a non existent service
// so it should always fail.
ClientConfig: admissionregistrationv1beta1.WebhookClientConfig{
Service: &admissionregistrationv1beta1.ServiceReference{
Namespace: "default",
Name: "invalid-webhook-service",
Path: &path,
},
CABundle: nil,
},
FailurePolicy: &failurePolicy,
},
},
}
}