Merge pull request #52673 from p0lyn0mial/webhook_default_service_resolver

Automatic merge from submit-queue (batch tested with PRs 52831, 52764, 52763, 52673, 52558). 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>..

default service resolver for webhook admission

**What this PR does / why we need it**:
provides a default service resolver for webhook plugin. The rationale behind is that webhook plugins names can be resolved by a dns server working inside a cluster.


**Release note**:

```
NONE
```
This commit is contained in:
Kubernetes Submit Queue 2017-09-23 22:57:54 -07:00 committed by GitHub
commit 7f3f986402
5 changed files with 107 additions and 4 deletions

View File

@ -188,9 +188,6 @@ func (i *PluginInitializer) Initialize(plugin admission.Interface) {
}
if wants, ok := plugin.(WantsServiceResolver); ok {
if i.serviceResolver == nil {
panic("An admission plugin wants the service resolver, but it was not provided.")
}
wants.SetServiceResolver(i.serviceResolver)
}

View File

@ -12,6 +12,7 @@ go_test(
"admission_test.go",
"certs_test.go",
"rules_test.go",
"serviceresolver_test.go",
],
library = ":go_default_library",
deps = [
@ -32,6 +33,7 @@ go_library(
"admission.go",
"doc.go",
"rules.go",
"serviceresolver.go",
],
deps = [
"//pkg/api:go_default_library",

View File

@ -97,6 +97,7 @@ func NewGenericAdmissionWebhook() (*GenericAdmissionWebhook, error) {
negotiatedSerializer: serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{
Serializer: api.Codecs.LegacyCodec(admissionv1alpha1.SchemeGroupVersion),
}),
serviceResolver: defaultServiceResolver{},
}, nil
}
@ -121,8 +122,12 @@ func (a *GenericAdmissionWebhook) SetProxyTransport(pt *http.Transport) {
a.proxyTransport = pt
}
// SetServiceResolver sets a service resolver for the webhook admission plugin.
// Passing a nil resolver does not have an effect, instead a default one will be used.
func (a *GenericAdmissionWebhook) SetServiceResolver(sr admissioninit.ServiceResolver) {
a.serviceResolver = sr
if sr != nil {
a.serviceResolver = sr
}
}
func (a *GenericAdmissionWebhook) SetClientCert(cert, key []byte) {

View File

@ -0,0 +1,42 @@
/*
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 webhook checks a webhook for configured operation admission
package webhook
import (
"errors"
"fmt"
"net/url"
admissioninit "k8s.io/kubernetes/pkg/kubeapiserver/admission"
)
type defaultServiceResolver struct{}
var _ admissioninit.ServiceResolver = defaultServiceResolver{}
// ResolveEndpoint constructs a service URL from a given namespace and name
// note that the name and namespace are required and by default all created addresses use HTTPS scheme.
// for example:
// name=ross namespace=andromeda resolves to https://ross.andromeda.svc
func (sr defaultServiceResolver) ResolveEndpoint(namespace, name string) (*url.URL, error) {
if len(name) == 0 || len(namespace) == 0 {
return &url.URL{}, errors.New("cannot resolve an empty service name or namespace")
}
return url.Parse(fmt.Sprintf("https://%s.%s.svc", name, namespace))
}

View File

@ -0,0 +1,57 @@
/*
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 webhook checks a webhook for configured operation admission
package webhook
import (
"fmt"
"testing"
)
func TestDefaultServiceResolver(t *testing.T) {
scenarios := []struct {
serviceName string
serviceNamespace string
expectedOutput string
expectError bool
}{
// scenario 1: a service name along with a namespace resolves
{serviceName: "ross", serviceNamespace: "andromeda", expectedOutput: "https://ross.andromeda.svc"},
// scenario 2: a service name without a namespace does not resolve
{serviceName: "ross", expectError: true},
// scenario 3: cannot resolve an empty service name
{serviceNamespace: "andromeda", expectError: true},
}
// act
for index, scenario := range scenarios {
t.Run(fmt.Sprintf("scenario %d", index), func(t *testing.T) {
target := defaultServiceResolver{}
serviceURL, err := target.ResolveEndpoint(scenario.serviceNamespace, scenario.serviceName)
if err != nil && !scenario.expectError {
t.Errorf("unexpected error has occurred = %v", err)
}
if err == nil && scenario.expectError {
t.Error("expected an error but got nothing")
}
if serviceURL.String() != scenario.expectedOutput {
t.Errorf("expected = %s, got = %s", scenario.expectedOutput, serviceURL.String())
}
})
}
}