diff --git a/Gopkg.lock b/Gopkg.lock index 7a856203927c..921a02e5cffa 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -777,7 +777,7 @@ version = "v2.2.2" [[projects]] - digest = "1:3ca4baec1a14e727951288f091badc5d403096d1c00d3ff5299f273a0c6db85d" + digest = "1:684a27f12acc943bee24373b4e8f4acde803990add2fc5dde602c27b58207d3b" name = "k8s.io/api" packages = [ "admission/v1beta1", @@ -815,8 +815,8 @@ "storage/v1beta1", ] pruneopts = "UT" - revision = "05914d821849570fba9eacfb29466f2d8d3cd229" - version = "kubernetes-1.13.1" + revision = "5cb15d34447165a97c76ed5a60e4e99c8a01ecfe" + version = "kubernetes-1.13.4" [[projects]] digest = "1:db0e48e58e92eddc9ae08e9efb3dfaa1d61e32de690fff0dae5bccf4b45ebcce" @@ -829,11 +829,11 @@ "pkg/client/clientset/clientset/typed/apiextensions/v1beta1", ] pruneopts = "UT" - revision = "0fe22c71c47604641d9aa352c785b7912c200562" - version = "kubernetes-1.13.1" + revision = "d002e88f6236312f0289d9d1deab106751718ff0" + version = "kubernetes-1.13.4" [[projects]] - digest = "1:756538c9b8fe0a9d82f3fe04bbdefb7486045a4939f0649ebdd3fcbdb27314d2" + digest = "1:ae4ffd63d09b73d49de165dbafa48079dd1ff7bcc9689954f4fe21860ae5a5b6" name = "k8s.io/apimachinery" packages = [ "pkg/api/equality", @@ -884,8 +884,8 @@ "third_party/forked/golang/reflect", ] pruneopts = "UT" - revision = "2b1284ed4c93a43499e781493253e2ac5959c4fd" - version = "kubernetes-1.13.1" + revision = "86fb29eff6288413d76bd8506874fddd9fccdff0" + version = "kubernetes-1.13.4" [[projects]] branch = "master" @@ -896,7 +896,7 @@ revision = "b6aa1175dafa586b8042c7bfdcd1585f9ecfaa08" [[projects]] - digest = "1:9e2617b6420a04f67a7b63938a238f43dd401f90a6c4d0f1f2470cd066e38928" + digest = "1:63958a94e210ef7f0e8ba68643e93cf5af66dd0723e60f0604de81fc347d833b" name = "k8s.io/client-go" packages = [ "discovery", @@ -1049,8 +1049,8 @@ "util/workqueue", ] pruneopts = "UT" - revision = "8d9ed539ba3134352c586810e749e58df4e94e4f" - version = "kubernetes-1.13.1" + revision = "b40b2a5939e43f7ffe0028ad67586b7ce50bb675" + version = "kubernetes-1.13.4" [[projects]] branch = "master" @@ -1120,7 +1120,7 @@ revision = "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" [[projects]] - digest = "1:723a0b3f18d8042f63570d2478cda739bc547d4c94ba787786ce5bb6b6ccb603" + digest = "1:e06b8ce47dfcd74cf6c677eb98a10685429d9acf4f52542568f11500bd813640" name = "sigs.k8s.io/controller-runtime" packages = [ "pkg/cache", @@ -1151,22 +1151,17 @@ "pkg/runtime/signals", "pkg/source", "pkg/source/internal", - "pkg/webhook", "pkg/webhook/admission", "pkg/webhook/admission/types", - "pkg/webhook/internal/cert", - "pkg/webhook/internal/cert/generator", - "pkg/webhook/internal/cert/writer", - "pkg/webhook/internal/cert/writer/atomic", "pkg/webhook/internal/metrics", "pkg/webhook/types", ] pruneopts = "UT" - revision = "477bf4f046c31c351b46fa00262bc814ac0bbca1" - version = "v0.1.11" + revision = "f1eaba5087d69cebb154c6a48193e6667f5b512c" + version = "v0.1.12" [[projects]] - digest = "1:992675a6714d511089a0b7ffb7063d36e5423089cda610642de7a0cfbbf673ab" + digest = "1:77a19ea61ca4e01817ad2bc3e91689c5097b4b439668127d1fb5d8b95c3aca03" name = "sigs.k8s.io/controller-tools" packages = [ "cmd/controller-gen", @@ -1178,11 +1173,10 @@ "pkg/rbac", "pkg/util", "pkg/webhook", - "pkg/webhook/internal", ] pruneopts = "UT" - revision = "fbf141159251d035089e7acdd5a343f8cec91b94" - version = "v0.1.9" + revision = "b8adde9bc6d7f3fba449d306613a9daed23676c8" + version = "v0.1.10" [[projects]] digest = "1:9070222ca967d09b3966552a161dd4420d62315964bf5e1efd8cc4c7c30ebca8" diff --git a/Gopkg.toml b/Gopkg.toml index f2be59abd2f3..1316ef39ae3b 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -24,11 +24,11 @@ required = [ [[constraint]] name="sigs.k8s.io/controller-runtime" - version="v0.1.11" + version="v0.1.12" [[constraint]] name="sigs.k8s.io/controller-tools" - version="v0.1.9" + version="v0.1.10" # For dependency below: Refer to issue https://github.com/golang/dep/issues/1799 [[override]] diff --git a/pkg/controller/machinedeployment/BUILD.bazel b/pkg/controller/machinedeployment/BUILD.bazel index 4f2a0522a690..8e89fc025405 100644 --- a/pkg/controller/machinedeployment/BUILD.bazel +++ b/pkg/controller/machinedeployment/BUILD.bazel @@ -56,6 +56,7 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", "//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", + "//vendor/k8s.io/client-go/tools/record:go_default_library", "//vendor/sigs.k8s.io/controller-runtime/pkg/client:go_default_library", "//vendor/sigs.k8s.io/controller-runtime/pkg/client/fake:go_default_library", "//vendor/sigs.k8s.io/controller-runtime/pkg/envtest:go_default_library", diff --git a/vendor/k8s.io/api/core/v1/generated.proto b/vendor/k8s.io/api/core/v1/generated.proto index 165aa2a2450e..b13a2db72f58 100644 --- a/vendor/k8s.io/api/core/v1/generated.proto +++ b/vendor/k8s.io/api/core/v1/generated.proto @@ -3156,6 +3156,7 @@ message PodSpec { // EnableServiceLinks indicates whether information about services should be injected into pod's // environment variables, matching the syntax of Docker links. + // Optional: Defaults to true. // +optional optional bool enableServiceLinks = 30; } diff --git a/vendor/k8s.io/api/core/v1/types.go b/vendor/k8s.io/api/core/v1/types.go index 43e70b9bad57..87f3f0c5babc 100644 --- a/vendor/k8s.io/api/core/v1/types.go +++ b/vendor/k8s.io/api/core/v1/types.go @@ -2920,6 +2920,7 @@ type PodSpec struct { RuntimeClassName *string `json:"runtimeClassName,omitempty" protobuf:"bytes,29,opt,name=runtimeClassName"` // EnableServiceLinks indicates whether information about services should be injected into pod's // environment variables, matching the syntax of Docker links. + // Optional: Defaults to true. // +optional EnableServiceLinks *bool `json:"enableServiceLinks,omitempty" protobuf:"varint,30,opt,name=enableServiceLinks"` } diff --git a/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go b/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go index 71f90685f35f..13ea6d226518 100644 --- a/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go +++ b/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go @@ -1540,7 +1540,7 @@ var map_PodSpec = map[string]string{ "dnsConfig": "Specifies the DNS parameters of a pod. Parameters specified here will be merged to the generated DNS configuration based on DNSPolicy.", "readinessGates": "If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://github.com/kubernetes/community/blob/master/keps/sig-network/0007-pod-ready%2B%2B.md", "runtimeClassName": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://github.com/kubernetes/community/blob/master/keps/sig-node/0014-runtime-class.md This is an alpha feature and may change in the future.", - "enableServiceLinks": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links.", + "enableServiceLinks": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.", } func (PodSpec) SwaggerDoc() map[string]string { diff --git a/vendor/k8s.io/apimachinery/pkg/api/errors/errors.go b/vendor/k8s.io/apimachinery/pkg/api/errors/errors.go index e736a9861400..48c1104d99fa 100644 --- a/vendor/k8s.io/apimachinery/pkg/api/errors/errors.go +++ b/vendor/k8s.io/apimachinery/pkg/api/errors/errors.go @@ -341,6 +341,17 @@ func NewTooManyRequestsError(message string) *StatusError { }} } +// NewRequestEntityTooLargeError returns an error indicating that the request +// entity was too large. +func NewRequestEntityTooLargeError(message string) *StatusError { + return &StatusError{metav1.Status{ + Status: metav1.StatusFailure, + Code: http.StatusRequestEntityTooLarge, + Reason: metav1.StatusReasonRequestEntityTooLarge, + Message: fmt.Sprintf("Request entity too large: %s", message), + }} +} + // NewGenericServerResponse returns a new error for server responses that are not in a recognizable form. func NewGenericServerResponse(code int, verb string, qualifiedResource schema.GroupResource, name, serverMessage string, retryAfterSeconds int, isUnexpectedResponse bool) *StatusError { reason := metav1.StatusReasonUnknown @@ -527,6 +538,19 @@ func IsTooManyRequests(err error) bool { return false } +// IsRequestEntityTooLargeError determines if err is an error which indicates +// the request entity is too large. +func IsRequestEntityTooLargeError(err error) bool { + if ReasonForError(err) == metav1.StatusReasonRequestEntityTooLarge { + return true + } + switch t := err.(type) { + case APIStatus: + return t.Status().Code == http.StatusRequestEntityTooLarge + } + return false +} + // IsUnexpectedServerError returns true if the server response was not in the expected API format, // and may be the result of another HTTP actor. func IsUnexpectedServerError(err error) bool { diff --git a/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go b/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go index 8f488ba7ea4d..65f87546d20e 100644 --- a/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go +++ b/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go @@ -713,6 +713,10 @@ const ( // Status code 406 StatusReasonNotAcceptable StatusReason = "NotAcceptable" + // StatusReasonRequestEntityTooLarge means that the request entity is too large. + // Status code 413 + StatusReasonRequestEntityTooLarge StatusReason = "RequestEntityTooLarge" + // StatusReasonUnsupportedMediaType means that the content type sent by the client is not acceptable // to the server - for instance, attempting to send protobuf for a resource that supports only json and yaml. // API calls that return UnsupportedMediaType can never succeed. diff --git a/vendor/k8s.io/apimachinery/pkg/runtime/codec.go b/vendor/k8s.io/apimachinery/pkg/runtime/codec.go index 6b859b288978..284e32bc3cb8 100644 --- a/vendor/k8s.io/apimachinery/pkg/runtime/codec.go +++ b/vendor/k8s.io/apimachinery/pkg/runtime/codec.go @@ -283,6 +283,7 @@ var _ GroupVersioner = multiGroupVersioner{} type multiGroupVersioner struct { target schema.GroupVersion acceptedGroupKinds []schema.GroupKind + coerce bool } // NewMultiGroupVersioner returns the provided group version for any kind that matches one of the provided group kinds. @@ -294,6 +295,22 @@ func NewMultiGroupVersioner(gv schema.GroupVersion, groupKinds ...schema.GroupKi return multiGroupVersioner{target: gv, acceptedGroupKinds: groupKinds} } +// NewCoercingMultiGroupVersioner returns the provided group version for any incoming kind. +// Incoming kinds that match the provided groupKinds are preferred. +// Kind may be empty in the provided group kind, in which case any kind will match. +// Examples: +// gv=mygroup/__internal, groupKinds=mygroup/Foo, anothergroup/Bar +// KindForGroupVersionKinds(yetanother/v1/Baz, anothergroup/v1/Bar) -> mygroup/__internal/Bar (matched preferred group/kind) +// +// gv=mygroup/__internal, groupKinds=mygroup, anothergroup +// KindForGroupVersionKinds(yetanother/v1/Baz, anothergroup/v1/Bar) -> mygroup/__internal/Bar (matched preferred group) +// +// gv=mygroup/__internal, groupKinds=mygroup, anothergroup +// KindForGroupVersionKinds(yetanother/v1/Baz, yetanother/v1/Bar) -> mygroup/__internal/Baz (no preferred group/kind match, uses first kind in list) +func NewCoercingMultiGroupVersioner(gv schema.GroupVersion, groupKinds ...schema.GroupKind) GroupVersioner { + return multiGroupVersioner{target: gv, acceptedGroupKinds: groupKinds, coerce: true} +} + // KindForGroupVersionKinds returns the target group version if any kind matches any of the original group kinds. It will // use the originating kind where possible. func (v multiGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) { @@ -308,5 +325,8 @@ func (v multiGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersio return v.target.WithKind(src.Kind), true } } + if v.coerce && len(kinds) > 0 { + return v.target.WithKind(kinds[0].Kind), true + } return schema.GroupVersionKind{}, false } diff --git a/vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming/streaming.go b/vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming/streaming.go index 91fd4ed4f0b3..a60a7c04156b 100644 --- a/vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming/streaming.go +++ b/vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming/streaming.go @@ -64,7 +64,7 @@ func NewDecoder(r io.ReadCloser, d runtime.Decoder) Decoder { reader: r, decoder: d, buf: make([]byte, 1024), - maxBytes: 1024 * 1024, + maxBytes: 16 * 1024 * 1024, } } diff --git a/vendor/k8s.io/client-go/transport/token_source.go b/vendor/k8s.io/client-go/transport/token_source.go index 818baffd4e6b..8595df271694 100644 --- a/vendor/k8s.io/client-go/transport/token_source.go +++ b/vendor/k8s.io/client-go/transport/token_source.go @@ -47,14 +47,14 @@ func TokenSourceWrapTransport(ts oauth2.TokenSource) func(http.RoundTripper) htt func NewCachedFileTokenSource(path string) oauth2.TokenSource { return &cachingTokenSource{ now: time.Now, - leeway: 1 * time.Minute, + leeway: 10 * time.Second, base: &fileTokenSource{ path: path, - // This period was picked because it is half of the minimum validity - // duration for a token provisioned by they TokenRequest API. This is - // unsophisticated and should induce rotation at a frequency that should - // work with the token volume source. - period: 5 * time.Minute, + // This period was picked because it is half of the duration between when the kubelet + // refreshes a projected service account token and when the original token expires. + // Default token lifetime is 10 minutes, and the kubelet starts refreshing at 80% of lifetime. + // This should induce re-reading at a frequency that works with the token volume source. + period: time.Minute, }, } } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/BUILD.bazel b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/BUILD.bazel deleted file mode 100644 index 75356c88608a..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/BUILD.bazel +++ /dev/null @@ -1,34 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = [ - "bootstrap.go", - "doc.go", - "server.go", - "util.go", - ], - importmap = "sigs.k8s.io/cluster-api/vendor/sigs.k8s.io/controller-runtime/pkg/webhook", - importpath = "sigs.k8s.io/controller-runtime/pkg/webhook", - visibility = ["//visibility:public"], - deps = [ - "//vendor/k8s.io/api/admissionregistration/v1beta1:go_default_library", - "//vendor/k8s.io/api/core/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/client:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/client/config:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/manager:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/runtime/log:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/types:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/webhook/types:go_default_library", - ], -) diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/bootstrap.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/bootstrap.go deleted file mode 100644 index c14f8dafff30..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/bootstrap.go +++ /dev/null @@ -1,356 +0,0 @@ -/* -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 webhook - -import ( - "errors" - "fmt" - "net" - "net/http" - "net/url" - "path" - "sort" - "strconv" - - "k8s.io/api/admissionregistration/v1beta1" - admissionregistration "k8s.io/api/admissionregistration/v1beta1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/intstr" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/config" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - "sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert" - "sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer" - "sigs.k8s.io/controller-runtime/pkg/webhook/types" -) - -// setDefault does defaulting for the Server. -func (s *Server) setDefault() { - s.setServerDefault() - s.setBootstrappingDefault() -} - -// setServerDefault does defaulting for the ServerOptions. -func (s *Server) setServerDefault() { - if len(s.Name) == 0 { - s.Name = "default-k8s-webhook-server" - } - if s.registry == nil { - s.registry = map[string]Webhook{} - } - if s.sMux == nil { - s.sMux = http.DefaultServeMux - } - if s.Port <= 0 { - s.Port = 443 - } - if len(s.CertDir) == 0 { - s.CertDir = path.Join("k8s-webhook-server", "cert") - } - if s.DisableWebhookConfigInstaller == nil { - diwc := false - s.DisableWebhookConfigInstaller = &diwc - } - - if s.Client == nil { - cfg, err := config.GetConfig() - if err != nil { - s.err = err - return - } - s.Client, err = client.New(cfg, client.Options{}) - if err != nil { - s.err = err - return - } - } -} - -// setBootstrappingDefault does defaulting for the Server bootstrapping. -func (s *Server) setBootstrappingDefault() { - if s.BootstrapOptions == nil { - s.BootstrapOptions = &BootstrapOptions{} - } - if len(s.MutatingWebhookConfigName) == 0 { - s.MutatingWebhookConfigName = "mutating-webhook-configuration" - } - if len(s.ValidatingWebhookConfigName) == 0 { - s.ValidatingWebhookConfigName = "validating-webhook-configuration" - } - if s.Host == nil && s.Service == nil { - varString := "localhost" - s.Host = &varString - } - - var certWriter writer.CertWriter - var err error - if s.Secret != nil { - certWriter, err = writer.NewSecretCertWriter( - writer.SecretCertWriterOptions{ - Secret: s.Secret, - Client: s.Client, - }) - } else { - certWriter, err = writer.NewFSCertWriter( - writer.FSCertWriterOptions{ - Path: s.CertDir, - }) - } - if err != nil { - s.err = err - return - } - s.certProvisioner = &cert.Provisioner{ - CertWriter: certWriter, - } -} - -// InstallWebhookManifests creates the admissionWebhookConfiguration objects and service if any. -// It also provisions the certificate for the admission server. -func (s *Server) InstallWebhookManifests() error { - // do defaulting if necessary - s.once.Do(s.setDefault) - if s.err != nil { - return s.err - } - - var err error - s.webhookConfigurations, err = s.whConfigs() - if err != nil { - return err - } - svc := s.service() - objects := append(s.webhookConfigurations, svc) - - cc, err := s.getClientConfig() - if err != nil { - return err - } - // Provision the cert by creating new one or refreshing existing one. - _, err = s.certProvisioner.Provision(cert.Options{ - ClientConfig: cc, - Objects: s.webhookConfigurations, - }) - if err != nil { - return err - } - - return batchCreateOrReplace(s.Client, objects...) -} - -func (s *Server) getClientConfig() (*admissionregistration.WebhookClientConfig, error) { - if s.Host != nil && s.Service != nil { - return nil, errors.New("URL and Service can't be set at the same time") - } - cc := &admissionregistration.WebhookClientConfig{ - CABundle: []byte{}, - } - if s.Host != nil { - u := url.URL{ - Scheme: "https", - Host: net.JoinHostPort(*s.Host, strconv.Itoa(int(s.Port))), - } - urlString := u.String() - cc.URL = &urlString - } - if s.Service != nil { - cc.Service = &admissionregistration.ServiceReference{ - Name: s.Service.Name, - Namespace: s.Service.Namespace, - // Path will be set later - } - } - return cc, nil -} - -// getClientConfigWithPath constructs a WebhookClientConfig based on the server options. -// It will use path to the set the path in WebhookClientConfig. -func (s *Server) getClientConfigWithPath(path string) (*admissionregistration.WebhookClientConfig, error) { - cc, err := s.getClientConfig() - if err != nil { - return nil, err - } - return cc, setPath(cc, path) -} - -// setPath sets the path in the WebhookClientConfig. -func setPath(cc *admissionregistration.WebhookClientConfig, path string) error { - if cc.URL != nil { - u, err := url.Parse(*cc.URL) - if err != nil { - return err - } - u.Path = path - urlString := u.String() - cc.URL = &urlString - } - if cc.Service != nil { - cc.Service.Path = &path - } - return nil -} - -// whConfigs creates a mutatingWebhookConfiguration and(or) a validatingWebhookConfiguration based on registry. -// For the same type of webhook configuration, it generates a webhook entry per endpoint. -func (s *Server) whConfigs() ([]runtime.Object, error) { - objs := []runtime.Object{} - mutatingWH, err := s.mutatingWHConfigs() - if err != nil { - return nil, err - } - if mutatingWH != nil { - objs = append(objs, mutatingWH) - } - validatingWH, err := s.validatingWHConfigs() - if err != nil { - return nil, err - } - if validatingWH != nil { - objs = append(objs, validatingWH) - } - return objs, nil -} - -func (s *Server) mutatingWHConfigs() (runtime.Object, error) { - mutatingWebhooks := []v1beta1.Webhook{} - for path, webhook := range s.registry { - if webhook.GetType() != types.WebhookTypeMutating { - continue - } - - admissionWebhook := webhook.(*admission.Webhook) - wh, err := s.admissionWebhook(path, admissionWebhook) - if err != nil { - return nil, err - } - mutatingWebhooks = append(mutatingWebhooks, *wh) - } - - sort.Slice(mutatingWebhooks, func(i, j int) bool { - return mutatingWebhooks[i].Name < mutatingWebhooks[j].Name - }) - - if len(mutatingWebhooks) > 0 { - return &admissionregistration.MutatingWebhookConfiguration{ - TypeMeta: metav1.TypeMeta{ - APIVersion: fmt.Sprintf("%s/%s", admissionregistration.GroupName, "v1beta1"), - Kind: "MutatingWebhookConfiguration", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: s.MutatingWebhookConfigName, - }, - Webhooks: mutatingWebhooks, - }, nil - } - return nil, nil -} - -func (s *Server) validatingWHConfigs() (runtime.Object, error) { - validatingWebhooks := []v1beta1.Webhook{} - for path, webhook := range s.registry { - var admissionWebhook *admission.Webhook - if webhook.GetType() != types.WebhookTypeValidating { - continue - } - - admissionWebhook = webhook.(*admission.Webhook) - wh, err := s.admissionWebhook(path, admissionWebhook) - if err != nil { - return nil, err - } - validatingWebhooks = append(validatingWebhooks, *wh) - } - - sort.Slice(validatingWebhooks, func(i, j int) bool { - return validatingWebhooks[i].Name < validatingWebhooks[j].Name - }) - - if len(validatingWebhooks) > 0 { - return &admissionregistration.ValidatingWebhookConfiguration{ - TypeMeta: metav1.TypeMeta{ - APIVersion: fmt.Sprintf("%s/%s", admissionregistration.GroupName, "v1beta1"), - Kind: "ValidatingWebhookConfiguration", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: s.ValidatingWebhookConfigName, - }, - Webhooks: validatingWebhooks, - }, nil - } - return nil, nil -} - -func (s *Server) admissionWebhook(path string, wh *admission.Webhook) (*admissionregistration.Webhook, error) { - if wh.NamespaceSelector == nil && s.Service != nil && len(s.Service.Namespace) > 0 { - wh.NamespaceSelector = &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "control-plane", - Operator: metav1.LabelSelectorOpDoesNotExist, - }, - }, - } - } - - webhook := &admissionregistration.Webhook{ - Name: wh.GetName(), - Rules: wh.Rules, - FailurePolicy: wh.FailurePolicy, - NamespaceSelector: wh.NamespaceSelector, - ClientConfig: admissionregistration.WebhookClientConfig{ - // The reason why we assign an empty byte array to CABundle is that - // CABundle field will be updated by the Provisioner. - CABundle: []byte{}, - }, - } - cc, err := s.getClientConfigWithPath(path) - if err != nil { - return nil, err - } - webhook.ClientConfig = *cc - return webhook, nil -} - -// service creates a corev1.service object fronting the admission server. -func (s *Server) service() runtime.Object { - if s.Service == nil { - return nil - } - svc := &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: s.Service.Name, - Namespace: s.Service.Namespace, - }, - Spec: corev1.ServiceSpec{ - Selector: s.Service.Selectors, - Ports: []corev1.ServicePort{ - { - // When using service, kube-apiserver will send admission request to port 443. - Port: 443, - TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: s.Port}, - }, - }, - }, - } - return svc -} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/doc.go deleted file mode 100644 index 2c9eba2a3d58..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/doc.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -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 webhook provides methods to build and bootstrap a webhook server. - -Currently, it only supports admission webhooks. It will support CRD conversion webhooks in the near future. - -Build webhooks - - // mgr is the manager that runs the server. - webhook1, err := NewWebhookBuilder(). - Name("foo.k8s.io"). - Mutating(). - Path("/mutating-pods"). - Operations(admissionregistrationv1beta1.Create). - ForType(&corev1.Pod{}). - WithManager(mgr). - Handlers(mutatingHandler1, mutatingHandler2). - Build() - if err != nil { - // handle error - } - - webhook2, err := NewWebhookBuilder(). - Name("bar.k8s.io"). - Validating(). - Path("/validating-deployment"). - Operations(admissionregistrationv1beta1.Create, admissionregistrationv1beta1.Update). - ForType(&appsv1.Deployment{}). - WithManager(mgr). - Handlers(validatingHandler1). - Build() - if err != nil { - // handle error - } - -Create a webhook server. - - as, err := NewServer("baz-admission-server", mgr, ServerOptions{ - CertDir: "/tmp/cert", - BootstrapOptions: &BootstrapOptions{ - Secret: &apitypes.NamespacedName{ - Namespace: "default", - Name: "foo-admission-server-secret", - }, - Service: &Service{ - Namespace: "default", - Name: "foo-admission-server-service", - // Selectors should select the pods that runs this webhook server. - Selectors: map[string]string{ - "app": "foo-admission-server", - }, - }, - }, - }) - if err != nil { - // handle error - } - -Register the webhooks in the server. - - err = as.Register(webhook1, webhook2) - if err != nil { - // handle error - } - -Start the server by starting the manager - - err := mrg.Start(signals.SetupSignalHandler()) - if err != nil { - // handle error - } -*/ -package webhook - -import ( - logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" -) - -var log = logf.KBLog.WithName("webhook") diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/BUILD.bazel b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/BUILD.bazel deleted file mode 100644 index 91feb98435cb..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = [ - "doc.go", - "provisioner.go", - ], - importmap = "sigs.k8s.io/cluster-api/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert", - importpath = "sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert", - visibility = ["//vendor/sigs.k8s.io/controller-runtime/pkg/webhook:__subpackages__"], - deps = [ - "//vendor/k8s.io/api/admissionregistration/v1beta1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer:go_default_library", - ], -) diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/doc.go deleted file mode 100644 index 5929246f0246..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/doc.go +++ /dev/null @@ -1,36 +0,0 @@ -/* -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 cert provides functions to manage certificates for webhookClientConfiguration. - -Create a Provisioner with a CertWriter. - - provisioner := Provisioner{ - CertWriter: admission.NewSecretCertWriter(admission.SecretCertWriterOptions{...}), - } - -Provision the certificates for the webhookClientConfig - - err := provisioner.Provision(Options{ - ClientConfig: webhookClientConfig, - Objects: []runtime.Object{mutatingWebhookConfiguration, validatingWebhookConfiguration} - }) - if err != nil { - // handle error - } -*/ -package cert diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator/BUILD.bazel b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator/BUILD.bazel deleted file mode 100644 index df636e8bc3be..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator/BUILD.bazel +++ /dev/null @@ -1,15 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = [ - "certgenerator.go", - "doc.go", - "selfsigned.go", - "util.go", - ], - importmap = "sigs.k8s.io/cluster-api/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator", - importpath = "sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator", - visibility = ["//vendor/sigs.k8s.io/controller-runtime/pkg/webhook:__subpackages__"], - deps = ["//vendor/k8s.io/client-go/util/cert:go_default_library"], -) diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator/certgenerator.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator/certgenerator.go deleted file mode 100644 index 633eefc73c65..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator/certgenerator.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -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 generator - -// Artifacts hosts a private key, its corresponding serving certificate and -// the CA certificate that signs the serving certificate. -type Artifacts struct { - // PEM encoded private key - Key []byte - // PEM encoded serving certificate - Cert []byte - // PEM encoded CA private key - CAKey []byte - // PEM encoded CA certificate - CACert []byte -} - -// CertGenerator is an interface to provision the serving certificate. -type CertGenerator interface { - // Generate returns a Artifacts struct. - Generate(CommonName string) (*Artifacts, error) - // SetCA sets the PEM-encoded CA private key and CA cert for signing the generated serving cert. - SetCA(caKey, caCert []byte) -} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator/doc.go deleted file mode 100644 index 9d814e4289f0..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator/doc.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -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 generator provides an interface and implementation to provision certificates. - -Create an instance of certGenerator. - - cg := SelfSignedCertGenerator{} - -Generate the certificates. - certs, err := cg.Generate("foo.bar.com") - if err != nil { - // handle error - } -*/ -package generator diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator/selfsigned.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator/selfsigned.go deleted file mode 100644 index 733b674ef736..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator/selfsigned.go +++ /dev/null @@ -1,117 +0,0 @@ -/* -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 generator - -import ( - "crypto/rsa" - "crypto/x509" - "fmt" - "time" - - "k8s.io/client-go/util/cert" -) - -// ServiceToCommonName generates the CommonName for the certificate when using a k8s service. -func ServiceToCommonName(serviceNamespace, serviceName string) string { - return fmt.Sprintf("%s.%s.svc", serviceName, serviceNamespace) -} - -// SelfSignedCertGenerator implements the certGenerator interface. -// It provisions self-signed certificates. -type SelfSignedCertGenerator struct { - caKey []byte - caCert []byte -} - -var _ CertGenerator = &SelfSignedCertGenerator{} - -// SetCA sets the PEM-encoded CA private key and CA cert for signing the generated serving cert. -func (cp *SelfSignedCertGenerator) SetCA(caKey, caCert []byte) { - cp.caKey = caKey - cp.caCert = caCert -} - -// Generate creates and returns a CA certificate, certificate and -// key for the server. serverKey and serverCert are used by the server -// to establish trust for clients, CA certificate is used by the -// client to verify the server authentication chain. -// The cert will be valid for 365 days. -func (cp *SelfSignedCertGenerator) Generate(commonName string) (*Artifacts, error) { - var signingKey *rsa.PrivateKey - var signingCert *x509.Certificate - var valid bool - var err error - - valid, signingKey, signingCert = cp.validCACert() - if !valid { - signingKey, err = cert.NewPrivateKey() - if err != nil { - return nil, fmt.Errorf("failed to create the CA private key: %v", err) - } - signingCert, err = cert.NewSelfSignedCACert(cert.Config{CommonName: "webhook-cert-ca"}, signingKey) - if err != nil { - return nil, fmt.Errorf("failed to create the CA cert: %v", err) - } - } - - key, err := cert.NewPrivateKey() - if err != nil { - return nil, fmt.Errorf("failed to create the private key: %v", err) - } - signedCert, err := cert.NewSignedCert( - cert.Config{ - CommonName: commonName, - Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - }, - key, signingCert, signingKey, - ) - if err != nil { - return nil, fmt.Errorf("failed to create the cert: %v", err) - } - return &Artifacts{ - Key: cert.EncodePrivateKeyPEM(key), - Cert: cert.EncodeCertPEM(signedCert), - CAKey: cert.EncodePrivateKeyPEM(signingKey), - CACert: cert.EncodeCertPEM(signingCert), - }, nil -} - -func (cp *SelfSignedCertGenerator) validCACert() (bool, *rsa.PrivateKey, *x509.Certificate) { - if !ValidCACert(cp.caKey, cp.caCert, cp.caCert, "", - time.Now().AddDate(1, 0, 0)) { - return false, nil, nil - } - - var ok bool - key, err := cert.ParsePrivateKeyPEM(cp.caKey) - if err != nil { - return false, nil, nil - } - privateKey, ok := key.(*rsa.PrivateKey) - if !ok { - return false, nil, nil - } - - certs, err := cert.ParseCertsPEM(cp.caCert) - if err != nil { - return false, nil, nil - } - if len(certs) != 1 { - return false, nil, nil - } - return true, privateKey, certs[0] -} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator/util.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator/util.go deleted file mode 100644 index fd459cf05b99..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator/util.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -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 generator - -import ( - "crypto/tls" - "crypto/x509" - "encoding/pem" - "time" -) - -// ValidCACert think cert and key are valid if they meet the following requirements: -// - key and cert are valid pair -// - caCert is the root ca of cert -// - cert is for dnsName -// - cert won't expire before time -func ValidCACert(key, cert, caCert []byte, dnsName string, time time.Time) bool { - if len(key) == 0 || len(cert) == 0 || len(caCert) == 0 { - return false - } - // Verify key and cert are valid pair - _, err := tls.X509KeyPair(cert, key) - if err != nil { - return false - } - - // Verify cert is valid for at least 1 year. - pool := x509.NewCertPool() - if !pool.AppendCertsFromPEM(caCert) { - return false - } - block, _ := pem.Decode([]byte(cert)) - if block == nil { - return false - } - c, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return false - } - ops := x509.VerifyOptions{ - DNSName: dnsName, - Roots: pool, - CurrentTime: time, - } - _, err = c.Verify(ops) - return err == nil -} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/provisioner.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/provisioner.go deleted file mode 100644 index 992e9da93998..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/provisioner.go +++ /dev/null @@ -1,133 +0,0 @@ -/* -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 cert - -import ( - "bytes" - "errors" - "fmt" - "net" - "net/url" - - admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator" - "sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer" -) - -// Provisioner provisions certificates for webhook configurations and writes them to an output -// destination - such as a Secret or local file. Provisioner can update the CA field of -// certain resources with the CA of the certs. -type Provisioner struct { - // CertWriter knows how to persist the certificate. - CertWriter writer.CertWriter -} - -// Options are options for provisioning the certificate. -type Options struct { - // ClientConfig is the WebhookClientCert that contains the information to generate - // the certificate. The CA Certificate will be updated in the ClientConfig. - // The updated ClientConfig will be used to inject into other runtime.Objects, - // e.g. MutatingWebhookConfiguration and ValidatingWebhookConfiguration. - ClientConfig *admissionregistrationv1beta1.WebhookClientConfig - // Objects are the objects that will use the ClientConfig above. - Objects []runtime.Object -} - -// Provision provisions certificates for the WebhookClientConfig. -// It ensures the cert and CA are valid and not expiring. -// It updates the CABundle in the webhookClientConfig if necessary. -// It inject the WebhookClientConfig into options.Objects. -func (cp *Provisioner) Provision(options Options) (bool, error) { - if cp.CertWriter == nil { - return false, errors.New("CertWriter need to be set") - } - - dnsName, err := dnsNameFromClientConfig(options.ClientConfig) - if err != nil { - return false, err - } - - certs, changed, err := cp.CertWriter.EnsureCert(dnsName) - if err != nil { - return false, err - } - - caBundle := options.ClientConfig.CABundle - caCert := certs.CACert - // TODO(mengqiy): limit the size of the CABundle by GC the old CA certificate - // this is important since the max record size in etcd is 1MB (latest version is 1.5MB). - if !bytes.Contains(caBundle, caCert) { - // Ensure the CA bundle in the webhook configuration has the signing CA. - options.ClientConfig.CABundle = append(caBundle, caCert...) - changed = true - } - return changed, cp.inject(options.ClientConfig, options.Objects) -} - -// Inject the ClientConfig to the objects. -// It supports MutatingWebhookConfiguration and ValidatingWebhookConfiguration. -func (cp *Provisioner) inject(cc *admissionregistrationv1beta1.WebhookClientConfig, objs []runtime.Object) error { - if cc == nil { - return nil - } - for i := range objs { - switch typed := objs[i].(type) { - case *admissionregistrationv1beta1.MutatingWebhookConfiguration: - injectForEachWebhook(cc, typed.Webhooks) - case *admissionregistrationv1beta1.ValidatingWebhookConfiguration: - injectForEachWebhook(cc, typed.Webhooks) - default: - return fmt.Errorf("%#v is not supported for injecting a webhookClientConfig", - objs[i].GetObjectKind().GroupVersionKind()) - } - } - return cp.CertWriter.Inject(objs...) -} - -func injectForEachWebhook( - cc *admissionregistrationv1beta1.WebhookClientConfig, - webhooks []admissionregistrationv1beta1.Webhook) { - for i := range webhooks { - // only replacing the CA bundle to preserve the path in the WebhookClientConfig - webhooks[i].ClientConfig.CABundle = cc.CABundle - } -} - -func dnsNameFromClientConfig(config *admissionregistrationv1beta1.WebhookClientConfig) (string, error) { - if config == nil { - return "", errors.New("clientConfig should not be empty") - } - if config.Service != nil && config.URL != nil { - return "", fmt.Errorf("service and URL can't be set at the same time in a webhook: %v", config) - } - if config.Service == nil && config.URL == nil { - return "", fmt.Errorf("one of service and URL need to be set in a webhook: %v", config) - } - if config.Service != nil { - return generator.ServiceToCommonName(config.Service.Namespace, config.Service.Name), nil - } - u, err := url.Parse(*config.URL) - if err != nil { - return "", err - } - host, _, err := net.SplitHostPort(u.Host) - if err != nil { - return u.Host, nil - } - return host, err -} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/BUILD.bazel b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/BUILD.bazel deleted file mode 100644 index d77bf4ab6afa..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/BUILD.bazel +++ /dev/null @@ -1,26 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = [ - "certwriter.go", - "doc.go", - "error.go", - "fs.go", - "secret.go", - ], - importmap = "sigs.k8s.io/cluster-api/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer", - importpath = "sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer", - visibility = ["//vendor/sigs.k8s.io/controller-runtime/pkg/webhook:__subpackages__"], - deps = [ - "//vendor/k8s.io/api/core/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/client:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/runtime/log:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/atomic:go_default_library", - ], -) diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/atomic/BUILD.bazel b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/atomic/BUILD.bazel deleted file mode 100644 index 7107da0b8535..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/atomic/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = ["atomic_writer.go"], - importmap = "sigs.k8s.io/cluster-api/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/atomic", - importpath = "sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/atomic", - visibility = ["//vendor/sigs.k8s.io/controller-runtime/pkg/webhook:__subpackages__"], - deps = [ - "//vendor/github.com/go-logr/logr:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", - ], -) diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/atomic/atomic_writer.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/atomic/atomic_writer.go deleted file mode 100644 index ec2665c3ee6e..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/atomic/atomic_writer.go +++ /dev/null @@ -1,453 +0,0 @@ -/* -Copyright 2016 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 atomic - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "path" - "path/filepath" - "runtime" - "strings" - "time" - - "github.com/go-logr/logr" - "k8s.io/apimachinery/pkg/util/sets" -) - -const ( - maxFileNameLength = 255 - maxPathLength = 4096 -) - -// AtomicWriter handles atomically projecting content for a set of files into -// a target directory. -// -// Note: -// -// 1. AtomicWriter reserves the set of pathnames starting with `..`. -// 2. AtomicWriter offers no concurrency guarantees and must be synchronized -// by the caller. -// -// The visible files in this volume are symlinks to files in the writer's data -// directory. Actual files are stored in a hidden timestamped directory which -// is symlinked to by the data directory. The timestamped directory and -// data directory symlink are created in the writer's target dir. This scheme -// allows the files to be atomically updated by changing the target of the -// data directory symlink. -// -// Consumers of the target directory can monitor the ..data symlink using -// inotify or fanotify to receive events when the content in the volume is -// updated. -type AtomicWriter struct { - targetDir string - log logr.Logger -} - -type FileProjection struct { - Data []byte - Mode int32 -} - -// NewAtomicWriter creates a new AtomicWriter configured to write to the given -// target directory, or returns an error if the target directory does not exist. -func NewAtomicWriter(targetDir string, log logr.Logger) (*AtomicWriter, error) { - _, err := os.Stat(targetDir) - if os.IsNotExist(err) { - return nil, err - } - - return &AtomicWriter{targetDir: targetDir, log: log}, nil -} - -const ( - dataDirName = "..data" - newDataDirName = "..data_tmp" -) - -// Write does an atomic projection of the given payload into the writer's target -// directory. Input paths must not begin with '..'. -// -// The Write algorithm is: -// -// 1. The payload is validated; if the payload is invalid, the function returns -// 2. The current timestamped directory is detected by reading the data directory -// symlink -// 3. The old version of the volume is walked to determine whether any -// portion of the payload was deleted and is still present on disk. -// 4. The data in the current timestamped directory is compared to the projected -// data to determine if an update is required. -// 5. A new timestamped dir is created -// 6. The payload is written to the new timestamped directory -// 7. Symlinks and directory for new user-visible files are created (if needed). -// -// For example, consider the files: -// <target-dir>/podName -// <target-dir>/user/labels -// <target-dir>/k8s/annotations -// -// The user visible files are symbolic links into the internal data directory: -// <target-dir>/podName -> ..data/podName -// <target-dir>/usr -> ..data/usr -// <target-dir>/k8s -> ..data/k8s -// -// The data directory itself is a link to a timestamped directory with -// the real data: -// <target-dir>/..data -> ..2016_02_01_15_04_05.12345678/ -// 8. A symlink to the new timestamped directory ..data_tmp is created that will -// become the new data directory -// 9. The new data directory symlink is renamed to the data directory; rename is atomic -// 10. Old paths are removed from the user-visible portion of the target directory -// 11. The previous timestamped directory is removed, if it exists -func (w *AtomicWriter) Write(payload map[string]FileProjection) error { - // (1) - cleanPayload, err := validatePayload(payload) - if err != nil { - w.log.Error(err, "invalid payload") - return err - } - - // (2) - dataDirPath := path.Join(w.targetDir, dataDirName) - oldTsDir, err := os.Readlink(dataDirPath) - if err != nil { - if !os.IsNotExist(err) { - w.log.Error(err, "unable to read link for data directory") - return err - } - // although Readlink() returns "" on err, don't be fragile by relying on it (since it's not specified in docs) - // empty oldTsDir indicates that it didn't exist - oldTsDir = "" - } - oldTsPath := path.Join(w.targetDir, oldTsDir) - - var pathsToRemove sets.String - // if there was no old version, there's nothing to remove - if len(oldTsDir) != 0 { - // (3) - pathsToRemove, err = w.pathsToRemove(cleanPayload, oldTsPath) - if err != nil { - w.log.Error(err, "unable to determine user-visible files to remove") - return err - } - - // (4) - if should, err := shouldWritePayload(cleanPayload, oldTsPath); err != nil { - w.log.Error(err, "unable to determine whether payload should be written to disk") - return err - } else if !should && len(pathsToRemove) == 0 { - w.log.V(1).Info("no update required for target directory", "directory", w.targetDir) - return nil - } else { - w.log.V(1).Info("write required for target directory", "directory", w.targetDir) - } - } - - // (5) - tsDir, err := w.newTimestampDir() - if err != nil { - w.log.Error(err, "error creating new ts data directory") - return err - } - tsDirName := filepath.Base(tsDir) - - // (6) - if err = w.writePayloadToDir(cleanPayload, tsDir); err != nil { - w.log.Error(err, "unable to write payload to ts data directory", "ts directory", tsDir) - return err - } else { - w.log.V(1).Info("performed write of new data to ts data directory", "ts directory", tsDir) - } - - // (7) - if err = w.createUserVisibleFiles(cleanPayload); err != nil { - w.log.Error(err, "unable to create visible symlinks in target directory", "target directory", w.targetDir) - return err - } - - // (8) - newDataDirPath := path.Join(w.targetDir, newDataDirName) - if err = os.Symlink(tsDirName, newDataDirPath); err != nil { - os.RemoveAll(tsDir) - w.log.Error(err, "unable to create symbolic link for atomic update") - return err - } - - // (9) - if runtime.GOOS == "windows" { - os.Remove(dataDirPath) - err = os.Symlink(tsDirName, dataDirPath) - os.Remove(newDataDirPath) - } else { - err = os.Rename(newDataDirPath, dataDirPath) - } - if err != nil { - os.Remove(newDataDirPath) - os.RemoveAll(tsDir) - w.log.Error(err, "unable to rename symbolic link for data directory", "data directory", newDataDirPath) - return err - } - - // (10) - if err = w.removeUserVisiblePaths(pathsToRemove); err != nil { - w.log.Error(err, "unable to remove old visible symlinks") - return err - } - - // (11) - if len(oldTsDir) > 0 { - if err = os.RemoveAll(oldTsPath); err != nil { - w.log.Error(err, "unable to remove old data directory", "data directory", oldTsDir) - return err - } - } - - return nil -} - -// validatePayload returns an error if any path in the payload returns a copy of the payload with the paths cleaned. -func validatePayload(payload map[string]FileProjection) (map[string]FileProjection, error) { - cleanPayload := make(map[string]FileProjection) - for k, content := range payload { - if err := validatePath(k); err != nil { - return nil, err - } - - cleanPayload[filepath.Clean(k)] = content - } - - return cleanPayload, nil -} - -// validatePath validates a single path, returning an error if the path is -// invalid. paths may not: -// -// 1. be absolute -// 2. contain '..' as an element -// 3. start with '..' -// 4. contain filenames larger than 255 characters -// 5. be longer than 4096 characters -func validatePath(targetPath string) error { - // TODO: somehow unify this with the similar api validation, - // validateVolumeSourcePath; the error semantics are just different enough - // from this that it was time-prohibitive trying to find the right - // refactoring to re-use. - if targetPath == "" { - return fmt.Errorf("invalid path: must not be empty: %q", targetPath) - } - if path.IsAbs(targetPath) { - return fmt.Errorf("invalid path: must be relative path: %s", targetPath) - } - - if len(targetPath) > maxPathLength { - return fmt.Errorf("invalid path: must be less than or equal to %d characters", maxPathLength) - } - - items := strings.Split(targetPath, string(os.PathSeparator)) - for _, item := range items { - if item == ".." { - return fmt.Errorf("invalid path: must not contain '..': %s", targetPath) - } - if len(item) > maxFileNameLength { - return fmt.Errorf("invalid path: filenames must be less than or equal to %d characters", maxFileNameLength) - } - } - if strings.HasPrefix(items[0], "..") && len(items[0]) > 2 { - return fmt.Errorf("invalid path: must not start with '..': %s", targetPath) - } - - return nil -} - -// shouldWritePayload returns whether the payload should be written to disk. -func shouldWritePayload(payload map[string]FileProjection, oldTsDir string) (bool, error) { - for userVisiblePath, fileProjection := range payload { - shouldWrite, err := shouldWriteFile(path.Join(oldTsDir, userVisiblePath), fileProjection.Data) - if err != nil { - return false, err - } - - if shouldWrite { - return true, nil - } - } - - return false, nil -} - -// shouldWriteFile returns whether a new version of a file should be written to disk. -func shouldWriteFile(path string, content []byte) (bool, error) { - _, err := os.Lstat(path) - if os.IsNotExist(err) { - return true, nil - } - - contentOnFs, err := ioutil.ReadFile(path) - if err != nil { - return false, err - } - - return (bytes.Compare(content, contentOnFs) != 0), nil -} - -// pathsToRemove walks the current version of the data directory and -// determines which paths should be removed (if any) after the payload is -// written to the target directory. -func (w *AtomicWriter) pathsToRemove(payload map[string]FileProjection, oldTsDir string) (sets.String, error) { - paths := sets.NewString() - visitor := func(path string, info os.FileInfo, err error) error { - relativePath := strings.TrimPrefix(path, oldTsDir) - relativePath = strings.TrimPrefix(relativePath, string(os.PathSeparator)) - if relativePath == "" { - return nil - } - - paths.Insert(relativePath) - return nil - } - - err := filepath.Walk(oldTsDir, visitor) - if os.IsNotExist(err) { - return nil, nil - } else if err != nil { - return nil, err - } - w.log.V(1).Info("current paths", "target directory", w.targetDir, "paths", paths.List()) - - newPaths := sets.NewString() - for file := range payload { - // add all subpaths for the payload to the set of new paths - // to avoid attempting to remove non-empty dirs - for subPath := file; subPath != ""; { - newPaths.Insert(subPath) - subPath, _ = filepath.Split(subPath) - subPath = strings.TrimSuffix(subPath, string(os.PathSeparator)) - } - } - w.log.V(1).Info("new paths", "target directory", w.targetDir, "paths", newPaths.List()) - - result := paths.Difference(newPaths) - w.log.V(1).Info("paths to remove", "target directory", w.targetDir, "paths", result) - - return result, nil -} - -// newTimestampDir creates a new timestamp directory -func (w *AtomicWriter) newTimestampDir() (string, error) { - tsDir, err := ioutil.TempDir(w.targetDir, time.Now().UTC().Format("..2006_01_02_15_04_05.")) - if err != nil { - w.log.Error(err, "unable to create new temp directory") - return "", err - } - - // 0755 permissions are needed to allow 'group' and 'other' to recurse the - // directory tree. do a chmod here to ensure that permissions are set correctly - // regardless of the process' umask. - err = os.Chmod(tsDir, 0755) - if err != nil { - w.log.Error(err, "unable to set mode on new temp directory") - return "", err - } - - return tsDir, nil -} - -// writePayloadToDir writes the given payload to the given directory. The -// directory must exist. -func (w *AtomicWriter) writePayloadToDir(payload map[string]FileProjection, dir string) error { - for userVisiblePath, fileProjection := range payload { - content := fileProjection.Data - mode := os.FileMode(fileProjection.Mode) - fullPath := path.Join(dir, userVisiblePath) - baseDir, _ := filepath.Split(fullPath) - - err := os.MkdirAll(baseDir, os.ModePerm) - if err != nil { - w.log.Error(err, "unable to create directory", "directory", baseDir) - return err - } - - err = ioutil.WriteFile(fullPath, content, mode) - if err != nil { - w.log.Error(err, "unable to write file", "file", fullPath, "mode", mode) - return err - } - // Chmod is needed because ioutil.WriteFile() ends up calling - // open(2) to create the file, so the final mode used is "mode & - // ~umask". But we want to make sure the specified mode is used - // in the file no matter what the umask is. - err = os.Chmod(fullPath, mode) - if err != nil { - w.log.Error(err, "unable to write file", "file", fullPath, "mode", mode) - } - } - - return nil -} - -// createUserVisibleFiles creates the relative symlinks for all the -// files configured in the payload. If the directory in a file path does not -// exist, it is created. -// -// Viz: -// For files: "bar", "foo/bar", "baz/bar", "foo/baz/blah" -// the following symlinks are created: -// bar -> ..data/bar -// foo -> ..data/foo -// baz -> ..data/baz -func (w *AtomicWriter) createUserVisibleFiles(payload map[string]FileProjection) error { - for userVisiblePath := range payload { - slashpos := strings.Index(userVisiblePath, string(os.PathSeparator)) - if slashpos == -1 { - slashpos = len(userVisiblePath) - } - linkname := userVisiblePath[:slashpos] - _, err := os.Readlink(path.Join(w.targetDir, linkname)) - if err != nil && os.IsNotExist(err) { - // The link into the data directory for this path doesn't exist; create it - visibleFile := path.Join(w.targetDir, linkname) - dataDirFile := path.Join(dataDirName, linkname) - - err = os.Symlink(dataDirFile, visibleFile) - if err != nil { - return err - } - } - } - return nil -} - -// removeUserVisiblePaths removes the set of paths from the user-visible -// portion of the writer's target directory. -func (w *AtomicWriter) removeUserVisiblePaths(paths sets.String) error { - ps := string(os.PathSeparator) - var lasterr error - for p := range paths { - // only remove symlinks from the volume root directory (i.e. items that don't contain '/') - if strings.Contains(p, ps) { - continue - } - if err := os.Remove(path.Join(w.targetDir, p)); err != nil { - w.log.Error(err, "unable to prune old user-visible path", "path", p) - lasterr = err - } - } - - return lasterr -} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/certwriter.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/certwriter.go deleted file mode 100644 index ed6b51107c2f..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/certwriter.go +++ /dev/null @@ -1,137 +0,0 @@ -/* -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 writer - -import ( - "crypto/tls" - "crypto/x509" - "encoding/pem" - "errors" - "time" - - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator" -) - -const ( - // CAKeyName is the name of the CA private key - CAKeyName = "ca-key.pem" - // CACertName is the name of the CA certificate - CACertName = "ca-cert.pem" - // ServerKeyName is the name of the server private key - ServerKeyName = "key.pem" - // ServerCertName is the name of the serving certificate - ServerCertName = "cert.pem" -) - -// CertWriter provides method to handle webhooks. -type CertWriter interface { - // EnsureCert provisions the cert for the webhookClientConfig. - EnsureCert(dnsName string) (*generator.Artifacts, bool, error) - // Inject injects the necessary information given the objects. - // It supports MutatingWebhookConfiguration and ValidatingWebhookConfiguration. - Inject(objs ...runtime.Object) error -} - -// handleCommon ensures the given webhook has a proper certificate. -// It uses the given certReadWriter to read and (or) write the certificate. -func handleCommon(dnsName string, ch certReadWriter) (*generator.Artifacts, bool, error) { - if len(dnsName) == 0 { - return nil, false, errors.New("dnsName should not be empty") - } - if ch == nil { - return nil, false, errors.New("certReaderWriter should not be nil") - } - - certs, changed, err := createIfNotExists(ch) - if err != nil { - return nil, changed, err - } - - // Recreate the cert if it's invalid. - valid := validCert(certs, dnsName) - if !valid { - log.Info("cert is invalid or expiring, regenerating a new one") - certs, err = ch.overwrite() - if err != nil { - return nil, false, err - } - changed = true - } - return certs, changed, nil -} - -func createIfNotExists(ch certReadWriter) (*generator.Artifacts, bool, error) { - // Try to read first - certs, err := ch.read() - if isNotFound(err) { - // Create if not exists - certs, err = ch.write() - switch { - // This may happen if there is another racer. - case isAlreadyExists(err): - certs, err = ch.read() - return certs, true, err - default: - return certs, true, err - } - } - return certs, false, err -} - -// certReadWriter provides methods for reading and writing certificates. -type certReadWriter interface { - // read reads a webhook name and returns the certs for it. - read() (*generator.Artifacts, error) - // write writes the certs and return the certs it wrote. - write() (*generator.Artifacts, error) - // overwrite overwrites the existing certs and return the certs it wrote. - overwrite() (*generator.Artifacts, error) -} - -func validCert(certs *generator.Artifacts, dnsName string) bool { - if certs == nil { - return false - } - - // Verify key and cert are valid pair - _, err := tls.X509KeyPair(certs.Cert, certs.Key) - if err != nil { - return false - } - - // Verify cert is good for desired DNS name and signed by CA and will be valid for desired period of time. - pool := x509.NewCertPool() - if !pool.AppendCertsFromPEM(certs.CACert) { - return false - } - block, _ := pem.Decode([]byte(certs.Cert)) - if block == nil { - return false - } - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return false - } - ops := x509.VerifyOptions{ - DNSName: dnsName, - Roots: pool, - CurrentTime: time.Now().AddDate(0, 6, 0), - } - _, err = cert.Verify(ops) - return err == nil -} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/doc.go deleted file mode 100644 index 91aa07ae4af5..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/doc.go +++ /dev/null @@ -1,64 +0,0 @@ -/* -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 writer provides method to provision and persist the certificates. - -It will create the certificates if they don't exist. -It will ensure the certificates are valid and not expiring. If not, it will recreate them. - -Create a CertWriter that can write the certificate to secret - - writer, err := NewSecretCertWriter(SecretCertWriterOptions{ - Secret: types.NamespacedName{Namespace: "foo", Name: "bar"}, - Client: client, - }) - if err != nil { - // handler error - } - -Create a CertWriter that can write the certificate to the filesystem. - - writer, err := NewFSCertWriter(FSCertWriterOptions{ - Path: "path/to/cert/", - }) - if err != nil { - // handler error - } - -Provision the certificates using the CertWriter. The certificate will be available in the desired secret or -the desired path. - - // writer can be either one of the CertWriters created above - certs, changed, err := writer.EnsureCerts("admissionwebhook.k8s.io", false) - if err != nil { - // handler error - } - -Inject necessary information given the objects. - - err = writer.Inject(objs...) - if err != nil { - // handler error - } -*/ -package writer - -import ( - logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" -) - -var log = logf.KBLog.WithName("admission").WithName("cert").WithName("writer") diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/error.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/error.go deleted file mode 100644 index 4f98e8cd0593..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/error.go +++ /dev/null @@ -1,43 +0,0 @@ -/* -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 writer - -type notFoundError struct { - err error -} - -func (e notFoundError) Error() string { - return e.err.Error() -} - -func isNotFound(err error) bool { - _, ok := err.(notFoundError) - return ok -} - -type alreadyExistError struct { - err error -} - -func (e alreadyExistError) Error() string { - return e.err.Error() -} - -func isAlreadyExists(err error) bool { - _, ok := err.(alreadyExistError) - return ok -} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/fs.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/fs.go deleted file mode 100644 index 6a4ef16da8d2..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/fs.go +++ /dev/null @@ -1,216 +0,0 @@ -/* -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 writer - -import ( - "errors" - "fmt" - "io/ioutil" - "os" - "path" - - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator" - "sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/atomic" -) - -// fsCertWriter provisions the certificate by reading and writing to the filesystem. -type fsCertWriter struct { - // dnsName is the DNS name that the certificate is for. - dnsName string - - *FSCertWriterOptions -} - -// FSCertWriterOptions are options for constructing a FSCertWriter. -type FSCertWriterOptions struct { - // certGenerator generates the certificates. - CertGenerator generator.CertGenerator - // path is the directory that the certificate and private key and CA certificate will be written. - Path string -} - -var _ CertWriter = &fsCertWriter{} - -func (ops *FSCertWriterOptions) setDefaults() { - if ops.CertGenerator == nil { - ops.CertGenerator = &generator.SelfSignedCertGenerator{} - } -} - -func (ops *FSCertWriterOptions) validate() error { - if len(ops.Path) == 0 { - return errors.New("path must be set in FSCertWriterOptions") - } - return nil -} - -// NewFSCertWriter constructs a CertWriter that persists the certificate on filesystem. -func NewFSCertWriter(ops FSCertWriterOptions) (CertWriter, error) { - ops.setDefaults() - err := ops.validate() - if err != nil { - return nil, err - } - return &fsCertWriter{ - FSCertWriterOptions: &ops, - }, nil -} - -// EnsureCert provisions certificates for a webhookClientConfig by writing the certificates in the filesystem. -func (f *fsCertWriter) EnsureCert(dnsName string) (*generator.Artifacts, bool, error) { - // create or refresh cert and write it to fs - f.dnsName = dnsName - return handleCommon(f.dnsName, f) -} - -func (f *fsCertWriter) write() (*generator.Artifacts, error) { - return f.doWrite() -} - -func (f *fsCertWriter) overwrite() (*generator.Artifacts, error) { - return f.doWrite() -} - -func (f *fsCertWriter) doWrite() (*generator.Artifacts, error) { - certs, err := f.CertGenerator.Generate(f.dnsName) - if err != nil { - return nil, err - } - - // AtomicWriter's algorithm only manages files using symbolic link. - // If a file is not a symbolic link, will ignore the update for it. - // We want to cleanup for AtomicWriter by removing old files that are not symbolic links. - err = prepareToWrite(f.Path) - if err != nil { - return nil, err - } - - aw, err := atomic.NewAtomicWriter(f.Path, log.WithName("atomic-writer"). - WithValues("task", "processing webhook")) - if err != nil { - return nil, err - } - err = aw.Write(certToProjectionMap(certs)) - return certs, err -} - -// prepareToWrite ensures it directory is compatible with the atomic.Writer library. -func prepareToWrite(dir string) error { - _, err := os.Stat(dir) - switch { - case os.IsNotExist(err): - log.Info("cert directory doesn't exist, creating", "directory", dir) - // TODO: figure out if we can reduce the permission. (Now it's 0777) - err = os.MkdirAll(dir, 0777) - if err != nil { - return fmt.Errorf("can't create dir: %v", dir) - } - case err != nil: - return err - } - - filenames := []string{CAKeyName, CACertName, ServerCertName, ServerKeyName} - for _, f := range filenames { - abspath := path.Join(dir, f) - _, err := os.Stat(abspath) - if os.IsNotExist(err) { - continue - } else if err != nil { - log.Error(err, "unable to stat file", "file", abspath) - } - _, err = os.Readlink(abspath) - // if it's not a symbolic link - if err != nil { - err = os.Remove(abspath) - if err != nil { - log.Error(err, "unable to remove old file", "file", abspath) - } - } - } - return nil -} - -func (f *fsCertWriter) read() (*generator.Artifacts, error) { - if err := ensureExist(f.Path); err != nil { - return nil, err - } - caKeyBytes, err := ioutil.ReadFile(path.Join(f.Path, CAKeyName)) - if err != nil { - return nil, err - } - caCertBytes, err := ioutil.ReadFile(path.Join(f.Path, CACertName)) - if err != nil { - return nil, err - } - certBytes, err := ioutil.ReadFile(path.Join(f.Path, ServerCertName)) - if err != nil { - return nil, err - } - keyBytes, err := ioutil.ReadFile(path.Join(f.Path, ServerKeyName)) - if err != nil { - return nil, err - } - return &generator.Artifacts{ - CAKey: caKeyBytes, - CACert: caCertBytes, - Cert: certBytes, - Key: keyBytes, - }, nil -} - -func ensureExist(dir string) error { - filenames := []string{CAKeyName, CACertName, ServerCertName, ServerKeyName} - for _, filename := range filenames { - _, err := os.Stat(path.Join(dir, filename)) - switch { - case err == nil: - continue - case os.IsNotExist(err): - return notFoundError{err} - default: - return err - } - } - return nil -} - -func certToProjectionMap(cert *generator.Artifacts) map[string]atomic.FileProjection { - // TODO: figure out if we can reduce the permission. (Now it's 0666) - return map[string]atomic.FileProjection{ - CAKeyName: { - Data: cert.CAKey, - Mode: 0666, - }, - CACertName: { - Data: cert.CACert, - Mode: 0666, - }, - ServerCertName: { - Data: cert.Cert, - Mode: 0666, - }, - ServerKeyName: { - Data: cert.Key, - Mode: 0666, - }, - } -} - -func (f *fsCertWriter) Inject(objs ...runtime.Object) error { - return nil -} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/secret.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/secret.go deleted file mode 100644 index 04d7e90c7d79..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer/secret.go +++ /dev/null @@ -1,184 +0,0 @@ -/* -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 writer - -import ( - "errors" - - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator" -) - -// secretCertWriter provisions the certificate by reading and writing to the k8s secrets. -type secretCertWriter struct { - *SecretCertWriterOptions - - // dnsName is the DNS name that the certificate is for. - dnsName string -} - -// SecretCertWriterOptions is options for constructing a secretCertWriter. -type SecretCertWriterOptions struct { - // client talks to a kubernetes cluster for creating the secret. - Client client.Client - // certGenerator generates the certificates. - CertGenerator generator.CertGenerator - // secret points the secret that contains certificates that written by the CertWriter. - Secret *types.NamespacedName -} - -var _ CertWriter = &secretCertWriter{} - -func (ops *SecretCertWriterOptions) setDefaults() { - if ops.CertGenerator == nil { - ops.CertGenerator = &generator.SelfSignedCertGenerator{} - } -} - -func (ops *SecretCertWriterOptions) validate() error { - if ops.Client == nil { - return errors.New("client must be set in SecretCertWriterOptions") - } - if ops.Secret == nil { - return errors.New("secret must be set in SecretCertWriterOptions") - } - return nil -} - -// NewSecretCertWriter constructs a CertWriter that persists the certificate in a k8s secret. -func NewSecretCertWriter(ops SecretCertWriterOptions) (CertWriter, error) { - ops.setDefaults() - err := ops.validate() - if err != nil { - return nil, err - } - return &secretCertWriter{ - SecretCertWriterOptions: &ops, - }, nil -} - -// EnsureCert provisions certificates for a webhookClientConfig by writing the certificates to a k8s secret. -func (s *secretCertWriter) EnsureCert(dnsName string) (*generator.Artifacts, bool, error) { - // Create or refresh the certs based on clientConfig - s.dnsName = dnsName - return handleCommon(s.dnsName, s) -} - -var _ certReadWriter = &secretCertWriter{} - -func (s *secretCertWriter) buildSecret() (*corev1.Secret, *generator.Artifacts, error) { - certs, err := s.CertGenerator.Generate(s.dnsName) - if err != nil { - return nil, nil, err - } - secret := certsToSecret(certs, *s.Secret) - return secret, certs, err -} - -func (s *secretCertWriter) write() (*generator.Artifacts, error) { - secret, certs, err := s.buildSecret() - if err != nil { - return nil, err - } - err = s.Client.Create(nil, secret) - if apierrors.IsAlreadyExists(err) { - return nil, alreadyExistError{err} - } - return certs, err -} - -func (s *secretCertWriter) overwrite() ( - *generator.Artifacts, error) { - secret, certs, err := s.buildSecret() - if err != nil { - return nil, err - } - err = s.Client.Update(nil, secret) - return certs, err -} - -func (s *secretCertWriter) read() (*generator.Artifacts, error) { - secret := &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Secret", - }, - } - err := s.Client.Get(nil, *s.Secret, secret) - if apierrors.IsNotFound(err) { - return nil, notFoundError{err} - } - certs := secretToCerts(secret) - if certs != nil { - // Store the CA for next usage. - s.CertGenerator.SetCA(certs.CAKey, certs.CACert) - } - return certs, nil -} - -func secretToCerts(secret *corev1.Secret) *generator.Artifacts { - if secret.Data == nil { - return nil - } - return &generator.Artifacts{ - CAKey: secret.Data[CAKeyName], - CACert: secret.Data[CACertName], - Cert: secret.Data[ServerCertName], - Key: secret.Data[ServerKeyName], - } -} - -func certsToSecret(certs *generator.Artifacts, sec types.NamespacedName) *corev1.Secret { - return &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: sec.Namespace, - Name: sec.Name, - }, - Data: map[string][]byte{ - CAKeyName: certs.CAKey, - CACertName: certs.CACert, - ServerKeyName: certs.Key, - ServerCertName: certs.Cert, - }, - } -} - -// Inject sets the ownerReference in the secret. -func (s *secretCertWriter) Inject(objs ...runtime.Object) error { - // TODO: figure out how to get the UID - //for i := range objs { - // accessor, err := meta.Accessor(objs[i]) - // if err != nil { - // return err - // } - // err = controllerutil.SetControllerReference(accessor, s.sec, scheme.Scheme) - // if err != nil { - // return err - // } - //} - //return s.client.Update(context.Background(), s.sec) - return nil -} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go deleted file mode 100644 index 180a2b538fe6..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go +++ /dev/null @@ -1,308 +0,0 @@ -/* -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 webhook - -import ( - "context" - "fmt" - "io" - "net/http" - "path" - "sync" - "time" - - "k8s.io/apimachinery/pkg/runtime" - apitypes "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/runtime/inject" - atypes "sigs.k8s.io/controller-runtime/pkg/webhook/admission/types" - "sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert" - "sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer" - "sigs.k8s.io/controller-runtime/pkg/webhook/types" -) - -// default interval for checking cert is 90 days (~3 months) -var defaultCertRefreshInterval = 3 * 30 * 24 * time.Hour - -// ServerOptions are options for configuring an admission webhook server. -type ServerOptions struct { - // Port is the port number that the server will serve. - // It will be defaulted to 443 if unspecified. - Port int32 - - // CertDir is the directory that contains the server key and certificate. - // If using FSCertWriter in Provisioner, the server itself will provision the certificate and - // store it in this directory. - // If using SecretCertWriter in Provisioner, the server will provision the certificate in a secret, - // the user is responsible to mount the secret to the this location for the server to consume. - CertDir string - - // Client is a client defined in controller-runtime instead of a client-go client. - // It knows how to talk to a kubernetes cluster. - // Client will be injected by the manager if not set. - Client client.Client - - // DisableWebhookConfigInstaller controls if the server will automatically create webhook related objects - // during bootstrapping. e.g. webhookConfiguration, service and secret. - // If false, the server will install the webhook config objects. It is defaulted to false. - DisableWebhookConfigInstaller *bool - - // BootstrapOptions contains the options for bootstrapping the admission server. - *BootstrapOptions -} - -// BootstrapOptions are options for bootstrapping an admission webhook server. -type BootstrapOptions struct { - // MutatingWebhookConfigName is the name that used for creating the MutatingWebhookConfiguration object. - MutatingWebhookConfigName string - // ValidatingWebhookConfigName is the name that used for creating the ValidatingWebhookConfiguration object. - ValidatingWebhookConfigName string - - // Secret is the location for storing the certificate for the admission server. - // The server should have permission to create a secret in the namespace. - // This is optional. If unspecified, it will write to the filesystem. - // It the secret already exists and is different from the desired, it will be replaced. - Secret *apitypes.NamespacedName - - // Deprecated: Writer will not be used anywhere. - Writer io.Writer - - // Service is k8s service fronting the webhook server pod(s). - // This field is optional. But one and only one of Service and Host need to be set. - // This maps to field .webhooks.getClientConfig.service - // https://github.com/kubernetes/api/blob/183f3326a9353bd6d41430fc80f96259331d029c/admissionregistration/v1beta1/types.go#L260 - Service *Service - // Host is the host name of .webhooks.clientConfig.url - // https://github.com/kubernetes/api/blob/183f3326a9353bd6d41430fc80f96259331d029c/admissionregistration/v1beta1/types.go#L250 - // This field is optional. But one and only one of Service and Host need to be set. - // If neither Service nor Host is unspecified, Host will be defaulted to "localhost". - Host *string - - // certProvisioner is constructed using certGenerator and certWriter - certProvisioner *cert.Provisioner // nolint: structcheck - - // err will be non-nil if there is an error occur during initialization. - err error // nolint: structcheck -} - -// Service contains information for creating a service -type Service struct { - // Name of the service - Name string - // Namespace of the service - Namespace string - // Selectors is the selector of the service. - // This must select the pods that runs this webhook server. - Selectors map[string]string -} - -// Server is an admission webhook server that can serve traffic and -// generates related k8s resources for deploying. -type Server struct { - // Name is the name of server - Name string - - // ServerOptions contains options for configuring the admission server. - ServerOptions - - sMux *http.ServeMux - // registry maps a path to a http.Handler. - registry map[string]Webhook - - // mutatingWebhookConfiguration and validatingWebhookConfiguration are populated during server bootstrapping. - // They can be nil, if there is no webhook registered under it. - webhookConfigurations []runtime.Object - - // manager is the manager that this webhook server will be registered. - manager manager.Manager - - // httpServer is the actual server that serves the traffic. - httpServer *http.Server - - once sync.Once -} - -// Webhook defines the basics that a webhook should support. -type Webhook interface { - // GetName returns the name of the webhook. - GetName() string - // GetPath returns the path that the webhook registered. - GetPath() string - // GetType returns the Type of the webhook. - // e.g. mutating or validating - GetType() types.WebhookType - // Handler returns a http.Handler for the webhook. - Handler() http.Handler - // Validate validates if the webhook itself is valid. - // If invalid, a non-nil error will be returned. - Validate() error -} - -// NewServer creates a new admission webhook server. -func NewServer(name string, mgr manager.Manager, options ServerOptions) (*Server, error) { - as := &Server{ - Name: name, - sMux: http.NewServeMux(), - registry: map[string]Webhook{}, - ServerOptions: options, - manager: mgr, - } - - return as, nil -} - -// Register validates and registers webhook(s) in the server -func (s *Server) Register(webhooks ...Webhook) error { - for i, webhook := range webhooks { - // validate the webhook before registering it. - err := webhook.Validate() - if err != nil { - return err - } - _, found := s.registry[webhook.GetPath()] - if found { - return fmt.Errorf("can't register duplicate path: %v", webhook.GetPath()) - } - s.registry[webhook.GetPath()] = webhooks[i] - s.sMux.Handle(webhook.GetPath(), webhook.Handler()) - } - - // Lazily add Server to manager. - // Because the all webhook handlers to be in place, so we can inject the things they need. - return s.manager.Add(s) -} - -// Handle registers a http.Handler for the given pattern. -func (s *Server) Handle(pattern string, handler http.Handler) { - s.sMux.Handle(pattern, handler) -} - -var _ manager.Runnable = &Server{} - -// Start runs the server. -// It will install the webhook related resources depend on the server configuration. -func (s *Server) Start(stop <-chan struct{}) error { - s.once.Do(s.setDefault) - if s.err != nil { - return s.err - } - - if s.DisableWebhookConfigInstaller != nil && !*s.DisableWebhookConfigInstaller { - log.Info("installing webhook configuration in cluster") - err := s.InstallWebhookManifests() - if err != nil { - return err - } - } else { - log.Info("webhook installer is disabled") - } - - return s.run(stop) -} - -func (s *Server) run(stop <-chan struct{}) error { // nolint: gocyclo - errCh := make(chan error) - serveFn := func() { - s.httpServer = &http.Server{ - Addr: fmt.Sprintf(":%v", s.Port), - Handler: s.sMux, - } - log.Info("starting the webhook server.") - errCh <- s.httpServer.ListenAndServeTLS(path.Join(s.CertDir, writer.ServerCertName), path.Join(s.CertDir, writer.ServerKeyName)) - } - - shutdownHappend := false - timer := time.Tick(wait.Jitter(defaultCertRefreshInterval, 0.1)) - go serveFn() - for { - select { - case <-timer: - changed, err := s.RefreshCert() - if err != nil { - log.Error(err, "encountering error when refreshing the certificate") - return err - } - if !changed { - log.Info("no need to reload the certificates.") - continue - } - log.Info("server is shutting down to reload the certificates.") - shutdownHappend = true - err = s.httpServer.Shutdown(context.Background()) - if err != nil { - log.Error(err, "encountering error when shutting down") - return err - } - timer = time.Tick(wait.Jitter(defaultCertRefreshInterval, 0.1)) - go serveFn() - case <-stop: - return s.httpServer.Shutdown(context.Background()) - case e := <-errCh: - // Don't exit when getting an http.ErrServerClosed error due to restarting the server. - if shutdownHappend && e == http.ErrServerClosed { - shutdownHappend = false - } else if e != nil { - log.Error(e, "server returns an unexpected error") - return e - } - } - } -} - -// RefreshCert refreshes the certificate using Server's Provisioner if the certificate is expiring. -func (s *Server) RefreshCert() (bool, error) { - cc, err := s.getClientConfig() - if err != nil { - return false, err - } - changed, err := s.certProvisioner.Provision(cert.Options{ - ClientConfig: cc, - Objects: s.webhookConfigurations, - }) - if err != nil { - return false, err - } - - return changed, batchCreateOrReplace(s.Client, s.webhookConfigurations...) -} - -var _ inject.Client = &Server{} - -// InjectClient injects the client into the server -func (s *Server) InjectClient(c client.Client) error { - s.Client = c - for _, wh := range s.registry { - if _, err := inject.ClientInto(c, wh.Handler()); err != nil { - return err - } - } - return nil -} - -var _ inject.Decoder = &Server{} - -// InjectDecoder injects the client into the server -func (s *Server) InjectDecoder(d atypes.Decoder) error { - for _, wh := range s.registry { - if _, err := inject.DecoderInto(d, wh.Handler()); err != nil { - return err - } - } - return nil -} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/util.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/util.go deleted file mode 100644 index 47e2635ca5e5..000000000000 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/util.go +++ /dev/null @@ -1,115 +0,0 @@ -/* -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 webhook - -import ( - "context" - - admissionregistration "k8s.io/api/admissionregistration/v1beta1" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -type mutateFn func(current, desired *runtime.Object) error - -var serviceFn = func(current, desired *runtime.Object) error { - typedC := (*current).(*corev1.Service) - typedD := (*desired).(*corev1.Service) - typedC.Spec.Selector = typedD.Spec.Selector - return nil -} - -var mutatingWebhookConfigFn = func(current, desired *runtime.Object) error { - typedC := (*current).(*admissionregistration.MutatingWebhookConfiguration) - typedD := (*desired).(*admissionregistration.MutatingWebhookConfiguration) - typedC.Webhooks = typedD.Webhooks - return nil -} - -var validatingWebhookConfigFn = func(current, desired *runtime.Object) error { - typedC := (*current).(*admissionregistration.ValidatingWebhookConfiguration) - typedD := (*desired).(*admissionregistration.ValidatingWebhookConfiguration) - typedC.Webhooks = typedD.Webhooks - return nil -} - -var genericFn = func(current, desired *runtime.Object) error { - *current = *desired - return nil -} - -// createOrReplaceHelper creates the object if it doesn't exist; -// otherwise, it will replace it. -// When replacing, fn should know how to preserve existing fields in the object GET from the APIServer. -// TODO: use the helper in #98 when it merges. -func createOrReplaceHelper(c client.Client, obj runtime.Object, fn mutateFn) error { - if obj == nil { - return nil - } - err := c.Create(context.Background(), obj) - if apierrors.IsAlreadyExists(err) { - // TODO: retry mutiple times with backoff if necessary. - existing := obj.DeepCopyObject() - objectKey, err := client.ObjectKeyFromObject(obj) - if err != nil { - return err - } - err = c.Get(context.Background(), objectKey, existing) - if err != nil { - return err - } - err = fn(&existing, &obj) - if err != nil { - return err - } - return c.Update(context.Background(), existing) - } - return err -} - -// createOrReplace creates the object if it doesn't exist; -// otherwise, it will replace it. -// When replacing, it knows how to preserve existing fields in the object GET from the APIServer. -// It currently only support MutatingWebhookConfiguration, ValidatingWebhookConfiguration and Service. -// For other kinds, it uses genericFn to replace the whole object. -func createOrReplace(c client.Client, obj runtime.Object) error { - if obj == nil { - return nil - } - switch obj.(type) { - case *admissionregistration.MutatingWebhookConfiguration: - return createOrReplaceHelper(c, obj, mutatingWebhookConfigFn) - case *admissionregistration.ValidatingWebhookConfiguration: - return createOrReplaceHelper(c, obj, validatingWebhookConfigFn) - case *corev1.Service: - return createOrReplaceHelper(c, obj, serviceFn) - default: - return createOrReplaceHelper(c, obj, genericFn) - } -} - -func batchCreateOrReplace(c client.Client, objs ...runtime.Object) error { - for i := range objs { - err := createOrReplace(c, objs[i]) - if err != nil { - return err - } - } - return nil -} diff --git a/vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go b/vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go index 6aad2c7c0b11..74c29d21d998 100644 --- a/vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go +++ b/vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go @@ -76,9 +76,13 @@ Usage: } f := cmd.Flags() - f.StringVar(&o.Name, "name", o.Name, "Name to be used as prefix in identifier for manifests") + f.StringVar(&o.Name, "name", o.Name, "name to be used as prefix in identifier for manifests") + f.StringVar(&o.ServiceAccount, "service-account", o.ServiceAccount, "service account to bind the role to") + f.StringVar(&o.Namespace, "service-account-namespace", o.Namespace, "namespace of the service account to bind the role to") f.StringVar(&o.InputDir, "input-dir", o.InputDir, "input directory pointing to Go source files") - f.StringVar(&o.OutputDir, "output-dir", o.OutputDir, "output directory where generated manifests will be saved.") + f.StringVar(&o.OutputDir, "output-dir", o.OutputDir, "output directory where generated manifests will be saved") + f.StringVar(&o.RoleFile, "role-file", o.RoleFile, "output file for the role manifest") + f.StringVar(&o.BindingFile, "binding-file", o.BindingFile, "output file for the role binding manifest") return cmd } @@ -110,6 +114,8 @@ Usage: f.StringVar(&g.Domain, "domain", "", "domain of the resources, will try to fetch it from PROJECT file if not specified") f.StringVar(&g.Namespace, "namespace", "", "CRD namespace, treat it as cluster scoped if not set") f.BoolVar(&g.SkipMapValidation, "skip-map-validation", true, "if set to true, skip generating OpenAPI validation schema for map type in CRD.") + f.StringVar(&g.APIsPath, "apis-path", "pkg/apis", "the path to search for apis relative to the current directory") + f.StringVar(&g.APIsPkg, "apis-pkg", "", "the absolute Go pkg name for current project's api pkg.") return cmd } @@ -158,6 +164,19 @@ Usage: log.Fatal(err) } fmt.Printf("RBAC manifests generated under '%s' \n", rbacOptions.OutputDir) + + o := &webhook.Options{ + WriterOptions: webhook.WriterOptions{ + InputDir: filepath.Join(projectDir, "pkg"), + OutputDir: filepath.Join(projectDir, "config", "webhook"), + PatchOutputDir: filepath.Join(projectDir, "config", "default"), + }, + } + o.SetDefaults() + if err := webhook.Generate(o); err != nil { + log.Fatal(err) + } + fmt.Printf("webhook manifests generated under '%s' directory\n", o.OutputDir) }, } f := cmd.Flags() @@ -167,7 +186,7 @@ Usage: } func newWebhookCmd() *cobra.Command { - o := &webhook.ManifestOptions{} + o := &webhook.Options{} o.SetDefaults() cmd := &cobra.Command{ diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/crd/generator/generator.go b/vendor/sigs.k8s.io/controller-tools/pkg/crd/generator/generator.go index 512311873201..dfbf15278995 100644 --- a/vendor/sigs.k8s.io/controller-tools/pkg/crd/generator/generator.go +++ b/vendor/sigs.k8s.io/controller-tools/pkg/crd/generator/generator.go @@ -48,6 +48,10 @@ type Generator struct { // apisPkg is the absolute Go pkg name for current project's 'pkg/apis' pkg. // This is needed to determine if a Type belongs to the project or it is a referred Type. apisPkg string + + // APIsPath and APIsPkg allow customized generation for Go types existing under directories other than pkg/apis + APIsPath string + APIsPkg string } // ValidateAndInitFields validate and init generator fields. @@ -80,13 +84,7 @@ func (c *Generator) ValidateAndInitFields() error { c.Domain = crdutil.GetDomainFromProject(c.RootPath) } - // Validate apis directory exists under working path - apisPath := path.Join(c.RootPath, "pkg/apis") - if _, err := os.Stat(apisPath); err != nil { - return fmt.Errorf("error validating apis path %s: %v", apisPath, err) - } - - c.apisPkg, err = crdutil.DirToGoPkg(apisPath) + err = c.setAPIsPkg() if err != nil { return err } @@ -112,7 +110,7 @@ func (c *Generator) Do() error { return fmt.Errorf("failed switching working dir: %v", err) } - if err := b.AddDirRecursive("./pkg/apis"); err != nil { + if err := b.AddDirRecursive("./" + c.APIsPath); err != nil { return fmt.Errorf("failed making a parser: %v", err) } ctx, err := parse.NewContext(b) @@ -185,3 +183,25 @@ func (c *Generator) getCrds(p *parse.APIs) map[string][]byte { func (c *Generator) belongsToAPIsPkg(t *types.Type) bool { return strings.HasPrefix(t.Name.Package, c.apisPkg) } + +func (c *Generator) setAPIsPkg() error { + var err error + if c.APIsPath == "" { + c.APIsPath = "pkg/apis" + } + + c.apisPkg = c.APIsPkg + if c.apisPkg == "" { + // Validate apis directory exists under working path + apisPath := path.Join(c.RootPath, c.APIsPath) + if _, err := os.Stat(apisPath); err != nil { + return fmt.Errorf("error validating apis path %s: %v", apisPath, err) + } + + c.apisPkg, err = crdutil.DirToGoPkg(apisPath) + if err != nil { + return err + } + } + return nil +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/crd.go b/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/crd.go index 0e079e209cf6..a03f6bb82ae3 100644 --- a/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/crd.go +++ b/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/crd.go @@ -86,6 +86,11 @@ func (b *APIs) parseCRDs() { resource.Categories = categories } + if hasSingular(resource.Type) { + singularName := getSingularName(resource.Type) + resource.CRD.Spec.Names.Singular = singularName + } + if hasStatusSubresource(resource.Type) { if resource.CRD.Spec.Subresources == nil { resource.CRD.Spec.Subresources = &v1beta1.CustomResourceSubresources{} @@ -121,7 +126,7 @@ func (b *APIs) parseCRDs() { resource.CRD.Spec.AdditionalPrinterColumns = result } if len(resource.ShortName) > 0 { - resource.CRD.Spec.Names.ShortNames = []string{resource.ShortName} + resource.CRD.Spec.Names.ShortNames = strings.Split(resource.ShortName, ";") } } } @@ -136,6 +141,18 @@ func (b *APIs) getTime() string { }` } +func (b *APIs) getDuration() string { + return `v1beta1.JSONSchemaProps{ + Type: "string", +}` +} + +func (b *APIs) getQuantity() string { + return `v1beta1.JSONSchemaProps{ + Type: "string", +}` +} + func (b *APIs) objSchema() string { return `v1beta1.JSONSchemaProps{ Type: "object", @@ -147,38 +164,43 @@ func (b *APIs) objSchema() string { func (b *APIs) typeToJSONSchemaProps(t *types.Type, found sets.String, comments []string, isRoot bool) (v1beta1.JSONSchemaProps, string) { // Special cases time := types.Name{Name: "Time", Package: "k8s.io/apimachinery/pkg/apis/meta/v1"} + duration := types.Name{Name: "Duration", Package: "k8s.io/apimachinery/pkg/apis/meta/v1"} + quantity := types.Name{Name: "Quantity", Package: "k8s.io/apimachinery/pkg/api/resource"} meta := types.Name{Name: "ObjectMeta", Package: "k8s.io/apimachinery/pkg/apis/meta/v1"} unstructured := types.Name{Name: "Unstructured", Package: "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"} + rawExtension := types.Name{Name: "RawExtension", Package: "k8s.io/apimachinery/pkg/runtime"} intOrString := types.Name{Name: "IntOrString", Package: "k8s.io/apimachinery/pkg/util/intstr"} + // special types first + specialTypeProps := v1beta1.JSONSchemaProps{ + Description: parseDescription(comments), + } + for _, l := range comments { + getValidation(l, &specialTypeProps) + } switch t.Name { case time: - return v1beta1.JSONSchemaProps{ - Type: "string", - Format: "date-time", - Description: parseDescription(comments), - }, b.getTime() - case meta: - return v1beta1.JSONSchemaProps{ - Type: "object", - Description: parseDescription(comments), - }, b.objSchema() - case unstructured: - return v1beta1.JSONSchemaProps{ - Type: "object", - Description: parseDescription(comments), - }, b.objSchema() + specialTypeProps.Type = "string" + specialTypeProps.Format = "date-time" + return specialTypeProps, b.getTime() + case duration: + specialTypeProps.Type = "string" + return specialTypeProps, b.getDuration() + case quantity: + specialTypeProps.Type = "string" + return specialTypeProps, b.getQuantity() + case meta, unstructured, rawExtension: + specialTypeProps.Type = "object" + return specialTypeProps, b.objSchema() case intOrString: - return v1beta1.JSONSchemaProps{ - OneOf: []v1beta1.JSONSchemaProps{ - { - Type: "string", - }, - { - Type: "integer", - }, + specialTypeProps.AnyOf = []v1beta1.JSONSchemaProps{ + { + Type: "string", + }, + { + Type: "integer", }, - Description: parseDescription(comments), - }, b.objSchema() + } + return specialTypeProps, b.objSchema() } var v v1beta1.JSONSchemaProps @@ -205,7 +227,7 @@ func (b *APIs) typeToJSONSchemaProps(t *types.Type, found sets.String, comments return v, s } -var jsonRegex = regexp.MustCompile("json:\"([a-zA-Z,]+)\"") +var jsonRegex = regexp.MustCompile("json:\"([a-zA-Z0-9,]+)\"") type primitiveTemplateArgs struct { v1beta1.JSONSchemaProps @@ -277,6 +299,7 @@ func (b *APIs) parsePrimitiveValidation(t *types.Type, found sets.String, commen n = "boolean" case "string": n = "string" + f = props.Format default: n = t.Name.Name } @@ -322,6 +345,11 @@ func (b *APIs) parseMapValidation(t *types.Type, found sets.String, comments []s Allows: true, Schema: &additionalProps} } + + for _, l := range comments { + getValidation(l, &props) + } + buff := &bytes.Buffer{} if err := mapTemplate.Execute(buff, mapTempateArgs{Result: result, SkipMapValidation: parseOption.SkipMapValidation}); err != nil { log.Fatalf("%v", err) @@ -377,6 +405,11 @@ func (b *APIs) parseArrayValidation(t *types.Type, found sets.String, comments [ for _, l := range comments { getValidation(l, &props) } + if t.Name.Name != "[]byte" { + // Except for the byte array special case above, the "format" property + // should be applied to the array items and not the array itself. + props.Format = "" + } buff := &bytes.Buffer{} if err := arrayTemplate.Execute(buff, arrayTemplateArgs{props, result}); err != nil { log.Fatalf("%v", err) @@ -417,6 +450,10 @@ func (b *APIs) parseObjectValidation(t *types.Type, found sets.String, comments Description: parseDescription(comments), } + for _, l := range comments { + getValidation(l, &props) + } + if strings.HasPrefix(t.Name.String(), "k8s.io/api") { if err := objectTemplate.Execute(buff, objectTemplateArgs{props, nil, nil, false}); err != nil { log.Fatalf("%v", err) @@ -426,11 +463,6 @@ func (b *APIs) parseObjectValidation(t *types.Type, found sets.String, comments props.Properties = m props.Required = required - // Only add field validation for non-inlined fields - for _, l := range comments { - getValidation(l, &props) - } - if err := objectTemplate.Execute(buff, objectTemplateArgs{props, result, required, isRoot}); err != nil { log.Fatalf("%v", err) } diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/index.go b/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/index.go index 067ba9a488c9..a08cf751b56b 100644 --- a/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/index.go +++ b/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/index.go @@ -71,7 +71,8 @@ func (b *APIs) parseIndex() { // TODO: revisit the part... if r.Resource == "" { - r.Resource = strings.ToLower(inflect.Pluralize(r.Kind)) + rs := inflect.NewDefaultRuleset() + r.Resource = rs.Pluralize(strings.ToLower(r.Kind)) } rt, err := parseResourceAnnotation(c) if err != nil { diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/util.go b/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/util.go index 6649bf59bb93..7df445353442 100644 --- a/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/util.go +++ b/vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/util.go @@ -217,6 +217,20 @@ func HasDocAnnotation(t *types.Type) bool { return false } +// hasSingular returns true if t is an APIResource annotated with +// +kubebuilder:singular +func hasSingular(t *types.Type) bool { + if !IsAPIResource(t) { + return false + } + for _, c := range t.CommentLines{ + if strings.Contains(c, "+kubebuilder:singular"){ + return true + } + } + return false +} + // IsUnversioned returns true if t is in given group, and not in versioned path. func IsUnversioned(t *types.Type, group string) bool { return IsApisDir(filepath.Base(filepath.Dir(t.Name.Package))) && GetGroup(t) == group @@ -311,6 +325,16 @@ func getCategoriesTag(c *types.Type) string { return resource } +// getSingularName returns the value of the +kubebuilder:singular tag +func getSingularName(c *types.Type) string { + comments := Comments(c.CommentLines) + singular := comments.getTag("kubebuilder:singular", "=") + if len(singular) == 0 { + panic(errors.Errorf("Must specify a value to use with +kubebuilder:singular comment for type %v", c.Name)) + } + return singular +} + // getDocAnnotation parse annotations of "+kubebuilder:doc:" with tags of "warning" or "doc" for control generating doc config. // E.g. +kubebuilder:doc:warning=foo +kubebuilder:doc:note=bar func getDocAnnotation(t *types.Type, tags ...string) map[string]string { diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/rbac/manifests.go b/vendor/sigs.k8s.io/controller-tools/pkg/rbac/manifests.go index a49b862fda2a..629480ec1b5c 100644 --- a/vendor/sigs.k8s.io/controller-tools/pkg/rbac/manifests.go +++ b/vendor/sigs.k8s.io/controller-tools/pkg/rbac/manifests.go @@ -30,10 +30,14 @@ import ( // ManifestOptions represent options for generating the RBAC manifests. type ManifestOptions struct { - InputDir string - OutputDir string - Name string - Labels map[string]string + InputDir string + OutputDir string + RoleFile string + BindingFile string + Name string + ServiceAccount string + Namespace string + Labels map[string]string } // SetDefaults sets up the default options for RBAC Manifest generator. @@ -41,6 +45,8 @@ func (o *ManifestOptions) SetDefaults() { o.Name = "manager" o.InputDir = filepath.Join(".", "pkg") o.OutputDir = filepath.Join(".", "config", "rbac") + o.ServiceAccount = "default" + o.Namespace = "system" } // RoleName returns the RBAC role name to be used in the manifests. @@ -48,15 +54,27 @@ func (o *ManifestOptions) RoleName() string { return o.Name + "-role" } +// RoleFileName returns the name of the manifest file to use for the role. +func (o *ManifestOptions) RoleFileName() string { + if len(o.RoleFile) == 0 { + return o.Name + "_role.yaml" + } + // TODO: validate file name + return o.RoleFile +} + // RoleBindingName returns the RBAC role binding name to be used in the manifests. func (o *ManifestOptions) RoleBindingName() string { return o.Name + "-rolebinding" } -// Namespace returns the namespace to be used in the RBAC manifests. -func (o *ManifestOptions) Namespace() string { - // TODO(droot): define this as a constant and share it with scaffold pkg. - return "system" +// RoleBindingFileName returns the name of the manifest file to use for the role binding. +func (o *ManifestOptions) RoleBindingFileName() string { + if len(o.BindingFile) == 0 { + return o.Name + "_role_binding.yaml" + } + // TODO: validate file name + return o.BindingFile } // Validate validates the input options. @@ -98,12 +116,12 @@ func Generate(o *ManifestOptions) error { if err != nil { return fmt.Errorf("failed to create output dir %v", err) } - roleManifestFile := filepath.Join(o.OutputDir, "rbac_role.yaml") + roleManifestFile := filepath.Join(o.OutputDir, o.RoleFileName()) if err := ioutil.WriteFile(roleManifestFile, roleManifest, 0666); err != nil { return fmt.Errorf("failed to write role manifest YAML file %v", err) } - roleBindingManifestFile := filepath.Join(o.OutputDir, "rbac_role_binding.yaml") + roleBindingManifestFile := filepath.Join(o.OutputDir, o.RoleBindingFileName()) if err := ioutil.WriteFile(roleBindingManifestFile, roleBindingManifest, 0666); err != nil { return fmt.Errorf("failed to write role manifest YAML file %v", err) } @@ -137,8 +155,8 @@ func getClusterRoleBindingManifest(o *ManifestOptions) ([]byte, error) { }, Subjects: []rbacv1.Subject{ { - Name: "default", - Namespace: o.Namespace(), + Name: o.ServiceAccount, + Namespace: o.Namespace, Kind: "ServiceAccount", }, }, diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/BUILD.bazel b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/BUILD.bazel index 5eb30ca2557d..d947be7dde35 100644 --- a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/BUILD.bazel +++ b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/BUILD.bazel @@ -3,21 +3,26 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "go_default_library", srcs = [ + "admission.go", + "generator.go", "manifests.go", "parser.go", + "types.go", + "writer.go", ], importmap = "sigs.k8s.io/cluster-api/vendor/sigs.k8s.io/controller-tools/pkg/webhook", importpath = "sigs.k8s.io/controller-tools/pkg/webhook", visibility = ["//visibility:public"], deps = [ "//vendor/github.com/ghodss/yaml:go_default_library", + "//vendor/github.com/spf13/afero:go_default_library", "//vendor/k8s.io/api/admissionregistration/v1beta1:go_default_library", + "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/webhook:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/webhook/types:go_default_library", "//vendor/sigs.k8s.io/controller-tools/pkg/internal/general:go_default_library", - "//vendor/sigs.k8s.io/controller-tools/pkg/webhook/internal:go_default_library", ], ) diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/admission.go b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/admission.go new file mode 100644 index 000000000000..13bd1bca4383 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/admission.go @@ -0,0 +1,89 @@ +/* +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 webhook + +import ( + "errors" + "fmt" + "regexp" + "strings" + + admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// admissionWebhook contains bits needed for generating a admissionWebhook Configuration +type admissionWebhook struct { + // name is the name of the webhook + name string + // typ is the webhook type, i.e. mutating, validating + typ webhookType + // path is the path this webhook will serve. + path string + // rules maps to the rules field in admissionregistrationv1beta1.admissionWebhook + rules []admissionregistrationv1beta1.RuleWithOperations + // failurePolicy maps to the failurePolicy field in admissionregistrationv1beta1.admissionWebhook + // This optional. If not set, will be defaulted to Ignore (fail-open) by the server. + // More details: https://github.com/kubernetes/api/blob/f5c295feaba2cbc946f0bbb8b535fc5f6a0345ee/admissionregistration/v1beta1/types.go#L144-L147 + failurePolicy *admissionregistrationv1beta1.FailurePolicyType + // namespaceSelector maps to the namespaceSelector field in admissionregistrationv1beta1.admissionWebhook + // This optional. + namespaceSelector *metav1.LabelSelector +} + +func (w *admissionWebhook) setDefaults() { + if len(w.path) == 0 { + if len(w.rules) == 0 || len(w.rules[0].Resources) == 0 { + // can't do defaulting, skip it. + return + } + if w.typ == mutatingWebhook { + w.path = "/mutate-" + w.rules[0].Resources[0] + } else if w.typ == validatingWebhook { + w.path = "/validate-" + w.rules[0].Resources[0] + } + } + if len(w.name) == 0 { + reg := regexp.MustCompile("[^a-zA-Z0-9]+") + processedPath := strings.ToLower(reg.ReplaceAllString(w.path, "")) + w.name = processedPath + ".example.com" + } +} + +var _ webhook = &admissionWebhook{} + +// GetType returns the type of the webhook. +func (w *admissionWebhook) GetType() webhookType { + return w.typ +} + +// Validate validates if the webhook is valid. +func (w *admissionWebhook) Validate() error { + if len(w.rules) == 0 { + return errors.New("field rules should not be empty") + } + if len(w.name) == 0 { + return errors.New("field name should not be empty") + } + if w.typ != mutatingWebhook && w.typ != validatingWebhook { + return fmt.Errorf("unsupported Type: %v, only mutatingWebhook and validatingWebhook are supported", w.typ) + } + if len(w.path) == 0 { + return errors.New("field path should not be empty") + } + return nil +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/generator.go b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/generator.go new file mode 100644 index 000000000000..838ab6c681c9 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/generator.go @@ -0,0 +1,334 @@ +/* +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 webhook + +import ( + "errors" + "net" + "net/url" + "path" + "sort" + "strconv" + + "k8s.io/api/admissionregistration/v1beta1" + admissionregistration "k8s.io/api/admissionregistration/v1beta1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + apitypes "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" +) + +type generatorOptions struct { + // webhooks maps a path to a webhoook. + webhooks map[string]webhook + + // port is the port number that the server will serve. + // It will be defaulted to 443 if unspecified. + port int32 + + // certDir is the directory that contains the server key and certificate. + certDir string + + // mutatingWebhookConfigName is the name that used for creating the MutatingWebhookConfiguration object. + mutatingWebhookConfigName string + // validatingWebhookConfigName is the name that used for creating the ValidatingWebhookConfiguration object. + validatingWebhookConfigName string + + // secret is the location for storing the certificate for the admission server. + // The server should have permission to create a secret in the namespace. + secret *apitypes.NamespacedName + + // service is a k8s service fronting the webhook server pod(s). + // One and only one of service and host can be set. + // This maps to field .Webhooks.ClientConfig.Service + // https://github.com/kubernetes/api/blob/183f3326a9353bd6d41430fc80f96259331d029c/admissionregistration/v1beta1/types.go#L260 + service *service + // host is the host name of .Webhooks.ClientConfig.URL + // https://github.com/kubernetes/api/blob/183f3326a9353bd6d41430fc80f96259331d029c/admissionregistration/v1beta1/types.go#L250 + // One and only one of service and host can be set. + // If neither service nor host is unspecified, host will be defaulted to "localhost". + host *string +} + +// service contains information for creating a Service +type service struct { + // name of the Service + name string + // namespace of the Service + namespace string + // selectors is the selector of the Service. + // This must select the pods that runs this webhook server. + selectors map[string]string +} + +// setDefaults does defaulting for the generatorOptions. +func (o *generatorOptions) setDefaults() { + if o.webhooks == nil { + o.webhooks = map[string]webhook{} + } + if o.port <= 0 { + o.port = 443 + } + if len(o.certDir) == 0 { + o.certDir = path.Join("/tmp", "k8s-webhook-server", "serving-certs") + } + + if len(o.mutatingWebhookConfigName) == 0 { + o.mutatingWebhookConfigName = "mutating-webhook-configuration" + } + if len(o.validatingWebhookConfigName) == 0 { + o.validatingWebhookConfigName = "validating-webhook-configuration" + } + if o.host == nil && o.service == nil { + varString := "localhost" + o.host = &varString + } +} + +// Generate creates the AdmissionWebhookConfiguration objects and Service if any. +// It also provisions the certificate for the admission server. +func (o *generatorOptions) Generate() ([]runtime.Object, error) { + // do defaulting if necessary + o.setDefaults() + + webhookConfigurations, err := o.whConfigs() + if err != nil { + return nil, err + } + svc := o.getService() + objects := append(webhookConfigurations, svc) + + return objects, nil +} + +// whConfigs creates a mutatingWebhookConfiguration and(or) a validatingWebhookConfiguration. +func (o *generatorOptions) whConfigs() ([]runtime.Object, error) { + for _, webhook := range o.webhooks { + if err := webhook.Validate(); err != nil { + return nil, err + } + } + + objs := []runtime.Object{} + mutatingWH, err := o.mutatingWHConfig() + if err != nil { + return nil, err + } + if mutatingWH != nil { + objs = append(objs, mutatingWH) + } + validatingWH, err := o.validatingWHConfigs() + if err != nil { + return nil, err + } + if validatingWH != nil { + objs = append(objs, validatingWH) + } + return objs, nil +} + +// mutatingWHConfig creates mutatingWebhookConfiguration. +func (o *generatorOptions) mutatingWHConfig() (runtime.Object, error) { + mutatingWebhooks := []v1beta1.Webhook{} + for path, webhook := range o.webhooks { + if webhook.GetType() != mutatingWebhook { + continue + } + + aw := webhook.(*admissionWebhook) + wh, err := o.admissionWebhook(path, aw) + if err != nil { + return nil, err + } + mutatingWebhooks = append(mutatingWebhooks, *wh) + } + + sort.Slice(mutatingWebhooks, func(i, j int) bool { + return mutatingWebhooks[i].Name < mutatingWebhooks[j].Name + }) + + if len(mutatingWebhooks) > 0 { + return &admissionregistration.MutatingWebhookConfiguration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: metav1.GroupVersion{Group: admissionregistration.GroupName, Version: "v1beta1"}.String(), + Kind: "MutatingWebhookConfiguration", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: o.mutatingWebhookConfigName, + Annotations: map[string]string{ + // TODO(DirectXMan12): Change the annotation to the format that cert-manager decides to use. + "alpha.admissionwebhook.cert-manager.io": "true", + }, + }, + Webhooks: mutatingWebhooks, + }, nil + } + return nil, nil +} + +func (o *generatorOptions) validatingWHConfigs() (runtime.Object, error) { + validatingWebhooks := []v1beta1.Webhook{} + for path, webhook := range o.webhooks { + var aw *admissionWebhook + if webhook.GetType() != validatingWebhook { + continue + } + + aw = webhook.(*admissionWebhook) + wh, err := o.admissionWebhook(path, aw) + if err != nil { + return nil, err + } + validatingWebhooks = append(validatingWebhooks, *wh) + } + + sort.Slice(validatingWebhooks, func(i, j int) bool { + return validatingWebhooks[i].Name < validatingWebhooks[j].Name + }) + + if len(validatingWebhooks) > 0 { + return &admissionregistration.ValidatingWebhookConfiguration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: metav1.GroupVersion{Group: admissionregistration.GroupName, Version: "v1beta1"}.String(), + Kind: "ValidatingWebhookConfiguration", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: o.validatingWebhookConfigName, + Annotations: map[string]string{ + // TODO(DirectXMan12): Change the annotation to the format that cert-manager decides to use. + "alpha.admissionwebhook.cert-manager.io": "true", + }, + }, + Webhooks: validatingWebhooks, + }, nil + } + return nil, nil +} + +func (o *generatorOptions) admissionWebhook(path string, wh *admissionWebhook) (*admissionregistration.Webhook, error) { + if wh.namespaceSelector == nil && o.service != nil && len(o.service.namespace) > 0 { + wh.namespaceSelector = &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "control-plane", + Operator: metav1.LabelSelectorOpDoesNotExist, + }, + }, + } + } + + webhook := &admissionregistration.Webhook{ + Name: wh.name, + Rules: wh.rules, + FailurePolicy: wh.failurePolicy, + NamespaceSelector: wh.namespaceSelector, + } + cc, err := o.getClientConfigWithPath(path) + if err != nil { + return nil, err + } + webhook.ClientConfig = *cc + return webhook, nil +} + +// getClientConfigWithPath constructs a WebhookClientConfig based on the server generatorOptions. +// It will use path to the set the path in WebhookClientConfig. +func (o *generatorOptions) getClientConfigWithPath(path string) (*admissionregistration.WebhookClientConfig, error) { + cc, err := o.getClientConfig() + if err != nil { + return nil, err + } + return cc, setPath(cc, path) +} + +func (o *generatorOptions) getClientConfig() (*admissionregistration.WebhookClientConfig, error) { + if o.host != nil && o.service != nil { + return nil, errors.New("URL and service can't be set at the same time") + } + cc := &admissionregistration.WebhookClientConfig{ + // Put an non-empty and not harmful CABundle here. + // Not doing this will cause the field + CABundle: []byte(`\n`), + } + if o.host != nil { + u := url.URL{ + Scheme: "https", + Host: net.JoinHostPort(*o.host, strconv.Itoa(int(o.port))), + } + urlString := u.String() + cc.URL = &urlString + } + if o.service != nil { + cc.Service = &admissionregistration.ServiceReference{ + Name: o.service.name, + Namespace: o.service.namespace, + // Path will be set later + } + } + return cc, nil +} + +// setPath sets the path in the WebhookClientConfig. +func setPath(cc *admissionregistration.WebhookClientConfig, path string) error { + if cc.URL != nil { + u, err := url.Parse(*cc.URL) + if err != nil { + return err + } + u.Path = path + urlString := u.String() + cc.URL = &urlString + } + if cc.Service != nil { + cc.Service.Path = &path + } + return nil +} + +// getService creates a corev1.Service object fronting the admission server. +func (o *generatorOptions) getService() runtime.Object { + if o.service == nil { + return nil + } + svc := &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: o.service.name, + Namespace: o.service.namespace, + Annotations: map[string]string{ + // Secret here only need name, since it will be in the same namespace as the service. + // TODO(DirectXMan12): Change the annotation to the format that cert-manager decides to use. + "alpha.service.cert-manager.io/serving-cert-secret-name": o.secret.Name, + }, + }, + Spec: corev1.ServiceSpec{ + Selector: o.service.selectors, + Ports: []corev1.ServicePort{ + { + // When using service, kube-apiserver will send admission request to port 443. + Port: 443, + TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: o.port}, + }, + }, + }, + } + return svc +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/internal/BUILD.bazel b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/internal/BUILD.bazel deleted file mode 100644 index ef482362937d..000000000000 --- a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/internal/BUILD.bazel +++ /dev/null @@ -1,27 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = [ - "client.go", - "manager.go", - ], - importmap = "sigs.k8s.io/cluster-api/vendor/sigs.k8s.io/controller-tools/pkg/webhook/internal", - importpath = "sigs.k8s.io/controller-tools/pkg/webhook/internal", - visibility = ["//visibility:public"], - deps = [ - "//vendor/github.com/ghodss/yaml:go_default_library", - "//vendor/github.com/spf13/afero:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", - "//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library", - "//vendor/k8s.io/client-go/rest:go_default_library", - "//vendor/k8s.io/client-go/tools/record:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/cache:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/client:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/manager:go_default_library", - "//vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/types:go_default_library", - ], -) diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/internal/client.go b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/internal/client.go deleted file mode 100644 index 7c043a4d2b19..000000000000 --- a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/internal/client.go +++ /dev/null @@ -1,189 +0,0 @@ -/* -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 internal - -import ( - "bytes" - "context" - "errors" - "sort" - - "github.com/ghodss/yaml" - "github.com/spf13/afero" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -var decoder = scheme.Codecs.UniversalDeserializer() - -// NewManifestClient constructs a new manifestClient. -func NewManifestClient(file string) client.Client { - return &manifestClient{ - ManifestFile: file, - fs: afero.NewOsFs(), - } -} - -// manifestClient reads from and writes to the file specified by ManifestFile. -type manifestClient struct { - ManifestFile string - - objects map[schema.GroupVersionKind][]byte - fs afero.Fs -} - -var _ client.Client = &manifestClient{} - -func (c *manifestClient) index() error { - c.objects = map[schema.GroupVersionKind][]byte{} - - _, err := c.fs.Stat(c.ManifestFile) - if err != nil { - return nil - } - - b, err := afero.ReadFile(c.fs, c.ManifestFile) - if err != nil { - return err - } - objs := bytes.Split(b, []byte("---\n")) - for _, objectB := range objs { - objB := bytes.TrimSpace(objectB) - if len(objB) == 0 { - continue - } - _, gvk, err := decoder.Decode(objB, nil, nil) - if err != nil { - return err - } - c.objects[*gvk] = objectB - } - return nil -} - -// Get read from the target file. -func (c *manifestClient) Get(ctx context.Context, key client.ObjectKey, obj runtime.Object) error { - if obj == nil { - return errors.New("obj should not be nil") - } - err := c.index() - if err != nil { - return err - } - gvk := obj.GetObjectKind().GroupVersionKind() - objectB, found := c.objects[gvk] - if !found { - return apierrors.NewNotFound(schema.GroupResource{}, key.Name) - } - _, _, err = decoder.Decode(objectB, nil, obj) - return err -} - -// List does nothing, it should not be invoked. -func (c *manifestClient) List(ctx context.Context, opts *client.ListOptions, list runtime.Object) error { - return errors.New("method List is not implemented") -} - -// Create creates an object and write it to the target file with other objects if any. -// If the object needs to be written already exists, it will error out. -func (c *manifestClient) Create(ctx context.Context, obj runtime.Object) error { - if obj == nil { - return errors.New("obj should not be nil") - } - err := c.index() - if err != nil { - return err - } - - gvk := obj.GetObjectKind().GroupVersionKind() - _, found := c.objects[gvk] - if found { - accessor, err := meta.Accessor(obj) - if err != nil { - return err - } - return apierrors.NewAlreadyExists(schema.GroupResource{}, accessor.GetName()) - } - b, err := yaml.Marshal(obj) - if err != nil { - return err - } - c.objects[gvk] = b - return c.writeObjects() -} - -// Delete does nothing, it should not be invoked. -func (c *manifestClient) Delete(ctx context.Context, obj runtime.Object, opts ...client.DeleteOptionFunc) error { - return errors.New("method Delete is not implemented") -} - -// Update replace the object if it already exists on the target file. -// Otherwise, it creates the object. -func (c *manifestClient) Update(ctx context.Context, obj runtime.Object) error { - if obj == nil { - return errors.New("obj should not be nil") - } - err := c.index() - if err != nil { - return err - } - - gvk := obj.GetObjectKind().GroupVersionKind() - m, err := yaml.Marshal(obj) - if err != nil { - return err - } - c.objects[gvk] = m - return c.writeObjects() -} - -// writeObjects writes objects to the target file in yaml format separated by `---`. -func (c *manifestClient) writeObjects() error { - needSeparator := false - buf := bytes.NewBuffer(nil) - - var gvks []schema.GroupVersionKind - for gvk := range c.objects { - gvks = append(gvks, gvk) - } - - sort.Slice(gvks, func(i, j int) bool { - return gvks[i].String() < gvks[j].String() - }) - - for _, gvk := range gvks { - if needSeparator { - buf.WriteString("---\n") - } - needSeparator = true - _, err := buf.Write(c.objects[gvk]) - if err != nil { - return err - } - } - return afero.WriteFile(c.fs, c.ManifestFile, buf.Bytes(), 0666) -} - -// Status returns a nil client.StatusWriter. -func (c *manifestClient) Status() client.StatusWriter { - return nil -} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/internal/manager.go b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/internal/manager.go deleted file mode 100644 index c4f7a5800f8d..000000000000 --- a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/internal/manager.go +++ /dev/null @@ -1,70 +0,0 @@ -/* -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 internal - -import ( - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/record" - "sigs.k8s.io/controller-runtime/pkg/cache" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission/types" -) - -// Manager is a dummy manager that does nothing. -type Manager struct{} - -var _ manager.Manager = &Manager{} - -// Add will set reqeusted dependencies on the component, and cause the component to be -// started when Start is called. Add will inject any dependencies for which the argument -// implements the inject interface - e.g. inject.Client -func (m *Manager) Add(manager.Runnable) error { return nil } - -// SetFields will set any dependencies on an object for which the object has implemented the inject -// interface - e.g. inject.Client. -func (m *Manager) SetFields(interface{}) error { return nil } - -// Start starts all registered Controllers and blocks until the Stop channel is closed. -// Returns an error if there is an error starting any controller. -func (m *Manager) Start(<-chan struct{}) error { return nil } - -// GetConfig returns an initialized Config -func (m *Manager) GetConfig() *rest.Config { return nil } - -// GetScheme returns and initialized Scheme -func (m *Manager) GetScheme() *runtime.Scheme { return nil } - -// GetAdmissionDecoder returns the runtime.Decoder based on the scheme. -func (m *Manager) GetAdmissionDecoder() types.Decoder { return nil } - -// GetClient returns a client configured with the Config -func (m *Manager) GetClient() client.Client { return nil } - -// GetFieldIndexer returns a client.FieldIndexer configured with the client -func (m *Manager) GetFieldIndexer() client.FieldIndexer { return nil } - -// GetCache returns a cache.Cache -func (m *Manager) GetCache() cache.Cache { return nil } - -// GetRecorder returns a new EventRecorder for the provided name -func (m *Manager) GetRecorder(name string) record.EventRecorder { return nil } - -// GetRESTMapper returns a RESTMapper -func (m *Manager) GetRESTMapper() meta.RESTMapper { return nil } diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/manifests.go b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/manifests.go index dc6c57a5802c..3bea5853e07c 100644 --- a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/manifests.go +++ b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/manifests.go @@ -19,90 +19,54 @@ package webhook import ( "bytes" "fmt" - "io/ioutil" - "os" "path" - "path/filepath" "strings" "text/template" "github.com/ghodss/yaml" + "github.com/spf13/afero" - "sigs.k8s.io/controller-runtime/pkg/webhook" - generateinteral "sigs.k8s.io/controller-tools/pkg/internal/general" - "sigs.k8s.io/controller-tools/pkg/webhook/internal" + "sigs.k8s.io/controller-tools/pkg/internal/general" ) -// ManifestOptions represent options for generating the webhook manifests. -type ManifestOptions struct { - InputDir string - OutputDir string - PatchOutputDir string +// Options represent options for generating the webhook manifests. +type Options struct { + // WriterOptions specifies the input and output + WriterOptions - webhooks []webhook.Webhook - svrOps *webhook.ServerOptions - svr *webhook.Server -} - -// SetDefaults sets up the default options for RBAC Manifest generator. -func (o *ManifestOptions) SetDefaults() { - o.InputDir = filepath.Join(".", "pkg", "webhook") - o.OutputDir = filepath.Join(".", "config", "webhook") - o.PatchOutputDir = filepath.Join(".", "config", "default") -} - -// Validate validates the input options. -func (o *ManifestOptions) Validate() error { - if _, err := os.Stat(o.InputDir); err != nil { - return fmt.Errorf("invalid input directory '%s' %v", o.InputDir, err) - } - return nil + generatorOptions } // Generate generates RBAC manifests by parsing the RBAC annotations in Go source // files specified in the input directory. -func Generate(o *ManifestOptions) error { - if err := o.Validate(); err != nil { - return err - } - - _, err := os.Stat(o.OutputDir) - if os.IsNotExist(err) { - err = os.MkdirAll(o.OutputDir, 0766) - if err != nil { - return err - } - } else if err != nil { +func Generate(o *Options) error { + if err := o.WriterOptions.Validate(); err != nil { return err } - o.webhooks = []webhook.Webhook{} - o.svrOps = &webhook.ServerOptions{ - Client: internal.NewManifestClient(path.Join(o.OutputDir, "webhook.yaml")), - } - err = generateinteral.ParseDir(o.InputDir, o.parseAnnotation) + err := general.ParseDir(o.InputDir, o.parseAnnotation) if err != nil { return fmt.Errorf("failed to parse the input dir: %v", err) } - o.svr, err = webhook.NewServer("generator", &internal.Manager{}, *o.svrOps) - if err != nil { - return err + if len(o.webhooks) == 0 { + return nil } - err = o.svr.Register(o.webhooks...) + + objs, err := o.Generate() if err != nil { - return fmt.Errorf("failed to process the input before generating: %v", err) + return err } - err = o.svr.InstallWebhookManifests() + err = o.WriteObjectsToDisk(objs...) if err != nil { return err } - return o.labelPatch() + return o.controllerManagerPatch() } -func (o *ManifestOptions) labelPatch() error { +func (o *Options) controllerManagerPatch() error { var kustomizeLabelPatch = `apiVersion: apps/v1 kind: StatefulSet metadata: @@ -114,13 +78,37 @@ spec: labels: {{ toYaml . | indent 8 }} {{- end }} + spec: + containers: + - name: manager + ports: + - containerPort: {{ .Port }} + name: webhook-server + protocol: TCP + volumeMounts: + - mountPath: {{ .CertDir }} + name: cert + readOnly: true + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: {{ .SecretName }} ` type KustomizeLabelPatch struct { - Labels map[string]string + Labels map[string]string + SecretName string + Port int32 + CertDir string } - p := KustomizeLabelPatch{Labels: o.svrOps.Service.Selectors} + p := KustomizeLabelPatch{ + Labels: o.service.selectors, + SecretName: o.secret.Name, + Port: o.port, + CertDir: o.certDir, + } funcMap := template.FuncMap{ "toYaml": toYAML, "indent": indent, @@ -133,7 +121,7 @@ spec: if err := temp.Execute(buf, p); err != nil { return err } - return ioutil.WriteFile(path.Join(o.PatchOutputDir, "manager_label_patch.yaml"), buf.Bytes(), 0644) + return afero.WriteFile(o.outFs, path.Join(o.PatchOutputDir, "manager_patch.yaml"), buf.Bytes(), 0644) } func toYAML(m map[string]string) (string, error) { diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/parser.go b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/parser.go index dc8a63adff4f..9ac73cef0452 100644 --- a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/parser.go +++ b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/parser.go @@ -26,9 +26,6 @@ import ( admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - webhooktypes "sigs.k8s.io/controller-runtime/pkg/webhook/types" "sigs.k8s.io/controller-tools/pkg/internal/general" ) @@ -40,7 +37,7 @@ var ( ) // parseAnnotation parses webhook annotations -func (o *ManifestOptions) parseAnnotation(commentText string) error { +func (o *Options) parseAnnotation(commentText string) error { webhookKVMap, serverKVMap := map[string]string{}, map[string]string{} for _, comment := range strings.Split(commentText, "\n") { comment := strings.TrimSpace(comment) @@ -72,12 +69,12 @@ func (o *ManifestOptions) parseAnnotation(commentText string) error { // parseWebhookAnnotation parses webhook annotations in the same comment group // nolint: gocyclo -func (o *ManifestOptions) parseWebhookAnnotation(kvMap map[string]string) error { +func (o *Options) parseWebhookAnnotation(kvMap map[string]string) error { if len(kvMap) == 0 { return nil } rule := admissionregistrationv1beta1.RuleWithOperations{} - w := &admission.Webhook{} + w := &admissionWebhook{} for key, value := range kvMap { switch key { case "groups": @@ -124,41 +121,43 @@ func (o *ManifestOptions) parseWebhookAnnotation(kvMap map[string]string) error case "type": switch strings.ToLower(value) { case "mutating": - w.Type = webhooktypes.WebhookTypeMutating + w.typ = mutatingWebhook case "validating": - w.Type = webhooktypes.WebhookTypeValidating + w.typ = validatingWebhook default: return fmt.Errorf("unknown webhook type: %v", value) } case "name": - w.Name = value + w.name = value case "path": - w.Path = value + w.path = value case "failure-policy": switch strings.ToLower(value) { case strings.ToLower(string(admissionregistrationv1beta1.Ignore)): fp := admissionregistrationv1beta1.Ignore - w.FailurePolicy = &fp + w.failurePolicy = &fp case strings.ToLower(string(admissionregistrationv1beta1.Fail)): fp := admissionregistrationv1beta1.Fail - w.FailurePolicy = &fp + w.failurePolicy = &fp default: return fmt.Errorf("unknown webhook failure policy: %v", value) } } } - w.Rules = []admissionregistrationv1beta1.RuleWithOperations{rule} - w.Handlers = []admission.Handler{admission.HandlerFunc(nil)} - o.webhooks = append(o.webhooks, w) + w.rules = []admissionregistrationv1beta1.RuleWithOperations{rule} + if o.webhooks == nil { + o.webhooks = map[string]webhook{} + } + o.webhooks[w.path] = w return nil } // parseWebhookAnnotation parses webhook server annotations in the same comment group // nolint: gocyclo -func (o *ManifestOptions) parseServerAnnotation(kvMap map[string]string) error { +func (o *Options) parseServerAnnotation(kvMap map[string]string) error { if len(kvMap) == 0 { return nil } @@ -169,71 +168,56 @@ func (o *ManifestOptions) parseServerAnnotation(kvMap map[string]string) error { if err != nil { return err } - o.svrOps.Port = int32(port) + o.port = int32(port) case "cert-dir": - o.svrOps.CertDir = value + o.certDir = value case "service": // format: <service=namespace:name> split := strings.Split(value, ":") if len(split) != 2 || len(split[0]) == 0 || len(split[1]) == 0 { return fmt.Errorf("invalid service format: expect <namespace:name>, but got %q", value) } - if o.svrOps.BootstrapOptions == nil { - o.svrOps.BootstrapOptions = &webhook.BootstrapOptions{} - } - if o.svrOps.Service == nil { - o.svrOps.Service = &webhook.Service{} + if o.service == nil { + o.service = &service{} } - o.svrOps.Service.Namespace = split[0] - o.svrOps.Service.Name = split[1] + o.service.namespace = split[0] + o.service.name = split[1] case "selector": // selector of the service. Format: <selector=label1:value1;label2:value2> split := strings.Split(value, ";") if len(split) == 0 { return fmt.Errorf("invalid selector format: expect <label1:value1;label2:value2>, but got %q", value) } - if o.svrOps.BootstrapOptions == nil { - o.svrOps.BootstrapOptions = &webhook.BootstrapOptions{} - } - if o.svrOps.Service == nil { - o.svrOps.Service = &webhook.Service{} + if o.service == nil { + o.service = &service{} } for _, v := range split { l := strings.Split(v, ":") if len(l) != 2 || len(l[0]) == 0 || len(l[1]) == 0 { return fmt.Errorf("invalid selector format: expect <label1:value1;label2:value2>, but got %q", value) } - if o.svrOps.Service.Selectors == nil { - o.svrOps.Service.Selectors = map[string]string{} + if o.service.selectors == nil { + o.service.selectors = map[string]string{} } - o.svrOps.Service.Selectors[l[0]] = l[1] + o.service.selectors[l[0]] = l[1] } case "host": if len(value) == 0 { return errors.New("host should not be empty if specified") } - if o.svrOps.BootstrapOptions == nil { - o.svrOps.BootstrapOptions = &webhook.BootstrapOptions{} - } - o.svrOps.Host = &value + o.host = &value case "mutating-webhook-config-name": if len(value) == 0 { return errors.New("mutating-webhook-config-name should not be empty if specified") } - if o.svrOps.BootstrapOptions == nil { - o.svrOps.BootstrapOptions = &webhook.BootstrapOptions{} - } - o.svrOps.MutatingWebhookConfigName = value + o.mutatingWebhookConfigName = value case "validating-webhook-config-name": if len(value) == 0 { return errors.New("validating-webhook-config-name should not be empty if specified") } - if o.svrOps.BootstrapOptions == nil { - o.svrOps.BootstrapOptions = &webhook.BootstrapOptions{} - } - o.svrOps.ValidatingWebhookConfigName = value + o.validatingWebhookConfigName = value case "secret": // format: <secret=namespace:name> @@ -241,14 +225,11 @@ func (o *ManifestOptions) parseServerAnnotation(kvMap map[string]string) error { if len(split) != 2 || len(split[0]) == 0 || len(split[1]) == 0 { return fmt.Errorf("invalid secret format: expect <namespace:name>, but got %q", value) } - if o.svrOps.BootstrapOptions == nil { - o.svrOps.BootstrapOptions = &webhook.BootstrapOptions{} - } - if o.svrOps.Secret == nil { - o.svrOps.Secret = &types.NamespacedName{} + if o.secret == nil { + o.secret = &types.NamespacedName{} } - o.svrOps.Secret.Namespace = split[0] - o.svrOps.Secret.Name = split[1] + o.secret.Namespace = split[0] + o.secret.Name = split[1] } } return nil diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/types.go b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/types.go new file mode 100644 index 000000000000..a7129e921c59 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/types.go @@ -0,0 +1,38 @@ +/* +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 webhook + +// webhookType defines the type of a webhook +type webhookType int + +const ( + _ = iota + // mutatingWebhook represents mutating type webhook + mutatingWebhook webhookType = iota + // validatingWebhook represents validating type webhook + validatingWebhook +) + +// webhook defines the basics that a webhook should support. +type webhook interface { + // GetType returns the Type of the webhook. + // e.g. mutating or validating + GetType() webhookType + // Validate validates if the webhook itself is valid. + // If invalid, a non-nil error will be returned. + Validate() error +} diff --git a/vendor/sigs.k8s.io/controller-tools/pkg/webhook/writer.go b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/writer.go new file mode 100644 index 000000000000..ecbb45f2a11f --- /dev/null +++ b/vendor/sigs.k8s.io/controller-tools/pkg/webhook/writer.go @@ -0,0 +1,92 @@ +package webhook + +import ( + "bytes" + "fmt" + "path" + "path/filepath" + + "github.com/ghodss/yaml" + "github.com/spf13/afero" + + "k8s.io/apimachinery/pkg/runtime" +) + +// WriterOptions specifies the input and output. +type WriterOptions struct { + InputDir string + OutputDir string + PatchOutputDir string + + // inFs is filesystem to be used for reading input + inFs afero.Fs + // outFs is filesystem to be used for writing out the result + outFs afero.Fs +} + +// SetDefaults sets up the default options for RBAC Manifest generator. +func (o *WriterOptions) SetDefaults() { + if o.inFs == nil { + o.inFs = afero.NewOsFs() + } + if o.outFs == nil { + o.outFs = afero.NewOsFs() + } + + if len(o.InputDir) == 0 { + o.InputDir = filepath.Join(".", "pkg", "webhook") + } + if len(o.OutputDir) == 0 { + o.OutputDir = filepath.Join(".", "config", "webhook") + } + if len(o.PatchOutputDir) == 0 { + o.PatchOutputDir = filepath.Join(".", "config", "default") + } +} + +// Validate validates the input options. +func (o *WriterOptions) Validate() error { + if _, err := o.inFs.Stat(o.InputDir); err != nil { + return fmt.Errorf("invalid input directory '%s' %v", o.InputDir, err) + } + return nil +} + +// WriteObjectsToDisk writes object to the location specified in WriterOptions. +func (o *WriterOptions) WriteObjectsToDisk(objects ...runtime.Object) error { + exists, err := afero.DirExists(o.outFs, o.OutputDir) + if err != nil { + return err + } + if !exists { + err = o.outFs.MkdirAll(o.OutputDir, 0766) + if err != nil { + return err + } + } + + var buf bytes.Buffer + isFirstObject := true + for _, obj := range objects { + if !isFirstObject { + _, err = buf.WriteString("---\n") + if err != nil { + return err + } + } + marshalled, err := yaml.Marshal(obj) + if err != nil { + return err + } + _, err = buf.Write(marshalled) + if err != nil { + return err + } + isFirstObject = false + } + err = afero.WriteFile(o.outFs, path.Join(o.OutputDir, "webhookmanifests.yaml"), buf.Bytes(), 0644) + if err != nil { + return err + } + return nil +}