Merge pull request #67798 from mbohlool/crd_refactoring
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions here: https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md. Refactor admission webhook client code to a apiserver/pkg/util package As part of #67006 This refactoring enable us to share code between admission webhooks and CRD conversion webhooks. @deads2k @lavalamp @sttts @kubernetes/sig-api-machinery-misc Kubernetes-commit: 14eb029fba5717e2dfe3434a682f9aec4752c44d
This commit is contained in:
commit
36e49471e7
|
@ -984,215 +984,215 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/apitesting",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/apitesting",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/apitesting/fuzzer",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/apitesting/fuzzer",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/apitesting/roundtrip",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/apitesting/roundtrip",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/equality",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/equality",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/errors",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/errors",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/meta",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/meta",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/resource",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/resource",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/validation",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/validation",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/validation/path",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/validation/path",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/fuzzer",
|
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/fuzzer",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/internalversion",
|
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/internalversion",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1",
|
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
|
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/validation",
|
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/validation",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1beta1",
|
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1beta1",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/conversion",
|
"ImportPath": "k8s.io/apimachinery/pkg/conversion",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
|
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/fields",
|
"ImportPath": "k8s.io/apimachinery/pkg/fields",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/labels",
|
"ImportPath": "k8s.io/apimachinery/pkg/labels",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime",
|
"ImportPath": "k8s.io/apimachinery/pkg/runtime",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/schema",
|
"ImportPath": "k8s.io/apimachinery/pkg/runtime/schema",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer",
|
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/json",
|
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/json",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/protobuf",
|
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/protobuf",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/recognizer",
|
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/recognizer",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/streaming",
|
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/streaming",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/versioning",
|
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/versioning",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/selection",
|
"ImportPath": "k8s.io/apimachinery/pkg/selection",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/types",
|
"ImportPath": "k8s.io/apimachinery/pkg/types",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/cache",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/cache",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/clock",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/clock",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/diff",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/diff",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/errors",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/errors",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/framer",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/framer",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/intstr",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/intstr",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/json",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/json",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/mergepatch",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/mergepatch",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/naming",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/naming",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/net",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/net",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/rand",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/rand",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/runtime",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/runtime",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/sets",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/sets",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/strategicpatch",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/strategicpatch",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/uuid",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/uuid",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/validation",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/validation",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/validation/field",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/validation/field",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/wait",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/wait",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/waitgroup",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/waitgroup",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/yaml",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/yaml",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/version",
|
"ImportPath": "k8s.io/apimachinery/pkg/version",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/watch",
|
"ImportPath": "k8s.io/apimachinery/pkg/watch",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/json",
|
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/json",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/reflect",
|
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/reflect",
|
||||||
"Rev": "c6b66c9c507abbefa93ad83f7fe8c9b52ca1ae30"
|
"Rev": "7022e8e5e6f8d55cdc303669184073a493482496"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/discovery",
|
"ImportPath": "k8s.io/client-go/discovery",
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||||
"k8s.io/api/admissionregistration/v1beta1"
|
"k8s.io/api/admissionregistration/v1beta1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
@ -29,6 +30,7 @@ import (
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/config"
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/config"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/namespace"
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/namespace"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/rules"
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/rules"
|
||||||
|
"k8s.io/apiserver/pkg/util/webhook"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
)
|
)
|
||||||
|
@ -40,7 +42,7 @@ type Webhook struct {
|
||||||
sourceFactory sourceFactory
|
sourceFactory sourceFactory
|
||||||
|
|
||||||
hookSource Source
|
hookSource Source
|
||||||
clientManager *config.ClientManager
|
clientManager *webhook.ClientManager
|
||||||
convertor *convertor
|
convertor *convertor
|
||||||
namespaceMatcher *namespace.Matcher
|
namespaceMatcher *namespace.Matcher
|
||||||
dispatcher Dispatcher
|
dispatcher Dispatcher
|
||||||
|
@ -52,7 +54,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type sourceFactory func(f informers.SharedInformerFactory) Source
|
type sourceFactory func(f informers.SharedInformerFactory) Source
|
||||||
type dispatcherFactory func(cm *config.ClientManager) Dispatcher
|
type dispatcherFactory func(cm *webhook.ClientManager) Dispatcher
|
||||||
|
|
||||||
// NewWebhook creates a new generic admission webhook.
|
// NewWebhook creates a new generic admission webhook.
|
||||||
func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory sourceFactory, dispatcherFactory dispatcherFactory) (*Webhook, error) {
|
func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory sourceFactory, dispatcherFactory dispatcherFactory) (*Webhook, error) {
|
||||||
|
@ -61,17 +63,17 @@ func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cm, err := config.NewClientManager()
|
cm, err := webhook.NewClientManager(admissionv1beta1.SchemeGroupVersion, admissionv1beta1.AddToScheme)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
authInfoResolver, err := config.NewDefaultAuthenticationInfoResolver(kubeconfigFile)
|
authInfoResolver, err := webhook.NewDefaultAuthenticationInfoResolver(kubeconfigFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Set defaults which may be overridden later.
|
// Set defaults which may be overridden later.
|
||||||
cm.SetAuthenticationInfoResolver(authInfoResolver)
|
cm.SetAuthenticationInfoResolver(authInfoResolver)
|
||||||
cm.SetServiceResolver(config.NewDefaultServiceResolver())
|
cm.SetServiceResolver(webhook.NewDefaultServiceResolver())
|
||||||
|
|
||||||
return &Webhook{
|
return &Webhook{
|
||||||
Handler: handler,
|
Handler: handler,
|
||||||
|
@ -86,13 +88,13 @@ func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory
|
||||||
// SetAuthenticationInfoResolverWrapper sets the
|
// SetAuthenticationInfoResolverWrapper sets the
|
||||||
// AuthenticationInfoResolverWrapper.
|
// AuthenticationInfoResolverWrapper.
|
||||||
// TODO find a better way wire this, but keep this pull small for now.
|
// TODO find a better way wire this, but keep this pull small for now.
|
||||||
func (a *Webhook) SetAuthenticationInfoResolverWrapper(wrapper config.AuthenticationInfoResolverWrapper) {
|
func (a *Webhook) SetAuthenticationInfoResolverWrapper(wrapper webhook.AuthenticationInfoResolverWrapper) {
|
||||||
a.clientManager.SetAuthenticationInfoResolverWrapper(wrapper)
|
a.clientManager.SetAuthenticationInfoResolverWrapper(wrapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetServiceResolver sets a service resolver for the webhook admission plugin.
|
// 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.
|
// Passing a nil resolver does not have an effect, instead a default one will be used.
|
||||||
func (a *Webhook) SetServiceResolver(sr config.ServiceResolver) {
|
func (a *Webhook) SetServiceResolver(sr webhook.ServiceResolver) {
|
||||||
a.clientManager.SetServiceResolver(sr)
|
a.clientManager.SetServiceResolver(sr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,13 +20,13 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
webhookconfig "k8s.io/apiserver/pkg/admission/plugin/webhook/config"
|
"k8s.io/apiserver/pkg/util/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WantsServiceResolver defines a function that accepts a ServiceResolver for
|
// WantsServiceResolver defines a function that accepts a ServiceResolver for
|
||||||
// admission plugins that need to make calls to services.
|
// admission plugins that need to make calls to services.
|
||||||
type WantsServiceResolver interface {
|
type WantsServiceResolver interface {
|
||||||
SetServiceResolver(webhookconfig.ServiceResolver)
|
SetServiceResolver(webhook.ServiceResolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceResolver knows how to convert a service reference into an actual
|
// ServiceResolver knows how to convert a service reference into an actual
|
||||||
|
@ -38,22 +38,22 @@ type ServiceResolver interface {
|
||||||
// WantsAuthenticationInfoResolverWrapper defines a function that wraps the standard AuthenticationInfoResolver
|
// WantsAuthenticationInfoResolverWrapper defines a function that wraps the standard AuthenticationInfoResolver
|
||||||
// to allow the apiserver to control what is returned as auth info
|
// to allow the apiserver to control what is returned as auth info
|
||||||
type WantsAuthenticationInfoResolverWrapper interface {
|
type WantsAuthenticationInfoResolverWrapper interface {
|
||||||
SetAuthenticationInfoResolverWrapper(webhookconfig.AuthenticationInfoResolverWrapper)
|
SetAuthenticationInfoResolverWrapper(wrapper webhook.AuthenticationInfoResolverWrapper)
|
||||||
admission.InitializationValidator
|
admission.InitializationValidator
|
||||||
}
|
}
|
||||||
|
|
||||||
// PluginInitializer is used for initialization of the webhook admission plugin.
|
// PluginInitializer is used for initialization of the webhook admission plugin.
|
||||||
type PluginInitializer struct {
|
type PluginInitializer struct {
|
||||||
serviceResolver webhookconfig.ServiceResolver
|
serviceResolver webhook.ServiceResolver
|
||||||
authenticationInfoResolverWrapper webhookconfig.AuthenticationInfoResolverWrapper
|
authenticationInfoResolverWrapper webhook.AuthenticationInfoResolverWrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ admission.PluginInitializer = &PluginInitializer{}
|
var _ admission.PluginInitializer = &PluginInitializer{}
|
||||||
|
|
||||||
// NewPluginInitializer constructs new instance of PluginInitializer
|
// NewPluginInitializer constructs new instance of PluginInitializer
|
||||||
func NewPluginInitializer(
|
func NewPluginInitializer(
|
||||||
authenticationInfoResolverWrapper webhookconfig.AuthenticationInfoResolverWrapper,
|
authenticationInfoResolverWrapper webhook.AuthenticationInfoResolverWrapper,
|
||||||
serviceResolver webhookconfig.ServiceResolver,
|
serviceResolver webhook.ServiceResolver,
|
||||||
) *PluginInitializer {
|
) *PluginInitializer {
|
||||||
return &PluginInitializer{
|
return &PluginInitializer{
|
||||||
authenticationInfoResolverWrapper: authenticationInfoResolverWrapper,
|
authenticationInfoResolverWrapper: authenticationInfoResolverWrapper,
|
||||||
|
|
|
@ -21,7 +21,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/config"
|
"k8s.io/apiserver/pkg/util/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
type doNothingAdmission struct{}
|
type doNothingAdmission struct{}
|
||||||
|
@ -39,7 +39,7 @@ type serviceWanter struct {
|
||||||
got ServiceResolver
|
got ServiceResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceWanter) SetServiceResolver(sr config.ServiceResolver) { s.got = sr }
|
func (s *serviceWanter) SetServiceResolver(sr webhook.ServiceResolver) { s.got = sr }
|
||||||
|
|
||||||
func TestWantsServiceResolver(t *testing.T) {
|
func TestWantsServiceResolver(t *testing.T) {
|
||||||
sw := &serviceWanter{}
|
sw := &serviceWanter{}
|
||||||
|
|
|
@ -33,19 +33,20 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
admissionmetrics "k8s.io/apiserver/pkg/admission/metrics"
|
admissionmetrics "k8s.io/apiserver/pkg/admission/metrics"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/config"
|
|
||||||
webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
|
webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/request"
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/request"
|
||||||
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/util"
|
||||||
|
"k8s.io/apiserver/pkg/util/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mutatingDispatcher struct {
|
type mutatingDispatcher struct {
|
||||||
cm *config.ClientManager
|
cm *webhook.ClientManager
|
||||||
plugin *Plugin
|
plugin *Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMutatingDispatcher(p *Plugin) func(cm *config.ClientManager) generic.Dispatcher {
|
func newMutatingDispatcher(p *Plugin) func(cm *webhook.ClientManager) generic.Dispatcher {
|
||||||
return func(cm *config.ClientManager) generic.Dispatcher {
|
return func(cm *webhook.ClientManager) generic.Dispatcher {
|
||||||
return &mutatingDispatcher{cm, p}
|
return &mutatingDispatcher{cm, p}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +63,7 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr *generic.Version
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore
|
ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore
|
||||||
if callErr, ok := err.(*webhookerrors.ErrCallingWebhook); ok {
|
if callErr, ok := err.(*webhook.ErrCallingWebhook); ok {
|
||||||
if ignoreClientCallFailures {
|
if ignoreClientCallFailures {
|
||||||
glog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr)
|
glog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr)
|
||||||
utilruntime.HandleError(callErr)
|
utilruntime.HandleError(callErr)
|
||||||
|
@ -84,7 +85,7 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr *generic.Version
|
||||||
func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta1.Webhook, attr *generic.VersionedAttributes) error {
|
func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta1.Webhook, attr *generic.VersionedAttributes) error {
|
||||||
if attr.IsDryRun() {
|
if attr.IsDryRun() {
|
||||||
if h.SideEffects == nil {
|
if h.SideEffects == nil {
|
||||||
return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")}
|
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")}
|
||||||
}
|
}
|
||||||
if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) {
|
if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) {
|
||||||
return webhookerrors.NewDryRunUnsupportedErr(h.Name)
|
return webhookerrors.NewDryRunUnsupportedErr(h.Name)
|
||||||
|
@ -93,17 +94,17 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta
|
||||||
|
|
||||||
// Make the webhook request
|
// Make the webhook request
|
||||||
request := request.CreateAdmissionReview(attr)
|
request := request.CreateAdmissionReview(attr)
|
||||||
client, err := a.cm.HookClient(h)
|
client, err := a.cm.HookClient(util.HookClientConfigForWebhook(h))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||||
}
|
}
|
||||||
response := &admissionv1beta1.AdmissionReview{}
|
response := &admissionv1beta1.AdmissionReview{}
|
||||||
if err := client.Post().Context(ctx).Body(&request).Do().Into(response); err != nil {
|
if err := client.Post().Context(ctx).Body(&request).Do().Into(response); err != nil {
|
||||||
return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.Response == nil {
|
if response.Response == nil {
|
||||||
return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")}
|
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")}
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range response.Response.AuditAnnotations {
|
for k, v := range response.Response.AuditAnnotations {
|
||||||
|
|
|
@ -19,22 +19,22 @@ package testing
|
||||||
import (
|
import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/config"
|
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/testcerts"
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/testcerts"
|
||||||
|
"k8s.io/apiserver/pkg/util/webhook"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Wrapper turns an AuthenticationInfoResolver into a AuthenticationInfoResolverWrapper that unconditionally
|
// Wrapper turns an AuthenticationInfoResolver into a AuthenticationInfoResolverWrapper that unconditionally
|
||||||
// returns the given AuthenticationInfoResolver.
|
// returns the given AuthenticationInfoResolver.
|
||||||
func Wrapper(r config.AuthenticationInfoResolver) func(config.AuthenticationInfoResolver) config.AuthenticationInfoResolver {
|
func Wrapper(r webhook.AuthenticationInfoResolver) func(webhook.AuthenticationInfoResolver) webhook.AuthenticationInfoResolver {
|
||||||
return func(config.AuthenticationInfoResolver) config.AuthenticationInfoResolver {
|
return func(webhook.AuthenticationInfoResolver) webhook.AuthenticationInfoResolver {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAuthenticationInfoResolver creates a fake AuthenticationInfoResolver that counts cache misses on
|
// NewAuthenticationInfoResolver creates a fake AuthenticationInfoResolver that counts cache misses on
|
||||||
// every call to its methods.
|
// every call to its methods.
|
||||||
func NewAuthenticationInfoResolver(cacheMisses *int32) config.AuthenticationInfoResolver {
|
func NewAuthenticationInfoResolver(cacheMisses *int32) webhook.AuthenticationInfoResolver {
|
||||||
return &authenticationInfoResolver{
|
return &authenticationInfoResolver{
|
||||||
restConfig: &rest.Config{
|
restConfig: &rest.Config{
|
||||||
TLSClientConfig: rest.TLSClientConfig{
|
TLSClientConfig: rest.TLSClientConfig{
|
||||||
|
|
|
@ -20,7 +20,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/config"
|
"k8s.io/apiserver/pkg/util/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
type serviceResolver struct {
|
type serviceResolver struct {
|
||||||
|
@ -29,7 +29,7 @@ type serviceResolver struct {
|
||||||
|
|
||||||
// NewServiceResolver returns a static service resolve that return the given URL or
|
// NewServiceResolver returns a static service resolve that return the given URL or
|
||||||
// an error for the failResolve namespace.
|
// an error for the failResolve namespace.
|
||||||
func NewServiceResolver(base url.URL) config.ServiceResolver {
|
func NewServiceResolver(base url.URL) webhook.ServiceResolver {
|
||||||
return &serviceResolver{base}
|
return &serviceResolver{base}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 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 util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/api/admissionregistration/v1beta1"
|
||||||
|
"k8s.io/apiserver/pkg/util/webhook"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HookClientConfigForWebhook construct a webhook.ClientConfig using a v1beta1.Webhook API object.
|
||||||
|
// webhook.ClientConfig is used to create a HookClient and the purpose of the config struct is to
|
||||||
|
// share that with other packages that need to create a HookClient.
|
||||||
|
func HookClientConfigForWebhook(w *v1beta1.Webhook) webhook.ClientConfig {
|
||||||
|
ret := webhook.ClientConfig{Name: w.Name, CABundle: w.ClientConfig.CABundle}
|
||||||
|
if w.ClientConfig.URL != nil {
|
||||||
|
ret.URL = *w.ClientConfig.URL
|
||||||
|
}
|
||||||
|
if w.ClientConfig.Service != nil {
|
||||||
|
ret.Service = &webhook.ClientConfigService{
|
||||||
|
Name: w.ClientConfig.Service.Name,
|
||||||
|
Namespace: w.ClientConfig.Service.Namespace,
|
||||||
|
}
|
||||||
|
if w.ClientConfig.Service.Path != nil {
|
||||||
|
ret.Service.Path = *w.ClientConfig.Service.Path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
|
@ -29,17 +29,18 @@ import (
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
admissionmetrics "k8s.io/apiserver/pkg/admission/metrics"
|
admissionmetrics "k8s.io/apiserver/pkg/admission/metrics"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/config"
|
|
||||||
webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
|
webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/request"
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/request"
|
||||||
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/util"
|
||||||
|
"k8s.io/apiserver/pkg/util/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
type validatingDispatcher struct {
|
type validatingDispatcher struct {
|
||||||
cm *config.ClientManager
|
cm *webhook.ClientManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func newValidatingDispatcher(cm *config.ClientManager) generic.Dispatcher {
|
func newValidatingDispatcher(cm *webhook.ClientManager) generic.Dispatcher {
|
||||||
return &validatingDispatcher{cm}
|
return &validatingDispatcher{cm}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@ func (d *validatingDispatcher) Dispatch(ctx context.Context, attr *generic.Versi
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore
|
ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore
|
||||||
if callErr, ok := err.(*webhookerrors.ErrCallingWebhook); ok {
|
if callErr, ok := err.(*webhook.ErrCallingWebhook); ok {
|
||||||
if ignoreClientCallFailures {
|
if ignoreClientCallFailures {
|
||||||
glog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr)
|
glog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr)
|
||||||
utilruntime.HandleError(callErr)
|
utilruntime.HandleError(callErr)
|
||||||
|
@ -99,7 +100,7 @@ func (d *validatingDispatcher) Dispatch(ctx context.Context, attr *generic.Versi
|
||||||
func (d *validatingDispatcher) callHook(ctx context.Context, h *v1beta1.Webhook, attr *generic.VersionedAttributes) error {
|
func (d *validatingDispatcher) callHook(ctx context.Context, h *v1beta1.Webhook, attr *generic.VersionedAttributes) error {
|
||||||
if attr.IsDryRun() {
|
if attr.IsDryRun() {
|
||||||
if h.SideEffects == nil {
|
if h.SideEffects == nil {
|
||||||
return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")}
|
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")}
|
||||||
}
|
}
|
||||||
if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) {
|
if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) {
|
||||||
return webhookerrors.NewDryRunUnsupportedErr(h.Name)
|
return webhookerrors.NewDryRunUnsupportedErr(h.Name)
|
||||||
|
@ -108,17 +109,17 @@ func (d *validatingDispatcher) callHook(ctx context.Context, h *v1beta1.Webhook,
|
||||||
|
|
||||||
// Make the webhook request
|
// Make the webhook request
|
||||||
request := request.CreateAdmissionReview(attr)
|
request := request.CreateAdmissionReview(attr)
|
||||||
client, err := d.cm.HookClient(h)
|
client, err := d.cm.HookClient(util.HookClientConfigForWebhook(h))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||||
}
|
}
|
||||||
response := &admissionv1beta1.AdmissionReview{}
|
response := &admissionv1beta1.AdmissionReview{}
|
||||||
if err := client.Post().Context(ctx).Body(&request).Do().Into(response); err != nil {
|
if err := client.Post().Context(ctx).Body(&request).Do().Into(response); err != nil {
|
||||||
return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.Response == nil {
|
if response.Response == nil {
|
||||||
return &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")}
|
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")}
|
||||||
}
|
}
|
||||||
for k, v := range response.Response.AuditAnnotations {
|
for k, v := range response.Response.AuditAnnotations {
|
||||||
key := h.Name + "/" + k
|
key := h.Name + "/" + k
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package config
|
package webhook
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -47,10 +47,12 @@ type AuthenticationInfoResolverDelegator struct {
|
||||||
ClientConfigForServiceFunc func(serviceName, serviceNamespace string) (*rest.Config, error)
|
ClientConfigForServiceFunc func(serviceName, serviceNamespace string) (*rest.Config, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClientConfigFor returns client config for given server.
|
||||||
func (a *AuthenticationInfoResolverDelegator) ClientConfigFor(server string) (*rest.Config, error) {
|
func (a *AuthenticationInfoResolverDelegator) ClientConfigFor(server string) (*rest.Config, error) {
|
||||||
return a.ClientConfigForFunc(server)
|
return a.ClientConfigForFunc(server)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClientConfigForService returns client config for given service.
|
||||||
func (a *AuthenticationInfoResolverDelegator) ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error) {
|
func (a *AuthenticationInfoResolverDelegator) ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error) {
|
||||||
return a.ClientConfigForServiceFunc(serviceName, serviceNamespace)
|
return a.ClientConfigForServiceFunc(serviceName, serviceNamespace)
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package config
|
package webhook
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package config
|
package webhook
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -24,13 +24,11 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
lru "github.com/hashicorp/golang-lru"
|
"github.com/hashicorp/golang-lru"
|
||||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
|
||||||
"k8s.io/api/admissionregistration/v1beta1"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||||
webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
|
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,9 +36,20 @@ const (
|
||||||
defaultCacheSize = 200
|
defaultCacheSize = 200
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// ClientConfig defines parameters required for creating a hook client.
|
||||||
ErrNeedServiceOrURL = errors.New("webhook configuration must have either service or URL")
|
type ClientConfig struct {
|
||||||
)
|
Name string
|
||||||
|
URL string
|
||||||
|
CABundle []byte
|
||||||
|
Service *ClientConfigService
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientConfigService defines service discovery parameters of the webhook.
|
||||||
|
type ClientConfigService struct {
|
||||||
|
Name string
|
||||||
|
Namespace string
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
// ClientManager builds REST clients to talk to webhooks. It caches the clients
|
// ClientManager builds REST clients to talk to webhooks. It caches the clients
|
||||||
// to avoid duplicate creation.
|
// to avoid duplicate creation.
|
||||||
|
@ -52,19 +61,19 @@ type ClientManager struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClientManager creates a clientManager.
|
// NewClientManager creates a clientManager.
|
||||||
func NewClientManager() (ClientManager, error) {
|
func NewClientManager(gv schema.GroupVersion, addToSchemaFunc func(s *runtime.Scheme) error) (ClientManager, error) {
|
||||||
cache, err := lru.New(defaultCacheSize)
|
cache, err := lru.New(defaultCacheSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ClientManager{}, err
|
return ClientManager{}, err
|
||||||
}
|
}
|
||||||
admissionScheme := runtime.NewScheme()
|
admissionScheme := runtime.NewScheme()
|
||||||
if err := admissionv1beta1.AddToScheme(admissionScheme); err != nil {
|
if err := addToSchemaFunc(admissionScheme); err != nil {
|
||||||
return ClientManager{}, err
|
return ClientManager{}, err
|
||||||
}
|
}
|
||||||
return ClientManager{
|
return ClientManager{
|
||||||
cache: cache,
|
cache: cache,
|
||||||
negotiatedSerializer: serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{
|
negotiatedSerializer: serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{
|
||||||
Serializer: serializer.NewCodecFactory(admissionScheme).LegacyCodec(admissionv1beta1.SchemeGroupVersion),
|
Serializer: serializer.NewCodecFactory(admissionScheme).LegacyCodec(gv),
|
||||||
}),
|
}),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -106,8 +115,10 @@ func (cm *ClientManager) Validate() error {
|
||||||
|
|
||||||
// HookClient get a RESTClient from the cache, or constructs one based on the
|
// HookClient get a RESTClient from the cache, or constructs one based on the
|
||||||
// webhook configuration.
|
// webhook configuration.
|
||||||
func (cm *ClientManager) HookClient(h *v1beta1.Webhook) (*rest.RESTClient, error) {
|
func (cm *ClientManager) HookClient(cc ClientConfig) (*rest.RESTClient, error) {
|
||||||
cacheKey, err := json.Marshal(h.ClientConfig)
|
ccWithNoName := cc
|
||||||
|
ccWithNoName.Name = ""
|
||||||
|
cacheKey, err := json.Marshal(ccWithNoName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -120,7 +131,7 @@ func (cm *ClientManager) HookClient(h *v1beta1.Webhook) (*rest.RESTClient, error
|
||||||
if len(cfg.TLSClientConfig.CAData) > 0 {
|
if len(cfg.TLSClientConfig.CAData) > 0 {
|
||||||
cfg.TLSClientConfig.CAData = append(cfg.TLSClientConfig.CAData, '\n')
|
cfg.TLSClientConfig.CAData = append(cfg.TLSClientConfig.CAData, '\n')
|
||||||
}
|
}
|
||||||
cfg.TLSClientConfig.CAData = append(cfg.TLSClientConfig.CAData, h.ClientConfig.CABundle...)
|
cfg.TLSClientConfig.CAData = append(cfg.TLSClientConfig.CAData, cc.CABundle...)
|
||||||
|
|
||||||
cfg.ContentConfig.NegotiatedSerializer = cm.negotiatedSerializer
|
cfg.ContentConfig.NegotiatedSerializer = cm.negotiatedSerializer
|
||||||
cfg.ContentConfig.ContentType = runtime.ContentTypeJSON
|
cfg.ContentConfig.ContentType = runtime.ContentTypeJSON
|
||||||
|
@ -131,18 +142,16 @@ func (cm *ClientManager) HookClient(h *v1beta1.Webhook) (*rest.RESTClient, error
|
||||||
return client, err
|
return client, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if svc := h.ClientConfig.Service; svc != nil {
|
if cc.Service != nil {
|
||||||
restConfig, err := cm.authInfoResolver.ClientConfigForService(svc.Name, svc.Namespace)
|
restConfig, err := cm.authInfoResolver.ClientConfigForService(cc.Service.Name, cc.Service.Namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cfg := rest.CopyConfig(restConfig)
|
cfg := rest.CopyConfig(restConfig)
|
||||||
serverName := svc.Name + "." + svc.Namespace + ".svc"
|
serverName := cc.Service.Name + "." + cc.Service.Namespace + ".svc"
|
||||||
host := serverName + ":443"
|
host := serverName + ":443"
|
||||||
cfg.Host = "https://" + host
|
cfg.Host = "https://" + host
|
||||||
if svc.Path != nil {
|
cfg.APIPath = cc.Service.Path
|
||||||
cfg.APIPath = *svc.Path
|
|
||||||
}
|
|
||||||
// Set the server name if not already set
|
// Set the server name if not already set
|
||||||
if len(cfg.TLSClientConfig.ServerName) == 0 {
|
if len(cfg.TLSClientConfig.ServerName) == 0 {
|
||||||
cfg.TLSClientConfig.ServerName = serverName
|
cfg.TLSClientConfig.ServerName = serverName
|
||||||
|
@ -155,7 +164,7 @@ func (cm *ClientManager) HookClient(h *v1beta1.Webhook) (*rest.RESTClient, error
|
||||||
}
|
}
|
||||||
cfg.Dial = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
cfg.Dial = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
if addr == host {
|
if addr == host {
|
||||||
u, err := cm.serviceResolver.ResolveEndpoint(svc.Namespace, svc.Name)
|
u, err := cm.serviceResolver.ResolveEndpoint(cc.Service.Namespace, cc.Service.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -167,13 +176,13 @@ func (cm *ClientManager) HookClient(h *v1beta1.Webhook) (*rest.RESTClient, error
|
||||||
return complete(cfg)
|
return complete(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.ClientConfig.URL == nil {
|
if cc.URL == "" {
|
||||||
return nil, &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: ErrNeedServiceOrURL}
|
return nil, &ErrCallingWebhook{WebhookName: cc.Name, Reason: errors.New("webhook configuration must have either service or URL")}
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := url.Parse(*h.ClientConfig.URL)
|
u, err := url.Parse(cc.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &webhookerrors.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Unparsable URL: %v", err)}
|
return nil, &ErrCallingWebhook{WebhookName: cc.Name, Reason: fmt.Errorf("Unparsable URL: %v", err)}
|
||||||
}
|
}
|
||||||
|
|
||||||
restConfig, err := cm.authInfoResolver.ClientConfigFor(u.Host)
|
restConfig, err := cm.authInfoResolver.ClientConfigFor(u.Host)
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package errors
|
package webhook
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package config
|
package webhook
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -29,6 +29,7 @@ type ServiceResolver interface {
|
||||||
|
|
||||||
type defaultServiceResolver struct{}
|
type defaultServiceResolver struct{}
|
||||||
|
|
||||||
|
// NewDefaultServiceResolver creates a new default server resolver.
|
||||||
func NewDefaultServiceResolver() ServiceResolver {
|
func NewDefaultServiceResolver() ServiceResolver {
|
||||||
return &defaultServiceResolver{}
|
return &defaultServiceResolver{}
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package config
|
package webhook
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
Loading…
Reference in New Issue