default service resolver for webhook admission
the rationale behind is that webhook plugins names can be resolved by a dns server working inside a cluster.
This commit is contained in:
		| @@ -188,9 +188,6 @@ func (i *PluginInitializer) Initialize(plugin admission.Interface) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if wants, ok := plugin.(WantsServiceResolver); ok { | 	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) | 		wants.SetServiceResolver(i.serviceResolver) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ go_test( | |||||||
|         "admission_test.go", |         "admission_test.go", | ||||||
|         "certs_test.go", |         "certs_test.go", | ||||||
|         "rules_test.go", |         "rules_test.go", | ||||||
|  |         "serviceresolver_test.go", | ||||||
|     ], |     ], | ||||||
|     library = ":go_default_library", |     library = ":go_default_library", | ||||||
|     deps = [ |     deps = [ | ||||||
| @@ -32,6 +33,7 @@ go_library( | |||||||
|         "admission.go", |         "admission.go", | ||||||
|         "doc.go", |         "doc.go", | ||||||
|         "rules.go", |         "rules.go", | ||||||
|  |         "serviceresolver.go", | ||||||
|     ], |     ], | ||||||
|     deps = [ |     deps = [ | ||||||
|         "//pkg/api:go_default_library", |         "//pkg/api:go_default_library", | ||||||
|   | |||||||
| @@ -97,6 +97,7 @@ func NewGenericAdmissionWebhook() (*GenericAdmissionWebhook, error) { | |||||||
| 		negotiatedSerializer: serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{ | 		negotiatedSerializer: serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{ | ||||||
| 			Serializer: api.Codecs.LegacyCodec(admissionv1alpha1.SchemeGroupVersion), | 			Serializer: api.Codecs.LegacyCodec(admissionv1alpha1.SchemeGroupVersion), | ||||||
| 		}), | 		}), | ||||||
|  | 		serviceResolver: defaultServiceResolver{}, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -121,8 +122,12 @@ func (a *GenericAdmissionWebhook) SetProxyTransport(pt *http.Transport) { | |||||||
| 	a.proxyTransport = pt | 	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) { | func (a *GenericAdmissionWebhook) SetServiceResolver(sr admissioninit.ServiceResolver) { | ||||||
|  | 	if sr != nil { | ||||||
| 		a.serviceResolver = sr | 		a.serviceResolver = sr | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (a *GenericAdmissionWebhook) SetClientCert(cert, key []byte) { | func (a *GenericAdmissionWebhook) SetClientCert(cert, key []byte) { | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								plugin/pkg/admission/webhook/serviceresolver.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								plugin/pkg/admission/webhook/serviceresolver.go
									
									
									
									
									
										Normal 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)) | ||||||
|  | } | ||||||
							
								
								
									
										57
									
								
								plugin/pkg/admission/webhook/serviceresolver_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								plugin/pkg/admission/webhook/serviceresolver_test.go
									
									
									
									
									
										Normal 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()) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 p0lyn0mial
					p0lyn0mial