Auto-update dependencies (#131)

Produced via:
  `dep ensure -update knative.dev/test-infra knative.dev/pkg`
/assign n3wscott
This commit is contained in:
Matt Moore 2019-11-15 07:58:02 -08:00 committed by Knative Prow Robot
parent 4276980149
commit 5256f8a931
12 changed files with 471 additions and 91 deletions

4
Gopkg.lock generated
View File

@ -933,7 +933,7 @@
[[projects]]
branch = "master"
digest = "1:ff0b6b2483ef9341cc0c524f1e5bdbba37b56dde366791986e098a01de81dcac"
digest = "1:b1b3870d2746e5277761a3c2811d2b486db20a09763a1d98476cf82f2afbc0fc"
name = "knative.dev/pkg"
packages = [
"apis",
@ -952,7 +952,7 @@
"metrics/metricskey",
]
pruneopts = "T"
revision = "d3841ea2c3cb7246076d771abd5cef8b94ef9031"
revision = "4836f680bbe5dc615e9c822f21c6685d97c6d553"
[[projects]]
branch = "master"

View File

@ -44,15 +44,6 @@ type Convertible interface {
ConvertDown(ctx context.Context, from Convertible) error
}
// Immutable indicates that a particular type has fields that should
// not change after creation.
// DEPRECATED: Use WithinUpdate / GetBaseline from within Validatable instead.
type Immutable interface {
// CheckImmutableFields checks that the current instance's immutable
// fields haven't changed from the provided original.
CheckImmutableFields(ctx context.Context, original Immutable) *FieldError
}
// Listable indicates that a particular type can be returned via the returned
// list type by the API server.
type Listable interface {

View File

@ -168,7 +168,7 @@ func MainWithConfig(ctx context.Context, component string, cfg *rest.Config, cto
metav1.GetOptions{}); err == nil {
cmw.Watch(logging.ConfigMapName(), logging.UpdateLevelFromConfigMap(logger, atomicLevel, component))
} else if !apierrors.IsNotFound(err) {
logger.Fatalw("Error reading ConfigMap: " + logging.ConfigMapName(), zap.Error(err))
logger.Fatalw("Error reading ConfigMap: "+logging.ConfigMapName(), zap.Error(err))
}
// Watch the observability config map
@ -178,7 +178,7 @@ func MainWithConfig(ctx context.Context, component string, cfg *rest.Config, cto
metrics.UpdateExporterFromConfigMap(component, logger),
profilingHandler.UpdateFromConfigMap)
} else if !apierrors.IsNotFound(err) {
logger.Fatalw("Error reading ConfigMap: " + metrics.ConfigMapName(), zap.Error(err))
logger.Fatalw("Error reading ConfigMap: "+metrics.ConfigMapName(), zap.Error(err))
}
if err := cmw.Start(ctx.Done()); err != nil {

View File

@ -25,11 +25,27 @@ import (
"os"
"os/user"
"path"
"sync"
"k8s.io/klog"
"knative.dev/pkg/test/logging"
)
// Flags holds the command line flags or defaults for settings in the user's environment.
// See EnvironmentFlags for a list of supported fields.
var Flags = initializeFlags()
const (
// e2eMetricExporter is the name for the metrics exporter logger
e2eMetricExporter = "e2e-metrics"
// The recommended default log level https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md
klogDefaultLogLevel = "2"
)
var (
flagsSetupOnce = &sync.Once{}
// Flags holds the command line flags or defaults for settings in the user's environment.
// See EnvironmentFlags for a list of supported fields.
Flags = initializeFlags()
)
// EnvironmentFlags define the flags that are needed to run the e2e tests.
type EnvironmentFlags struct {
@ -73,9 +89,42 @@ func initializeFlags() *EnvironmentFlags {
flag.StringVar(&f.Tag, "tag", "latest", "Provide the version tag for the test images.")
klog.InitFlags(nil)
flag.Set("v", klogDefaultLogLevel)
flag.Set("alsologtostderr", "true")
return &f
}
func printFlags() {
fmt.Print("Test Flags: {")
flag.CommandLine.VisitAll(func(f *flag.Flag) {
fmt.Printf("'%s': '%s', ", f.Name, f.Value.String())
})
fmt.Println("}")
}
// SetupLoggingFlags initializes the logging libraries at runtime
func SetupLoggingFlags() {
flagsSetupOnce.Do(func() {
if Flags.LogVerbose {
// If klog verbosity is not set to a non-default value (via "-args -v=X"),
if flag.CommandLine.Lookup("v").Value.String() == klogDefaultLogLevel {
// set up verbosity for klog so round_trippers.go prints:
// URL, request headers, response headers, and partial response body
// See levels in vendor/k8s.io/client-go/transport/round_trippers.go:DebugWrappers for other options
flag.Set("v", "8")
}
printFlags()
}
logging.InitializeLogger(Flags.LogVerbose)
if Flags.EmitMetrics {
logging.InitializeMetricExporter(e2eMetricExporter)
}
})
}
// ImagePath is a helper function to prefix image name with repo and suffix with tag
func ImagePath(name string) string {
return fmt.Sprintf("%s/%s:%s", Flags.DockerRepo, name, Flags.Tag)

View File

@ -38,7 +38,6 @@ type Resource struct {
// Check that Resource may be validated and defaulted.
var _ apis.Validatable = (*Resource)(nil)
var _ apis.Defaultable = (*Resource)(nil)
var _ apis.Immutable = (*Resource)(nil)
var _ apis.Listable = (*Resource)(nil)
// ResourceSpec represents test resource spec.
@ -105,12 +104,7 @@ func (cs *ResourceSpec) Validate(ctx context.Context) *apis.FieldError {
return nil
}
func (current *Resource) CheckImmutableFields(ctx context.Context, og apis.Immutable) *apis.FieldError {
original, ok := og.(*Resource)
if !ok {
return &apis.FieldError{Message: "The provided original was not a Resource"}
}
func (current *Resource) CheckImmutableFields(ctx context.Context, original *Resource) *apis.FieldError {
if original.Spec.FieldThatsImmutable != current.Spec.FieldThatsImmutable {
return &apis.FieldError{
Message: "Immutable field changed",

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package resourcesemantics
package defaulting
import (
"context"
@ -30,13 +30,14 @@ import (
"knative.dev/pkg/logging"
"knative.dev/pkg/system"
"knative.dev/pkg/webhook"
"knative.dev/pkg/webhook/resourcesemantics"
)
// NewAdmissionController constructs a reconciler
func NewAdmissionController(
ctx context.Context,
name, path string,
handlers map[schema.GroupVersionKind]GenericCRD,
handlers map[schema.GroupVersionKind]resourcesemantics.GenericCRD,
wc func(context.Context) context.Context,
disallowUnknownFields bool,
) *controller.Impl {

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package resourcesemantics
package defaulting
import (
"bytes"
@ -30,7 +30,6 @@ import (
"go.uber.org/zap"
admissionv1beta1 "k8s.io/api/admission/v1beta1"
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes"
admissionlisters "k8s.io/client-go/listers/admissionregistration/v1beta1"
@ -44,23 +43,16 @@ import (
"knative.dev/pkg/system"
"knative.dev/pkg/webhook"
certresources "knative.dev/pkg/webhook/certificates/resources"
"knative.dev/pkg/webhook/resourcesemantics"
)
// GenericCRD is the interface definition that allows us to perform the generic
// CRD actions like deciding whether to increment generation and so forth.
type GenericCRD interface {
apis.Defaultable
apis.Validatable
runtime.Object
}
var errMissingNewObject = errors.New("the new object may not be nil")
// reconciler implements the AdmissionController for resources
type reconciler struct {
name string
path string
handlers map[schema.GroupVersionKind]GenericCRD
handlers map[schema.GroupVersionKind]resourcesemantics.GenericCRD
withContext func(context.Context) context.Context
@ -217,10 +209,10 @@ func (ac *reconciler) mutate(ctx context.Context, req *admissionv1beta1.Admissio
}
// nil values denote absence of `old` (create) or `new` (delete) objects.
var oldObj, newObj GenericCRD
var oldObj, newObj resourcesemantics.GenericCRD
if len(newBytes) != 0 {
newObj = handler.DeepCopyObject().(GenericCRD)
newObj = handler.DeepCopyObject().(resourcesemantics.GenericCRD)
newDecoder := json.NewDecoder(bytes.NewBuffer(newBytes))
if ac.disallowUnknownFields {
newDecoder.DisallowUnknownFields()
@ -230,7 +222,7 @@ func (ac *reconciler) mutate(ctx context.Context, req *admissionv1beta1.Admissio
}
}
if len(oldBytes) != 0 {
oldObj = handler.DeepCopyObject().(GenericCRD)
oldObj = handler.DeepCopyObject().(resourcesemantics.GenericCRD)
oldDecoder := json.NewDecoder(bytes.NewBuffer(oldBytes))
if ac.disallowUnknownFields {
oldDecoder.DisallowUnknownFields()
@ -258,7 +250,7 @@ func (ac *reconciler) mutate(ctx context.Context, req *admissionv1beta1.Admissio
if oldObj != nil {
// Copy the old object and set defaults so that we don't reject our own
// defaulting done earlier in the webhook.
oldObj = oldObj.DeepCopyObject().(GenericCRD)
oldObj = oldObj.DeepCopyObject().(resourcesemantics.GenericCRD)
oldObj.SetDefaults(ctx)
s, ok := oldObj.(apis.HasSpec)
@ -293,17 +285,10 @@ func (ac *reconciler) mutate(ctx context.Context, req *admissionv1beta1.Admissio
if newObj == nil {
return nil, errMissingNewObject
}
if err := validate(ctx, newObj); err != nil {
logger.Errorw("Failed the resource specific validation", zap.Error(err))
// Return the error message as-is to give the validation callback
// discretion over (our portion of) the message that the user sees.
return nil, err
}
return json.Marshal(patches)
}
func (ac *reconciler) setUserInfoAnnotations(ctx context.Context, patches duck.JSONPatch, new GenericCRD, groupName string) (duck.JSONPatch, error) {
func (ac *reconciler) setUserInfoAnnotations(ctx context.Context, patches duck.JSONPatch, new resourcesemantics.GenericCRD, groupName string) (duck.JSONPatch, error) {
if new == nil {
return patches, nil
}
@ -341,33 +326,8 @@ func roundTripPatch(bytes []byte, unmarshalled interface{}) (duck.JSONPatch, err
return jsonpatch.CreatePatch(bytes, marshaledBytes)
}
// validate performs validation on the provided "new" CRD.
// For legacy purposes, this also does apis.Immutable validation,
// which is deprecated and will be removed in a future release.
func validate(ctx context.Context, new apis.Validatable) error {
if apis.IsInUpdate(ctx) {
old := apis.GetBaseline(ctx)
if immutableNew, ok := new.(apis.Immutable); ok {
immutableOld, ok := old.(apis.Immutable)
if !ok {
return fmt.Errorf("unexpected type mismatch %T vs. %T", old, new)
}
if err := immutableNew.CheckImmutableFields(ctx, immutableOld); err != nil {
return err
}
}
}
// Can't just `return new.Validate()` because it doesn't properly nil-check.
if err := new.Validate(ctx); err != nil {
return err
}
return nil
}
// setDefaults simply leverages apis.Defaultable to set defaults.
func setDefaults(ctx context.Context, patches duck.JSONPatch, crd GenericCRD) (duck.JSONPatch, error) {
func setDefaults(ctx context.Context, patches duck.JSONPatch, crd resourcesemantics.GenericCRD) (duck.JSONPatch, error) {
before, after := crd.DeepCopyObject(), crd
after.SetDefaults(ctx)

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package resourcesemantics
package defaulting
import (
"context"

View File

@ -0,0 +1,30 @@
/*
Copyright 2019 The Knative 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 resourcesemantics
import (
"k8s.io/apimachinery/pkg/runtime"
"knative.dev/pkg/apis"
)
// GenericCRD is the interface definition that allows us to perform the generic
// CRD actions like deciding whether to increment generation and so forth.
type GenericCRD interface {
apis.Defaultable
apis.Validatable
runtime.Object
}

View File

@ -0,0 +1,84 @@
/*
Copyright 2019 The Knative 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 validation
import (
"context"
// Injection stuff
kubeclient "knative.dev/pkg/client/injection/kube/client"
vwhinformer "knative.dev/pkg/client/injection/kube/informers/admissionregistration/v1beta1/validatingwebhookconfiguration"
secretinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/secret"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/tools/cache"
"knative.dev/pkg/controller"
"knative.dev/pkg/logging"
"knative.dev/pkg/system"
"knative.dev/pkg/webhook"
"knative.dev/pkg/webhook/resourcesemantics"
)
// NewAdmissionController constructs a reconciler
func NewAdmissionController(
ctx context.Context,
name, path string,
handlers map[schema.GroupVersionKind]resourcesemantics.GenericCRD,
wc func(context.Context) context.Context,
disallowUnknownFields bool,
) *controller.Impl {
client := kubeclient.Get(ctx)
vwhInformer := vwhinformer.Get(ctx)
secretInformer := secretinformer.Get(ctx)
options := webhook.GetOptions(ctx)
wh := &reconciler{
name: name,
path: path,
handlers: handlers,
withContext: wc,
disallowUnknownFields: disallowUnknownFields,
secretName: options.SecretName,
client: client,
vwhlister: vwhInformer.Lister(),
secretlister: secretInformer.Lister(),
}
logger := logging.FromContext(ctx)
c := controller.NewImpl(wh, logger, "ConfigMapWebhook")
// Reconcile when the named ValidatingWebhookConfiguration changes.
vwhInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{
FilterFunc: controller.FilterWithName(name),
// It doesn't matter what we enqueue because we will always Reconcile
// the named VWH resource.
Handler: controller.HandleAll(c.Enqueue),
})
// Reconcile when the cert bundle changes.
secretInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{
FilterFunc: controller.FilterWithNameAndNamespace(system.Namespace(), wh.secretName),
// It doesn't matter what we enqueue because we will always Reconcile
// the named VWH resource.
Handler: controller.HandleAll(c.Enqueue),
})
return c
}

View File

@ -0,0 +1,264 @@
/*
Copyright 2019 The Knative 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 validation
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"sort"
"strings"
"github.com/markbates/inflect"
"go.uber.org/zap"
admissionv1beta1 "k8s.io/api/admission/v1beta1"
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes"
admissionlisters "k8s.io/client-go/listers/admissionregistration/v1beta1"
corelisters "k8s.io/client-go/listers/core/v1"
"knative.dev/pkg/apis"
"knative.dev/pkg/controller"
"knative.dev/pkg/kmp"
"knative.dev/pkg/logging"
"knative.dev/pkg/ptr"
"knative.dev/pkg/system"
"knative.dev/pkg/webhook"
certresources "knative.dev/pkg/webhook/certificates/resources"
"knative.dev/pkg/webhook/resourcesemantics"
)
var errMissingNewObject = errors.New("the new object may not be nil")
// reconciler implements the AdmissionController for resources
type reconciler struct {
name string
path string
handlers map[schema.GroupVersionKind]resourcesemantics.GenericCRD
withContext func(context.Context) context.Context
client kubernetes.Interface
vwhlister admissionlisters.ValidatingWebhookConfigurationLister
secretlister corelisters.SecretLister
disallowUnknownFields bool
secretName string
}
var _ controller.Reconciler = (*reconciler)(nil)
var _ webhook.AdmissionController = (*reconciler)(nil)
// Reconcile implements controller.Reconciler
func (ac *reconciler) Reconcile(ctx context.Context, key string) error {
logger := logging.FromContext(ctx)
// Look up the webhook secret, and fetch the CA cert bundle.
secret, err := ac.secretlister.Secrets(system.Namespace()).Get(ac.secretName)
if err != nil {
logger.Errorw("Error fetching secret", zap.Error(err))
return err
}
caCert, ok := secret.Data[certresources.CACert]
if !ok {
return fmt.Errorf("secret %q is missing %q key", ac.secretName, certresources.CACert)
}
// Reconcile the webhook configuration.
return ac.reconcileValidatingWebhook(ctx, caCert)
}
// Path implements AdmissionController
func (ac *reconciler) Path() string {
return ac.path
}
// Admit implements AdmissionController
func (ac *reconciler) Admit(ctx context.Context, request *admissionv1beta1.AdmissionRequest) *admissionv1beta1.AdmissionResponse {
if ac.withContext != nil {
ctx = ac.withContext(ctx)
}
logger := logging.FromContext(ctx)
switch request.Operation {
case admissionv1beta1.Create, admissionv1beta1.Update:
default:
logger.Infof("Unhandled webhook operation, letting it through %v", request.Operation)
return &admissionv1beta1.AdmissionResponse{Allowed: true}
}
if err := ac.validate(ctx, request); err != nil {
return webhook.MakeErrorStatus("validation failed: %v", err)
}
return &admissionv1beta1.AdmissionResponse{Allowed: true}
}
func (ac *reconciler) reconcileValidatingWebhook(ctx context.Context, caCert []byte) error {
logger := logging.FromContext(ctx)
var rules []admissionregistrationv1beta1.RuleWithOperations
for gvk := range ac.handlers {
plural := strings.ToLower(inflect.Pluralize(gvk.Kind))
rules = append(rules, admissionregistrationv1beta1.RuleWithOperations{
Operations: []admissionregistrationv1beta1.OperationType{
admissionregistrationv1beta1.Create,
admissionregistrationv1beta1.Update,
},
Rule: admissionregistrationv1beta1.Rule{
APIGroups: []string{gvk.Group},
APIVersions: []string{gvk.Version},
Resources: []string{plural + "/*"},
},
})
}
// Sort the rules by Group, Version, Kind so that things are deterministically ordered.
sort.Slice(rules, func(i, j int) bool {
lhs, rhs := rules[i], rules[j]
if lhs.APIGroups[0] != rhs.APIGroups[0] {
return lhs.APIGroups[0] < rhs.APIGroups[0]
}
if lhs.APIVersions[0] != rhs.APIVersions[0] {
return lhs.APIVersions[0] < rhs.APIVersions[0]
}
return lhs.Resources[0] < rhs.Resources[0]
})
configuredWebhook, err := ac.vwhlister.Get(ac.name)
if err != nil {
return fmt.Errorf("error retrieving webhook: %v", err)
}
webhook := configuredWebhook.DeepCopy()
// Clear out any previous (bad) OwnerReferences.
// See: https://github.com/knative/serving/issues/5845
webhook.OwnerReferences = nil
for i, wh := range webhook.Webhooks {
if wh.Name != webhook.Name {
continue
}
webhook.Webhooks[i].Rules = rules
webhook.Webhooks[i].ClientConfig.CABundle = caCert
if webhook.Webhooks[i].ClientConfig.Service == nil {
return fmt.Errorf("missing service reference for webhook: %s", wh.Name)
}
webhook.Webhooks[i].ClientConfig.Service.Path = ptr.String(ac.Path())
}
if ok, err := kmp.SafeEqual(configuredWebhook, webhook); err != nil {
return fmt.Errorf("error diffing webhooks: %v", err)
} else if !ok {
logger.Info("Updating webhook")
vwhclient := ac.client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations()
if _, err := vwhclient.Update(webhook); err != nil {
return fmt.Errorf("failed to update webhook: %v", err)
}
} else {
logger.Info("Webhook is valid")
}
return nil
}
func (ac *reconciler) validate(ctx context.Context, req *admissionv1beta1.AdmissionRequest) error {
kind := req.Kind
newBytes := req.Object.Raw
oldBytes := req.OldObject.Raw
// Why, oh why are these different types...
gvk := schema.GroupVersionKind{
Group: kind.Group,
Version: kind.Version,
Kind: kind.Kind,
}
logger := logging.FromContext(ctx)
handler, ok := ac.handlers[gvk]
if !ok {
logger.Errorf("Unhandled kind: %v", gvk)
return fmt.Errorf("unhandled kind: %v", gvk)
}
// nil values denote absence of `old` (create) or `new` (delete) objects.
var oldObj, newObj resourcesemantics.GenericCRD
if len(newBytes) != 0 {
newObj = handler.DeepCopyObject().(resourcesemantics.GenericCRD)
newDecoder := json.NewDecoder(bytes.NewBuffer(newBytes))
if ac.disallowUnknownFields {
newDecoder.DisallowUnknownFields()
}
if err := newDecoder.Decode(&newObj); err != nil {
return fmt.Errorf("cannot decode incoming new object: %v", err)
}
}
if len(oldBytes) != 0 {
oldObj = handler.DeepCopyObject().(resourcesemantics.GenericCRD)
oldDecoder := json.NewDecoder(bytes.NewBuffer(oldBytes))
if ac.disallowUnknownFields {
oldDecoder.DisallowUnknownFields()
}
if err := oldDecoder.Decode(&oldObj); err != nil {
return fmt.Errorf("cannot decode incoming old object: %v", err)
}
}
// Set up the context for defaulting and validation
if oldObj != nil {
// TODO(mattmoor): Remove this after 0.11 cuts.
oldObj.SetDefaults(ctx)
if req.SubResource == "" {
ctx = apis.WithinUpdate(ctx, oldObj)
} else {
ctx = apis.WithinSubResourceUpdate(ctx, oldObj, req.SubResource)
}
} else {
ctx = apis.WithinCreate(ctx)
}
ctx = apis.WithUserInfo(ctx, &req.UserInfo)
// None of the validators will accept a nil value for newObj.
if newObj == nil {
return errMissingNewObject
}
// TODO(mattmoor): Remove this after 0.11 cuts.
newObj.SetDefaults(ctx)
if err := validate(ctx, newObj); err != nil {
logger.Errorw("Failed the resource specific validation", zap.Error(err))
// Return the error message as-is to give the validation callback
// discretion over (our portion of) the message that the user sees.
return err
}
return nil
}
// validate performs validation on the provided "new" CRD.
func validate(ctx context.Context, new apis.Validatable) error {
// Can't just `return new.Validate()` because it doesn't properly nil-check.
if err := new.Validate(ctx); err != nil {
return err
}
return nil
}

View File

@ -80,7 +80,7 @@ type Webhook struct {
Client kubernetes.Interface
Options Options
Logger *zap.SugaredLogger
admissionControllers map[string]AdmissionController
admissionControllers map[string][]AdmissionController
secretlister corelisters.SecretLister
}
@ -114,12 +114,9 @@ func New(
}
// Build up a map of paths to admission controllers for routing handlers.
acs := map[string]AdmissionController{}
acs := map[string][]AdmissionController{}
for _, ac := range admissionControllers {
if _, ok := acs[ac.Path()]; ok {
return nil, fmt.Errorf("duplicate admission controller path %q", ac.Path())
}
acs[ac.Path()] = ac
acs[ac.Path()] = append(acs[ac.Path()], ac)
}
return &Webhook{
@ -212,23 +209,33 @@ func (ac *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
zap.String(logkey.UserInfo, fmt.Sprint(review.Request.UserInfo)))
ctx := logging.WithLogger(r.Context(), logger)
c, ok := ac.admissionControllers[r.URL.Path]
cs, ok := ac.admissionControllers[r.URL.Path]
if !ok {
http.Error(w, fmt.Sprintf("no admission controller registered for: %s", r.URL.Path), http.StatusBadRequest)
return
}
// Where the magic happens.
reviewResponse := c.Admit(ctx, review.Request)
// TODO(mattmoor): Remove support for multiple AdmissionControllers at
// the same path after 0.11 cuts.
// We only TEMPORARILY support multiple AdmissionControllers at the same path because of
// the issue described here: https://github.com/knative/serving/pull/5947
// So we only support a single AdmissionController per path returning Patches.
var response admissionv1beta1.AdmissionReview
if reviewResponse != nil {
response.Response = reviewResponse
response.Response.UID = review.Request.UID
}
for _, c := range cs {
reviewResponse := c.Admit(ctx, review.Request)
logger.Infof("AdmissionReview for %#v: %s/%s response=%#v",
review.Request.Kind, review.Request.Namespace, review.Request.Name, reviewResponse)
logger.Infof("AdmissionReview for %#v: %s/%s response=%#v",
review.Request.Kind, review.Request.Namespace, review.Request.Name, reviewResponse)
if !reviewResponse.Allowed {
response.Response = reviewResponse
break
}
if reviewResponse.PatchType != nil || response.Response == nil {
response.Response = reviewResponse
}
}
response.Response.UID = review.Request.UID
if err := json.NewEncoder(w).Encode(response); err != nil {
http.Error(w, fmt.Sprintf("could encode response: %v", err), http.StatusInternalServerError)