Unverified Commit c3e40840 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #55739 from caesarxuchao/webhook-move-more-shared-code

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Reorganizing more webhook code ref: kubernetes/features#492 Continue on https://github.com/kubernetes/kubernetes/pull/55132. With this PR, all code shared between the mutating and validating webhook plugins is extracted into its own package.
parents 86480fc0 47ef9aaf
......@@ -883,6 +883,18 @@
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/admission/plugin/webhook/errors",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/admission/plugin/webhook/request",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/admission/plugin/webhook/rules",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
......@@ -891,6 +903,10 @@
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/admission/plugin/webhook/versioned",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/apis/apiserver",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
......
......@@ -79,8 +79,12 @@ filegroup(
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/initialization:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/config:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/errors:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/request:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/rules:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/versioned:all-srcs",
],
tags = ["automanaged"],
)
......@@ -5,7 +5,6 @@ go_library(
srcs = [
"authentication.go",
"client.go",
"errors.go",
"kubeconfig.go",
"serviceresolver.go",
],
......@@ -17,6 +16,7 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/errors:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
......
......@@ -27,6 +27,7 @@ import (
"k8s.io/api/admissionregistration/v1alpha1"
"k8s.io/apimachinery/pkg/runtime"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
"k8s.io/client-go/rest"
)
......@@ -153,12 +154,12 @@ func (cm *ClientManager) HookClient(h *v1alpha1.Webhook) (*rest.RESTClient, erro
}
if h.ClientConfig.URL == nil {
return nil, &ErrCallingWebhook{WebhookName: h.Name, Reason: ErrNeedServiceOrURL}
return nil, &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: ErrNeedServiceOrURL}
}
u, err := url.Parse(*h.ClientConfig.URL)
if err != nil {
return nil, &ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Unparsable URL: %v", err)}
return nil, &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Unparsable URL: %v", err)}
}
restConfig, err := cm.authInfoResolver.ClientConfigFor(u.Host)
......
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"errors.go",
"statuserror.go",
],
importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/errors",
visibility = ["//visibility:public"],
deps = [
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["statuserror_test.go"],
importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/errors",
library = ":go_default_library",
deps = ["//vendor/k8s.io/apimachinery/pkg/apis/meta/v1: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"],
)
/*
Copyright 2017 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 errors contains utilities for admission webhook specific errors
package errors // import "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
......@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package config
package errors
import "fmt"
......
/*
Copyright 2017 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 errors
import (
"fmt"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ToStatusErr returns a StatusError with information about the webhook plugin
func ToStatusErr(webhookName string, result *metav1.Status) *apierrors.StatusError {
deniedBy := fmt.Sprintf("admission webhook %q denied the request", webhookName)
const noExp = "without explanation"
if result == nil {
result = &metav1.Status{Status: metav1.StatusFailure}
}
switch {
case len(result.Message) > 0:
result.Message = fmt.Sprintf("%s: %s", deniedBy, result.Message)
case len(result.Reason) > 0:
result.Message = fmt.Sprintf("%s: %s", deniedBy, result.Reason)
default:
result.Message = fmt.Sprintf("%s %s", deniedBy, noExp)
}
return &apierrors.StatusError{
ErrStatus: *result,
}
}
/*
Copyright 2017 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 errors
import (
"fmt"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestToStatusErr(t *testing.T) {
hookName := "foo"
deniedBy := fmt.Sprintf("admission webhook %q denied the request", hookName)
tests := []struct {
name string
result *metav1.Status
expectedError string
}{
{
"nil result",
nil,
deniedBy + " without explanation",
},
{
"only message",
&metav1.Status{
Message: "you shall not pass",
},
deniedBy + ": you shall not pass",
},
{
"only reason",
&metav1.Status{
Reason: metav1.StatusReasonForbidden,
},
deniedBy + ": Forbidden",
},
{
"message and reason",
&metav1.Status{
Message: "you shall not pass",
Reason: metav1.StatusReasonForbidden,
},
deniedBy + ": you shall not pass",
},
{
"no message, no reason",
&metav1.Status{},
deniedBy + " without explanation",
},
}
for _, test := range tests {
err := ToStatusErr(hookName, test.result)
if err == nil || err.Error() != test.expectedError {
t.Errorf("%s: expected an error saying %q, but got %v", test.name, test.expectedError, err)
}
}
}
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"matcher.go",
],
importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace",
visibility = ["//visibility:public"],
deps = [
"//vendor/k8s.io/api/admissionregistration/v1alpha1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/listers/core/v1:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["matcher_test.go"],
importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace",
library = ":go_default_library",
deps = [
"//vendor/k8s.io/api/admissionregistration/v1alpha1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission: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"],
)
/*
Copyright 2017 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 namespace defines the utilities that are used by the webhook
// plugin to decide if a webhook should be applied to an object based on its
// namespace.
package namespace // import "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace"
/*
Copyright 2017 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 namespace
import (
"fmt"
"k8s.io/api/admissionregistration/v1alpha1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apiserver/pkg/admission"
clientset "k8s.io/client-go/kubernetes"
corelisters "k8s.io/client-go/listers/core/v1"
)
// Matcher decides if a request is exempted by the NamespaceSelector of a
// webhook configuration.
type Matcher struct {
NamespaceLister corelisters.NamespaceLister
Client clientset.Interface
}
// Validate checks if the Matcher has a NamespaceLister and Client.
func (m *Matcher) Validate() error {
var errs []error
if m.NamespaceLister == nil {
errs = append(errs, fmt.Errorf("the namespace matcher requires a namespaceLister"))
}
if m.Client == nil {
errs = append(errs, fmt.Errorf("the namespace matcher requires a namespaceLister"))
}
return utilerrors.NewAggregate(errs)
}
// GetNamespaceLabels gets the labels of the namespace related to the attr.
func (m *Matcher) GetNamespaceLabels(attr admission.Attributes) (map[string]string, error) {
// If the request itself is creating or updating a namespace, then get the
// labels from attr.Object, because namespaceLister doesn't have the latest
// namespace yet.
//
// However, if the request is deleting a namespace, then get the label from
// the namespace in the namespaceLister, because a delete request is not
// going to change the object, and attr.Object will be a DeleteOptions
// rather than a namespace object.
if attr.GetResource().Resource == "namespaces" &&
len(attr.GetSubresource()) == 0 &&
(attr.GetOperation() == admission.Create || attr.GetOperation() == admission.Update) {
accessor, err := meta.Accessor(attr.GetObject())
if err != nil {
return nil, err
}
return accessor.GetLabels(), nil
}
namespaceName := attr.GetNamespace()
namespace, err := m.NamespaceLister.Get(namespaceName)
if err != nil && !apierrors.IsNotFound(err) {
return nil, err
}
if apierrors.IsNotFound(err) {
// in case of latency in our caches, make a call direct to storage to verify that it truly exists or not
namespace, err = m.Client.CoreV1().Namespaces().Get(namespaceName, metav1.GetOptions{})
if err != nil {
return nil, err
}
}
return namespace.Labels, nil
}
// MatchNamespaceSelector decideds whether the request matches the
// namespaceSelctor of the webhook. Only when they match, the webhook is called.
func (m *Matcher) MatchNamespaceSelector(h *v1alpha1.Webhook, attr admission.Attributes) (bool, *apierrors.StatusError) {
namespaceName := attr.GetNamespace()
if len(namespaceName) == 0 && attr.GetResource().Resource != "namespaces" {
// If the request is about a cluster scoped resource, and it is not a
// namespace, it is exempted from all webhooks for now.
// TODO: figure out a way selective exempt cluster scoped resources.
// Also update the comment in types.go
return false, nil
}
namespaceLabels, err := m.GetNamespaceLabels(attr)
// this means the namespace is not found, for backwards compatibility,
// return a 404
if apierrors.IsNotFound(err) {
status, ok := err.(apierrors.APIStatus)
if !ok {
return false, apierrors.NewInternalError(err)
}
return false, &apierrors.StatusError{status.Status()}
}
if err != nil {
return false, apierrors.NewInternalError(err)
}
// TODO: adding an LRU cache to cache the translation
selector, err := metav1.LabelSelectorAsSelector(h.NamespaceSelector)
if err != nil {
return false, apierrors.NewInternalError(err)
}
return selector.Matches(labels.Set(namespaceLabels)), nil
}
/*
Copyright 2017 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 namespace
import (
"reflect"
"testing"
registrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
)
type fakeNamespaceLister struct {
namespaces map[string]*corev1.Namespace
}
func (f fakeNamespaceLister) List(selector labels.Selector) (ret []*corev1.Namespace, err error) {
return nil, nil
}
func (f fakeNamespaceLister) Get(name string) (*corev1.Namespace, error) {
ns, ok := f.namespaces[name]
if ok {
return ns, nil
}
return nil, errors.NewNotFound(corev1.Resource("namespaces"), name)
}
func TestGetNamespaceLabels(t *testing.T) {
namespace1Labels := map[string]string{
"runlevel": "1",
}
namespace1 := corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "1",
Labels: namespace1Labels,
},
}
namespace2Labels := map[string]string{
"runlevel": "2",
}
namespace2 := corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "2",
Labels: namespace2Labels,
},
}
namespaceLister := fakeNamespaceLister{map[string]*corev1.Namespace{
"1": &namespace1,
},
}
tests := []struct {
name string
attr admission.Attributes
expectedLabels map[string]string
}{
{
name: "request is for creating namespace, the labels should be from the object itself",
attr: admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, "", namespace2.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Create, nil),
expectedLabels: namespace2Labels,
},
{
name: "request is for updating namespace, the labels should be from the new object",
attr: admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, namespace2.Name, namespace2.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Update, nil),
expectedLabels: namespace2Labels,
},
{
name: "request is for deleting namespace, the labels should be from the cache",
attr: admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, namespace1.Name, namespace1.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Delete, nil),
expectedLabels: namespace1Labels,
},
{
name: "request is for namespace/finalizer",
attr: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, namespace1.Name, "mock-name", schema.GroupVersionResource{Resource: "namespaces"}, "finalizers", admission.Create, nil),
expectedLabels: namespace1Labels,
},
{
name: "request is for pod",
attr: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, namespace1.Name, "mock-name", schema.GroupVersionResource{Resource: "pods"}, "", admission.Create, nil),
expectedLabels: namespace1Labels,
},
}
matcher := Matcher{
NamespaceLister: namespaceLister,
}
for _, tt := range tests {
actualLabels, err := matcher.GetNamespaceLabels(tt.attr)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(actualLabels, tt.expectedLabels) {
t.Errorf("expected labels to be %#v, got %#v", tt.expectedLabels, actualLabels)
}
}
}
func TestExemptClusterScopedResource(t *testing.T) {
hook := &registrationv1alpha1.Webhook{
NamespaceSelector: &metav1.LabelSelector{},
}
attr := admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "", "mock-name", schema.GroupVersionResource{Version: "v1", Resource: "nodes"}, "", admission.Create, nil)
matcher := Matcher{}
matches, err := matcher.MatchNamespaceSelector(hook, attr)
if err != nil {
t.Fatal(err)
}
if matches {
t.Errorf("cluster scoped resources (but not a namespace) should be exempted from all webhooks")
}
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"admissionreview.go",
"doc.go",
],
importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/request",
visibility = ["//visibility:public"],
deps = [
"//vendor/k8s.io/api/admission/v1alpha1:go_default_library",
"//vendor/k8s.io/api/authentication/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission: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"],
)
......@@ -14,8 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Package webhook delegates admission checks to dynamically configured webhooks.
package validating
package request
import (
admissionv1alpha1 "k8s.io/api/admission/v1alpha1"
......@@ -25,9 +24,8 @@ import (
"k8s.io/apiserver/pkg/admission"
)
// TODO: move this function to a common package
// createAdmissionReview creates an AdmissionReview for the provided admission.Attributes
func createAdmissionReview(attr admission.Attributes) admissionv1alpha1.AdmissionReview {
// CreateAdmissionReview creates an AdmissionReview for the provided admission.Attributes
func CreateAdmissionReview(attr admission.Attributes) admissionv1alpha1.AdmissionReview {
gvk := attr.GetKind()
gvr := attr.GetResource()
aUserInfo := attr.GetUserInfo()
......
/*
Copyright 2017 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 request creates admissionReview request based on admission attributes.
package request // import "k8s.io/apiserver/pkg/admission/plugin/webhook/request"
......@@ -4,7 +4,6 @@ go_library(
name = "go_default_library",
srcs = [
"admission.go",
"admissionreview.go",
"doc.go",
],
importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/validating",
......@@ -13,13 +12,9 @@ go_library(
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/admission/v1alpha1:go_default_library",
"//vendor/k8s.io/api/admissionregistration/v1alpha1:go_default_library",
"//vendor/k8s.io/api/authentication/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
......@@ -27,10 +22,13 @@ go_library(
"//vendor/k8s.io/apiserver/pkg/admission/configuration:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/initializer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/errors:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/rules:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/versioned:go_default_library",
"//vendor/k8s.io/client-go/informers:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/listers/core/v1:go_default_library",
],
)
......@@ -39,7 +37,6 @@ go_test(
srcs = [
"admission_test.go",
"certs_test.go",
"conversion_test.go",
],
importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/validating",
library = ":go_default_library",
......@@ -49,15 +46,10 @@ go_test(
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config:go_default_library",
"//vendor/k8s.io/apiserver/pkg/apis/example:go_default_library",
"//vendor/k8s.io/apiserver/pkg/apis/example/v1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/apis/example2/v1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
],
......
......@@ -24,7 +24,6 @@ import (
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"strings"
"sync/atomic"
"testing"
......@@ -37,7 +36,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/webhook/config"
"k8s.io/apiserver/pkg/authentication/user"
......@@ -146,7 +144,7 @@ func TestAdmit(t *testing.T) {
t.Fatal(err)
}
namespace := "webhook-test"
wh.namespaceLister = fakeNamespaceLister{map[string]*corev1.Namespace{
wh.namespaceMatcher.NamespaceLister = fakeNamespaceLister{map[string]*corev1.Namespace{
namespace: {
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
......@@ -414,7 +412,7 @@ func TestAdmitCachedClient(t *testing.T) {
wh.clientManager = cm
wh.SetScheme(scheme)
namespace := "webhook-test"
wh.namespaceLister = fakeNamespaceLister{map[string]*corev1.Namespace{
wh.namespaceMatcher.NamespaceLister = fakeNamespaceLister{map[string]*corev1.Namespace{
namespace: {
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
......@@ -638,55 +636,6 @@ func (c *fakeAuthenticationInfoResolver) ClientConfigFor(server string) (*rest.C
return c.restConfig, nil
}
func TestToStatusErr(t *testing.T) {
hookName := "foo"
deniedBy := fmt.Sprintf("admission webhook %q denied the request", hookName)
tests := []struct {
name string
result *metav1.Status
expectedError string
}{
{
"nil result",
nil,
deniedBy + " without explanation",
},
{
"only message",
&metav1.Status{
Message: "you shall not pass",
},
deniedBy + ": you shall not pass",
},
{
"only reason",
&metav1.Status{
Reason: metav1.StatusReasonForbidden,
},
deniedBy + ": Forbidden",
},
{
"message and reason",
&metav1.Status{
Message: "you shall not pass",
Reason: metav1.StatusReasonForbidden,
},
deniedBy + ": you shall not pass",
},
{
"no message, no reason",
&metav1.Status{},
deniedBy + " without explanation",
},
}
for _, test := range tests {
err := toStatusErr(hookName, test.result)
if err == nil || err.Error() != test.expectedError {
t.Errorf("%s: expected an error saying %q, but got %v", test.name, test.expectedError, err)
}
}
}
func newMatchEverythingRules() []registrationv1alpha1.RuleWithOperations {
return []registrationv1alpha1.RuleWithOperations{{
Operations: []registrationv1alpha1.OperationType{registrationv1alpha1.OperationAll},
......@@ -697,89 +646,3 @@ func newMatchEverythingRules() []registrationv1alpha1.RuleWithOperations {
},
}}
}
func TestGetNamespaceLabels(t *testing.T) {
namespace1Labels := map[string]string{
"runlevel": "1",
}
namespace1 := corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "1",
Labels: namespace1Labels,
},
}
namespace2Labels := map[string]string{
"runlevel": "2",
}
namespace2 := corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "2",
Labels: namespace2Labels,
},
}
namespaceLister := fakeNamespaceLister{map[string]*corev1.Namespace{
"1": &namespace1,
},
}
tests := []struct {
name string
attr admission.Attributes
expectedLabels map[string]string
}{
{
name: "request is for creating namespace, the labels should be from the object itself",
attr: admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, "", namespace2.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Create, nil),
expectedLabels: namespace2Labels,
},
{
name: "request is for updating namespace, the labels should be from the new object",
attr: admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, namespace2.Name, namespace2.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Update, nil),
expectedLabels: namespace2Labels,
},
{
name: "request is for deleting namespace, the labels should be from the cache",
attr: admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, namespace1.Name, namespace1.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Delete, nil),
expectedLabels: namespace1Labels,
},
{
name: "request is for namespace/finalizer",
attr: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, namespace1.Name, "mock-name", schema.GroupVersionResource{Resource: "namespaces"}, "finalizers", admission.Create, nil),
expectedLabels: namespace1Labels,
},
{
name: "request is for pod",
attr: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, namespace1.Name, "mock-name", schema.GroupVersionResource{Resource: "pods"}, "", admission.Create, nil),
expectedLabels: namespace1Labels,
},
}
wh, err := NewGenericAdmissionWebhook(nil)
if err != nil {
t.Fatal(err)
}
wh.namespaceLister = namespaceLister
for _, tt := range tests {
actualLabels, err := wh.getNamespaceLabels(tt.attr)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(actualLabels, tt.expectedLabels) {
t.Errorf("expected labels to be %#v, got %#v", tt.expectedLabels, actualLabels)
}
}
}
func TestExemptClusterScopedResource(t *testing.T) {
hook := &registrationv1alpha1.Webhook{
NamespaceSelector: &metav1.LabelSelector{},
}
attr := admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "", "mock-name", schema.GroupVersionResource{Version: "v1", Resource: "nodes"}, "", admission.Create, nil)
g := GenericAdmissionWebhook{}
exempted, err := g.exemptedByNamespaceSelector(hook, attr)
if err != nil {
t.Fatal(err)
}
if !exempted {
t.Errorf("cluster scoped resources (but not a namespace) should be exempted from all webhooks")
}
}
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"attributes.go",
"conversion.go",
"doc.go",
],
importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/versioned",
visibility = ["//visibility:public"],
deps = [
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["conversion_test.go"],
importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/versioned",
library = ":go_default_library",
deps = [
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apiserver/pkg/apis/example:go_default_library",
"//vendor/k8s.io/apiserver/pkg/apis/example/v1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/apis/example2/v1: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"],
)
/*
Copyright 2017 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 versioned
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/admission"
)
// Attributes is a wrapper around the original admission attributes. It allows
// override the internal objects with the versioned ones.
type Attributes struct {
admission.Attributes
OldObject runtime.Object
Object runtime.Object
}
// GetObject overrides the original GetObjects() and it returns the versioned
// object.
func (v Attributes) GetObject() runtime.Object {
return v.Object
}
// GetOldObject overrides the original GetOldObjects() and it returns the
// versioned oldObject.
func (v Attributes) GetOldObject() runtime.Object {
return v.OldObject
}
/*
Copyright 2017 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 versioned
import (
"fmt"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// Convertor converts objects to the desired version.
type Convertor struct {
Scheme *runtime.Scheme
}
// ConvertToGVK converts object to the desired gvk.
func (c Convertor) ConvertToGVK(obj runtime.Object, gvk schema.GroupVersionKind) (runtime.Object, error) {
// Unlike other resources, custom resources do not have internal version, so
// if obj is a custom resource, it should not need conversion.
if obj.GetObjectKind().GroupVersionKind() == gvk {
return obj, nil
}
out, err := c.Scheme.New(gvk)
if err != nil {
return nil, err
}
err = c.Scheme.Convert(obj, out, nil)
if err != nil {
return nil, err
}
return out, nil
}
// Validate checks if the conversion has a scheme.
func (c *Convertor) Validate() error {
if c.Scheme == nil {
return fmt.Errorf("the Convertor requires a scheme")
}
return nil
}
......@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package validating
package versioned
import (
"reflect"
......@@ -39,10 +39,7 @@ func initiateScheme() *runtime.Scheme {
func TestConvertToGVK(t *testing.T) {
scheme := initiateScheme()
w := GenericAdmissionWebhook{
convertor: scheme,
creator: scheme,
}
c := Convertor{Scheme: scheme}
table := map[string]struct {
obj runtime.Object
gvk schema.GroupVersionKind
......@@ -123,7 +120,7 @@ func TestConvertToGVK(t *testing.T) {
for name, test := range table {
t.Run(name, func(t *testing.T) {
actual, err := w.convertToGVK(test.obj, test.gvk)
actual, err := c.ConvertToGVK(test.obj, test.gvk)
if err != nil {
t.Error(err)
}
......
/*
Copyright 2017 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 versioned provides tools for making sure the objects sent to a
// webhook are in a version the webhook understands.
package versioned // import "k8s.io/apiserver/pkg/admission/plugin/webhook/versioned"
......@@ -851,6 +851,18 @@
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/admission/plugin/webhook/errors",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/admission/plugin/webhook/request",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/admission/plugin/webhook/rules",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
......@@ -859,6 +871,10 @@
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/admission/plugin/webhook/versioned",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/apis/apiserver",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
......
......@@ -847,6 +847,18 @@
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/admission/plugin/webhook/errors",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/admission/plugin/webhook/request",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/admission/plugin/webhook/rules",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
......@@ -855,6 +867,10 @@
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/admission/plugin/webhook/versioned",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath": "k8s.io/apiserver/pkg/apis/apiserver",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment