mirror of https://github.com/knative/caching.git
Auto-update dependencies (#131)
Produced via: `dep ensure -update knative.dev/test-infra knative.dev/pkg` /assign n3wscott
This commit is contained in:
parent
4276980149
commit
5256f8a931
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resourcesemantics
|
||||
package defaulting
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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,24 +209,34 @@ 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)
|
||||
|
||||
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)
|
||||
return
|
||||
|
|
|
|||
Loading…
Reference in New Issue