From b11b0d8ddb9774841191db95241b65f75cc67a31 Mon Sep 17 00:00:00 2001 From: Jason DeTiberus Date: Wed, 13 Nov 2019 16:04:06 -0500 Subject: [PATCH 1/2] Add KubeadmControlPlane types, validations, and defaulting --- Makefile | 3 +- api/v1alpha3/kubeadm_control_plane_types.go | 127 +++ .../kubeadm_control_plane_webhook_default.go | 33 + ...ubeadm_control_plane_webhook_validation.go | 72 ++ api/v1alpha3/zz_generated.deepcopy.go | 103 +- ...cluster.x-k8s.io_kubeadmcontrolplanes.yaml | 940 ++++++++++++++++++ config/default/kustomization.yaml | 86 +- config/default/manager_webhook_patch.yaml | 23 + config/default/webhookcainjection_patch.yaml | 16 + config/webhook/kustomization.yaml | 6 + config/webhook/kustomizeconfig.yaml | 25 + config/webhook/manifests.yaml | 52 + config/webhook/service.yaml | 12 + errors/consts.go | 25 + errors/kubeadmcontrolplane.go | 30 + main.go | 56 +- scripts/ci-integration.sh | 6 + test/framework/go.sum | 12 + 18 files changed, 1588 insertions(+), 39 deletions(-) create mode 100644 api/v1alpha3/kubeadm_control_plane_types.go create mode 100644 api/v1alpha3/kubeadm_control_plane_webhook_default.go create mode 100644 api/v1alpha3/kubeadm_control_plane_webhook_validation.go create mode 100644 config/crd/bases/cluster.x-k8s.io_kubeadmcontrolplanes.yaml create mode 100644 config/default/manager_webhook_patch.yaml create mode 100644 config/default/webhookcainjection_patch.yaml create mode 100644 config/webhook/kustomization.yaml create mode 100644 config/webhook/kustomizeconfig.yaml create mode 100644 config/webhook/service.yaml create mode 100644 errors/kubeadmcontrolplane.go diff --git a/Makefile b/Makefile index b1533098ae48..bf287d77de2a 100644 --- a/Makefile +++ b/Makefile @@ -149,7 +149,8 @@ generate-manifests: $(CONTROLLER_GEN) ## Generate manifests e.g. CRD, RBAC etc. paths=./bootstrap/kubeadm/controllers/... \ crd \ rbac:roleName=manager-role \ - output:crd:dir=./config/crd/bases + output:crd:dir=./config/crd/bases \ + webhook $(CONTROLLER_GEN) \ paths=./bootstrap/kubeadm/api/... \ crd:trivialVersions=true \ diff --git a/api/v1alpha3/kubeadm_control_plane_types.go b/api/v1alpha3/kubeadm_control_plane_types.go new file mode 100644 index 000000000000..d17d3965cd4f --- /dev/null +++ b/api/v1alpha3/kubeadm_control_plane_types.go @@ -0,0 +1,127 @@ +/* +Copyright 2019 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 v1alpha3 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + cabpkv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha2" + "sigs.k8s.io/cluster-api/errors" +) + +// KubeadmControlPlaneSpec defines the desired state of KubeadmControlPlane. +type KubeadmControlPlaneSpec struct { + // Number of desired machines. Defaults to 1. When stacked etcd is used only + // odd numbers are permitted, as per [etcd best practice](https://etcd.io/docs/v3.3.12/faq/#why-an-odd-number-of-cluster-members). + // This is a pointer to distinguish between explicit zero and not specified. + // +optional + Replicas *int32 `json:"replicas,omitempty"` + + // Version defines the desired Kubernetes version. + // +kubebuilder:validation:MinLength:=1 + Version string `json:"version"` + + // InfrastructureTemplate is a required reference to a custom resource + // offered by an infrastructure provider. + InfrastructureTemplate corev1.ObjectReference `json:"infrastructureTemplate"` + + // KubeadmConfigSpec is a KubeadmConfigSpec + // to use for initializing and joining machines to the control plane. + KubeadmConfigSpec cabpkv1.KubeadmConfigSpec `json:"kubeadmConfigSpec"` +} + +// KubeadmControlPlaneStatus defines the observed state of KubeadmControlPlane. +type KubeadmControlPlaneStatus struct { + // Selector is the label selector in string format to avoid introspection + // by clients, and is used to provide the CRD-based integration for the + // scale subresource and additional integrations for things like kubectl + // describe.. The string will be in the same format as the query-param syntax. + // More info about label selectors: http://kubernetes.io/docs/user-guide/labels#label-selectors + // +optional + Selector string `json:"selector,omitempty"` + + // Total number of non-terminated machines targeted by this control plane + // (their labels match the selector). + // +optional + Replicas int32 `json:"replicas,omitempty"` + + // Total number of non-terminated machines targeted by this control plane + // that have the desired template spec. + // +optional + UpdatedReplicas int32 `json:"updatedReplicas,omitempty"` + + // Total number of fully running and ready control plane machines. + // +optional + ReadyReplicas int32 `json:"readyReplicas,omitempty"` + + // Total number of unavailable machines targeted by this control plane. + // This is the total number of machines that are still required for + // the deployment to have 100% available capacity. They may either + // be machines that are running but not yet ready or machines + // that still have not been created. + // +optional + UnavailableReplicas int32 `json:"unavailableReplicas,omitempty"` + + // Initialized denotes whether or not the control plane has the + // uploaded kubeadm-config configmap. + // +optional + Initialized bool `json:"initialized,omitempty"` + + // Ready denotes that the KubeadmControlPlane API Server is ready to + // receive requests. + // +optional + Ready bool `json:"ready,omitempty"` + + // FailureReason indicates that there is a terminal problem reconciling the + // state, and will be set to a token value suitable for + // programmatic interpretation. + // +optional + Failurereason errors.KubeadmControlPlaneStatusError `json:"failureReason,omitempty"` + + // ErrorMessage indicates that there is a terminal problem reconciling the + // state, and will be set to a descriptive error message. + // +optional + FailureMessage *string `json:"failureMessage,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:path=kubeadmcontrolplanes,shortName=kcp,scope=Namespaced,categories=cluster-api +// +kubebuilder:storageversion +// +kubebuilder:subresource:status +// +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas,selectorpath=.status.selector + +// KubeadmControlPlane is the Schema for the KubeadmControlPlane API. +type KubeadmControlPlane struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec KubeadmControlPlaneSpec `json:"spec,omitempty"` + Status KubeadmControlPlaneStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// KubeadmControlPlaneList contains a list of KubeadmControlPlane. +type KubeadmControlPlaneList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []KubeadmControlPlane `json:"items"` +} + +func init() { + SchemeBuilder.Register(&KubeadmControlPlane{}, &KubeadmControlPlaneList{}) +} diff --git a/api/v1alpha3/kubeadm_control_plane_webhook_default.go b/api/v1alpha3/kubeadm_control_plane_webhook_default.go new file mode 100644 index 000000000000..96a20f96ae4c --- /dev/null +++ b/api/v1alpha3/kubeadm_control_plane_webhook_default.go @@ -0,0 +1,33 @@ +/* +Copyright 2019 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 v1alpha3 + +import ( + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +// +kubebuilder:webhook:verbs=create;update,path=/mutate-cluster-x-k8s-io-v1alpha3-kubeadmcontrolplane,mutating=true,failurePolicy=fail,groups=cluster.x-k8s.io,resources=kubeadmcontrolplane,versions=v1alpha3,name=default.kubeadmcontrolplane.cluster.x-k8s.io + +var _ webhook.Defaulter = &KubeadmControlPlane{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *KubeadmControlPlane) Default() { + if r.Spec.Replicas == nil { + replicas := int32(1) + r.Spec.Replicas = &replicas + } +} diff --git a/api/v1alpha3/kubeadm_control_plane_webhook_validation.go b/api/v1alpha3/kubeadm_control_plane_webhook_validation.go new file mode 100644 index 000000000000..992f84d74efe --- /dev/null +++ b/api/v1alpha3/kubeadm_control_plane_webhook_validation.go @@ -0,0 +1,72 @@ +/* +Copyright 2019 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 v1alpha3 + +import ( + "reflect" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +func (r *KubeadmControlPlane) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// +kubebuilder:webhook:verbs=create;update,path=/validate-cluster-x-k8s-io-v1alpha3-kubeadmcontrolplane,mutating=false,failurePolicy=fail,groups=cluster.x-k8s.io,resources=kubeadmcontrolplane,versions=v1alpha3,name=validation.kubeadmcontrolplane.cluster.x-k8s.io + +var _ webhook.Validator = &KubeadmControlPlane{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *KubeadmControlPlane) ValidateCreate() error { + if r.Spec.Replicas == nil { + return field.Required(field.NewPath("spec", "replicas"), "is required") + } + + if *r.Spec.Replicas <= 0 { + return field.Forbidden(field.NewPath("spec", "replicas"), "cannot be less than or equal to 0") + } + + if r.Spec.KubeadmConfigSpec.InitConfiguration != nil { + if r.Spec.KubeadmConfigSpec.InitConfiguration.Etcd.External == nil { + if *r.Spec.Replicas%2 == 0 { + return field.Forbidden(field.NewPath("spec", "replicas"), "cannot be an even number when using managed etcd") + } + } + } + + return nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *KubeadmControlPlane) ValidateUpdate(old runtime.Object) error { + oldKubeadmControlPlane := old.(*KubeadmControlPlane) + if !reflect.DeepEqual(r.Spec.KubeadmConfigSpec, oldKubeadmControlPlane.Spec.KubeadmConfigSpec) { + return field.Forbidden(field.NewPath("spec", "kubeadmConfigSpec"), "cannot be modified") + } + + return nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *KubeadmControlPlane) ValidateDelete() error { + return nil +} diff --git a/api/v1alpha3/zz_generated.deepcopy.go b/api/v1alpha3/zz_generated.deepcopy.go index 69a2850bc676..80539397d2bb 100644 --- a/api/v1alpha3/zz_generated.deepcopy.go +++ b/api/v1alpha3/zz_generated.deepcopy.go @@ -23,7 +23,7 @@ package v1alpha3 import ( "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" "sigs.k8s.io/cluster-api/errors" ) @@ -212,6 +212,107 @@ func (in *ClusterStatus) DeepCopy() *ClusterStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmControlPlane) DeepCopyInto(out *KubeadmControlPlane) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlane. +func (in *KubeadmControlPlane) DeepCopy() *KubeadmControlPlane { + if in == nil { + return nil + } + out := new(KubeadmControlPlane) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KubeadmControlPlane) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmControlPlaneList) DeepCopyInto(out *KubeadmControlPlaneList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KubeadmControlPlane, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlaneList. +func (in *KubeadmControlPlaneList) DeepCopy() *KubeadmControlPlaneList { + if in == nil { + return nil + } + out := new(KubeadmControlPlaneList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KubeadmControlPlaneList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmControlPlaneSpec) DeepCopyInto(out *KubeadmControlPlaneSpec) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + out.InfrastructureTemplate = in.InfrastructureTemplate + in.KubeadmConfigSpec.DeepCopyInto(&out.KubeadmConfigSpec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlaneSpec. +func (in *KubeadmControlPlaneSpec) DeepCopy() *KubeadmControlPlaneSpec { + if in == nil { + return nil + } + out := new(KubeadmControlPlaneSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeadmControlPlaneStatus) DeepCopyInto(out *KubeadmControlPlaneStatus) { + *out = *in + if in.FailureMessage != nil { + in, out := &in.FailureMessage, &out.FailureMessage + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeadmControlPlaneStatus. +func (in *KubeadmControlPlaneStatus) DeepCopy() *KubeadmControlPlaneStatus { + if in == nil { + return nil + } + out := new(KubeadmControlPlaneStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Machine) DeepCopyInto(out *Machine) { *out = *in diff --git a/config/crd/bases/cluster.x-k8s.io_kubeadmcontrolplanes.yaml b/config/crd/bases/cluster.x-k8s.io_kubeadmcontrolplanes.yaml new file mode 100644 index 000000000000..21c870bfbbd7 --- /dev/null +++ b/config/crd/bases/cluster.x-k8s.io_kubeadmcontrolplanes.yaml @@ -0,0 +1,940 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.3 + creationTimestamp: null + name: kubeadmcontrolplanes.cluster.x-k8s.io +spec: + group: cluster.x-k8s.io + names: + categories: + - cluster-api + kind: KubeadmControlPlane + listKind: KubeadmControlPlaneList + plural: kubeadmcontrolplanes + shortNames: + - kcp + singular: kubeadmcontrolplane + preserveUnknownFields: false + scope: Namespaced + subresources: + scale: + labelSelectorPath: .status.selector + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} + validation: + openAPIV3Schema: + description: KubeadmControlPlane is the Schema for the KubeadmControlPlane API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: KubeadmControlPlaneSpec defines the desired state of KubeadmControlPlane. + properties: + infrastructureTemplate: + description: InfrastructureTemplate is a required reference to a custom + resource offered by an infrastructure provider. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + kubeadmConfigSpec: + description: KubeadmConfigSpec is a KubeadmConfigSpec to use for initializing + and joining machines to the control plane. + properties: + clusterConfiguration: + description: ClusterConfiguration along with InitConfiguration are + the configurations necessary for the init command + properties: + apiServer: + description: APIServer contains extra settings for the API server + control plane component + properties: + certSANs: + description: CertSANs sets extra Subject Alternative Names + for the API Server signing cert. + items: + type: string + type: array + extraArgs: + additionalProperties: + type: string + description: 'ExtraArgs is an extra set of flags to pass + to the control plane component. TODO: This is temporary + and ideally we would like to switch all components to + use ComponentConfig + ConfigMaps.' + type: object + extraVolumes: + description: ExtraVolumes is an extra set of host volumes, + mounted to the control plane component. + items: + description: HostPathMount contains elements describing + volumes that are mounted from the host. + properties: + hostPath: + description: HostPath is the path in the host that + will be mounted inside the pod. + type: string + mountPath: + description: MountPath is the path inside the pod + where hostPath will be mounted. + type: string + name: + description: Name of the volume inside the pod template. + type: string + pathType: + description: PathType is the type of the HostPath. + type: string + readOnly: + description: ReadOnly controls write access to the + volume + type: boolean + required: + - hostPath + - mountPath + - name + type: object + type: array + timeoutForControlPlane: + description: TimeoutForControlPlane controls the timeout + that we use for API server to appear + type: string + type: object + apiVersion: + description: 'APIVersion defines the versioned schema of this + representation of an object. Servers should convert recognized + schemas to the latest internal value, and may reject unrecognized + values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' + type: string + certificatesDir: + description: CertificatesDir specifies where to store or look + for all required certificates. + type: string + clusterName: + description: The cluster name + type: string + controlPlaneEndpoint: + description: 'ControlPlaneEndpoint sets a stable IP address + or DNS name for the control plane; it can be a valid IP address + or a RFC-1123 DNS subdomain, both with optional TCP port. + In case the ControlPlaneEndpoint is not specified, the AdvertiseAddress + + BindPort are used; in case the ControlPlaneEndpoint is specified + but without a TCP port, the BindPort is used. Possible usages + are: e.g. In a cluster with more than one control plane instances, + this field should be assigned the address of the external + load balancer in front of the control plane instances. e.g. in + environments with enforced node recycling, the ControlPlaneEndpoint + could be used for assigning a stable DNS to the control plane. + NB: This value defaults to the first value in the Cluster + object status.apiEndpoints array.' + type: string + controllerManager: + description: ControllerManager contains extra settings for the + controller manager control plane component + properties: + extraArgs: + additionalProperties: + type: string + description: 'ExtraArgs is an extra set of flags to pass + to the control plane component. TODO: This is temporary + and ideally we would like to switch all components to + use ComponentConfig + ConfigMaps.' + type: object + extraVolumes: + description: ExtraVolumes is an extra set of host volumes, + mounted to the control plane component. + items: + description: HostPathMount contains elements describing + volumes that are mounted from the host. + properties: + hostPath: + description: HostPath is the path in the host that + will be mounted inside the pod. + type: string + mountPath: + description: MountPath is the path inside the pod + where hostPath will be mounted. + type: string + name: + description: Name of the volume inside the pod template. + type: string + pathType: + description: PathType is the type of the HostPath. + type: string + readOnly: + description: ReadOnly controls write access to the + volume + type: boolean + required: + - hostPath + - mountPath + - name + type: object + type: array + type: object + dns: + description: DNS defines the options for the DNS add-on installed + in the cluster. + properties: + imageRepository: + description: ImageRepository sets the container registry + to pull images from. if not set, the ImageRepository defined + in ClusterConfiguration will be used instead. + type: string + imageTag: + description: ImageTag allows to specify a tag for the image. + In case this value is set, kubeadm does not change automatically + the version of the above components during upgrades. + type: string + type: + description: Type defines the DNS add-on to be used + type: string + required: + - type + type: object + etcd: + description: Etcd holds configuration for etcd. + properties: + external: + description: External describes how to connect to an external + etcd cluster Local and External are mutually exclusive + properties: + caFile: + description: CAFile is an SSL Certificate Authority + file used to secure etcd communication. Required if + using a TLS connection. + type: string + certFile: + description: CertFile is an SSL certification file used + to secure etcd communication. Required if using a + TLS connection. + type: string + endpoints: + description: Endpoints of etcd members. Required for + ExternalEtcd. + items: + type: string + type: array + keyFile: + description: KeyFile is an SSL key file used to secure + etcd communication. Required if using a TLS connection. + type: string + required: + - caFile + - certFile + - endpoints + - keyFile + type: object + local: + description: Local provides configuration knobs for configuring + the local etcd instance Local and External are mutually + exclusive + properties: + dataDir: + description: DataDir is the directory etcd will place + its data. Defaults to "/var/lib/etcd". + type: string + extraArgs: + additionalProperties: + type: string + description: ExtraArgs are extra arguments provided + to the etcd binary when run inside a static pod. + type: object + imageRepository: + description: ImageRepository sets the container registry + to pull images from. if not set, the ImageRepository + defined in ClusterConfiguration will be used instead. + type: string + imageTag: + description: ImageTag allows to specify a tag for the + image. In case this value is set, kubeadm does not + change automatically the version of the above components + during upgrades. + type: string + peerCertSANs: + description: PeerCertSANs sets extra Subject Alternative + Names for the etcd peer signing cert. + items: + type: string + type: array + serverCertSANs: + description: ServerCertSANs sets extra Subject Alternative + Names for the etcd server signing cert. + items: + type: string + type: array + required: + - dataDir + type: object + type: object + featureGates: + additionalProperties: + type: boolean + description: FeatureGates enabled by the user. + type: object + imageRepository: + description: ImageRepository sets the container registry to + pull images from. If empty, `k8s.gcr.io` will be used by default; + in case of kubernetes version is a CI build (kubernetes version + starts with `ci/` or `ci-cross/`) `gcr.io/kubernetes-ci-images` + will be used as a default for control plane components and + for kube-proxy, while `k8s.gcr.io` will be used for all the + other images. + type: string + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + kubernetesVersion: + description: 'KubernetesVersion is the target version of the + control plane. NB: This value defaults to the Machine object + spec.kuberentesVersion' + type: string + networking: + description: 'Networking holds configuration for the networking + topology of the cluster. NB: This value defaults to the Cluster + object spec.clusterNetwork.' + properties: + dnsDomain: + description: DNSDomain is the dns domain used by k8s services. + Defaults to "cluster.local". + type: string + podSubnet: + description: PodSubnet is the subnet used by pods. + type: string + serviceSubnet: + description: ServiceSubnet is the subnet used by k8s services. + Defaults to "10.96.0.0/12". + type: string + required: + - dnsDomain + - podSubnet + - serviceSubnet + type: object + scheduler: + description: Scheduler contains extra settings for the scheduler + control plane component + properties: + extraArgs: + additionalProperties: + type: string + description: 'ExtraArgs is an extra set of flags to pass + to the control plane component. TODO: This is temporary + and ideally we would like to switch all components to + use ComponentConfig + ConfigMaps.' + type: object + extraVolumes: + description: ExtraVolumes is an extra set of host volumes, + mounted to the control plane component. + items: + description: HostPathMount contains elements describing + volumes that are mounted from the host. + properties: + hostPath: + description: HostPath is the path in the host that + will be mounted inside the pod. + type: string + mountPath: + description: MountPath is the path inside the pod + where hostPath will be mounted. + type: string + name: + description: Name of the volume inside the pod template. + type: string + pathType: + description: PathType is the type of the HostPath. + type: string + readOnly: + description: ReadOnly controls write access to the + volume + type: boolean + required: + - hostPath + - mountPath + - name + type: object + type: array + type: object + useHyperKubeImage: + description: UseHyperKubeImage controls if hyperkube should + be used for Kubernetes components instead of their respective + separate images + type: boolean + type: object + files: + description: Files specifies extra files to be passed to user_data + upon creation. + items: + description: File defines the input for generating write_files + in cloud-init. + properties: + content: + description: Content is the actual content of the file. + type: string + encoding: + description: Encoding specifies the encoding of the file contents. + enum: + - base64 + - gzip + - gzip+base64 + type: string + owner: + description: Owner specifies the ownership of the file, e.g. + "root:root". + type: string + path: + description: Path specifies the full path on disk where to + store the file. + type: string + permissions: + description: Permissions specifies the permissions to assign + to the file, e.g. "0640". + type: string + required: + - content + - path + type: object + type: array + format: + description: Format specifies the output format of the bootstrap + data + enum: + - cloud-config + type: string + initConfiguration: + description: InitConfiguration along with ClusterConfiguration are + the configurations necessary for the init command + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this + representation of an object. Servers should convert recognized + schemas to the latest internal value, and may reject unrecognized + values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' + type: string + bootstrapTokens: + description: BootstrapTokens is respected at `kubeadm init` + time and describes a set of Bootstrap Tokens to create. This + information IS NOT uploaded to the kubeadm cluster configmap, + partly because of its sensitive nature + items: + description: BootstrapToken describes one bootstrap token, + stored as a Secret in the cluster + properties: + description: + description: Description sets a human-friendly message + why this token exists and what it's used for, so other + administrators can know its purpose. + type: string + expires: + description: Expires specifies the timestamp when this + token expires. Defaults to being set dynamically at + runtime based on the TTL. Expires and TTL are mutually + exclusive. + format: date-time + type: string + groups: + description: Groups specifies the extra groups that this + token will authenticate as when/if used for authentication + items: + type: string + type: array + token: + description: Token is used for establishing bidirectional + trust between nodes and control-planes. Used for joining + nodes in the cluster. + type: object + ttl: + description: TTL defines the time to live for this token. + Defaults to 24h. Expires and TTL are mutually exclusive. + type: string + usages: + description: Usages describes the ways in which this token + can be used. Can by default be used for establishing + bidirectional trust, but that can be changed here. + items: + type: string + type: array + required: + - token + type: object + type: array + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + localAPIEndpoint: + description: LocalAPIEndpoint represents the endpoint of the + API server instance that's deployed on this control plane + node In HA setups, this differs from ClusterConfiguration.ControlPlaneEndpoint + in the sense that ControlPlaneEndpoint is the global endpoint + for the cluster, which then loadbalances the requests to each + individual API server. This configuration object lets you + customize what IP/DNS name and port the local API server advertises + it's accessible on. By default, kubeadm tries to auto-detect + the IP of the default interface and use that, but in case + that process fails you may set the desired value here. + properties: + advertiseAddress: + description: AdvertiseAddress sets the IP address for the + API server to advertise. + type: string + bindPort: + description: BindPort sets the secure port for the API Server + to bind to. Defaults to 6443. + format: int32 + type: integer + required: + - advertiseAddress + - bindPort + type: object + nodeRegistration: + description: NodeRegistration holds fields that relate to registering + the new control-plane node to the cluster + properties: + criSocket: + description: CRISocket is used to retrieve container runtime + info. This information will be annotated to the Node API + object, for later re-use + type: string + kubeletExtraArgs: + additionalProperties: + type: string + description: KubeletExtraArgs passes through extra arguments + to the kubelet. The arguments here are passed to the kubelet + command line via the environment file kubeadm writes at + runtime for the kubelet to source. This overrides the + generic base-level configuration in the kubelet-config-1.X + ConfigMap Flags have higher priority when parsing. These + values are local and specific to the node kubeadm is executing + on. + type: object + name: + description: Name is the `.Metadata.Name` field of the Node + API object that will be created in this `kubeadm init` + or `kubeadm join` operation. This field is also used in + the CommonName field of the kubelet's client certificate + to the API server. Defaults to the hostname of the node + if not provided. + type: string + taints: + description: 'Taints specifies the taints the Node API object + should be registered with. If this field is unset, i.e. + nil, in the `kubeadm init` process it will be defaulted + to []v1.Taint{''node-role.kubernetes.io/master=""''}. + If you don''t want to taint your control-plane node, set + this field to an empty slice, i.e. `taints: {}` in the + YAML file. This field is solely used for Node registration.' + items: + description: The node this Taint is attached to has the + "effect" on any pod that does not tolerate the Taint. + properties: + effect: + description: Required. The effect of the taint on + pods that do not tolerate the taint. Valid effects + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Required. The taint key to be applied + to a node. + type: string + timeAdded: + description: TimeAdded represents the time at which + the taint was added. It is only written for NoExecute + taints. + format: date-time + type: string + value: + description: Required. The taint value corresponding + to the taint key. + type: string + required: + - effect + - key + type: object + type: array + type: object + type: object + joinConfiguration: + description: JoinConfiguration is the kubeadm configuration for + the join command + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this + representation of an object. Servers should convert recognized + schemas to the latest internal value, and may reject unrecognized + values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' + type: string + caCertPath: + description: 'CACertPath is the path to the SSL certificate + authority used to secure comunications between node and control-plane. + Defaults to "/etc/kubernetes/pki/ca.crt". TODO: revisit when + there is defaulting from k/k' + type: string + controlPlane: + description: ControlPlane defines the additional control plane + instance to be deployed on the joining node. If nil, no additional + control plane instance will be deployed. + properties: + localAPIEndpoint: + description: LocalAPIEndpoint represents the endpoint of + the API server instance to be deployed on this node. + properties: + advertiseAddress: + description: AdvertiseAddress sets the IP address for + the API server to advertise. + type: string + bindPort: + description: BindPort sets the secure port for the API + Server to bind to. Defaults to 6443. + format: int32 + type: integer + required: + - advertiseAddress + - bindPort + type: object + type: object + discovery: + description: 'Discovery specifies the options for the kubelet + to use during the TLS Bootstrap process TODO: revisit when + there is defaulting from k/k' + properties: + bootstrapToken: + description: BootstrapToken is used to set the options for + bootstrap token based discovery BootstrapToken and File + are mutually exclusive + properties: + apiServerEndpoint: + description: APIServerEndpoint is an IP or domain name + to the API server from which info will be fetched. + type: string + caCertHashes: + description: 'CACertHashes specifies a set of public + key pins to verify when token-based discovery is used. + The root CA found during discovery must match one + of these values. Specifying an empty set disables + root CA pinning, which can be unsafe. Each hash is + specified as ":", where the only currently + supported type is "sha256". This is a hex-encoded + SHA-256 hash of the Subject Public Key Info (SPKI) + object in DER-encoded ASN.1. These hashes can be calculated + using, for example, OpenSSL: openssl x509 -pubkey + -in ca.crt openssl rsa -pubin -outform der 2>&/dev/null + | openssl dgst -sha256 -hex' + items: + type: string + type: array + token: + description: Token is a token used to validate cluster + information fetched from the control-plane. + type: string + unsafeSkipCAVerification: + description: UnsafeSkipCAVerification allows token-based + discovery without CA verification via CACertHashes. + This can weaken the security of kubeadm since other + nodes can impersonate the control-plane. + type: boolean + required: + - token + - unsafeSkipCAVerification + type: object + file: + description: File is used to specify a file or URL to a + kubeconfig file from which to load cluster information + BootstrapToken and File are mutually exclusive + properties: + kubeConfigPath: + description: KubeConfigPath is used to specify the actual + file path or URL to the kubeconfig file from which + to load cluster information + type: string + required: + - kubeConfigPath + type: object + timeout: + description: Timeout modifies the discovery timeout + type: string + tlsBootstrapToken: + description: 'TLSBootstrapToken is a token used for TLS + bootstrapping. If .BootstrapToken is set, this field is + defaulted to .BootstrapToken.Token, but can be overridden. + If .File is set, this field **must be set** in case the + KubeConfigFile does not contain any other authentication + information TODO: revisit when there is defaulting from + k/k' + type: string + type: object + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + nodeRegistration: + description: NodeRegistration holds fields that relate to registering + the new control-plane node to the cluster + properties: + criSocket: + description: CRISocket is used to retrieve container runtime + info. This information will be annotated to the Node API + object, for later re-use + type: string + kubeletExtraArgs: + additionalProperties: + type: string + description: KubeletExtraArgs passes through extra arguments + to the kubelet. The arguments here are passed to the kubelet + command line via the environment file kubeadm writes at + runtime for the kubelet to source. This overrides the + generic base-level configuration in the kubelet-config-1.X + ConfigMap Flags have higher priority when parsing. These + values are local and specific to the node kubeadm is executing + on. + type: object + name: + description: Name is the `.Metadata.Name` field of the Node + API object that will be created in this `kubeadm init` + or `kubeadm join` operation. This field is also used in + the CommonName field of the kubelet's client certificate + to the API server. Defaults to the hostname of the node + if not provided. + type: string + taints: + description: 'Taints specifies the taints the Node API object + should be registered with. If this field is unset, i.e. + nil, in the `kubeadm init` process it will be defaulted + to []v1.Taint{''node-role.kubernetes.io/master=""''}. + If you don''t want to taint your control-plane node, set + this field to an empty slice, i.e. `taints: {}` in the + YAML file. This field is solely used for Node registration.' + items: + description: The node this Taint is attached to has the + "effect" on any pod that does not tolerate the Taint. + properties: + effect: + description: Required. The effect of the taint on + pods that do not tolerate the taint. Valid effects + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Required. The taint key to be applied + to a node. + type: string + timeAdded: + description: TimeAdded represents the time at which + the taint was added. It is only written for NoExecute + taints. + format: date-time + type: string + value: + description: Required. The taint value corresponding + to the taint key. + type: string + required: + - effect + - key + type: object + type: array + type: object + required: + - nodeRegistration + type: object + ntp: + description: NTP specifies NTP configuration + properties: + enabled: + description: Enabled specifies whether NTP should be enabled + type: boolean + servers: + description: Servers specifies which NTP servers to use + items: + type: string + type: array + type: object + postKubeadmCommands: + description: PostKubeadmCommands specifies extra commands to run + after kubeadm runs + items: + type: string + type: array + preKubeadmCommands: + description: PreKubeadmCommands specifies extra commands to run + before kubeadm runs + items: + type: string + type: array + users: + description: Users specifies extra users to add + items: + description: User defines the input for a generated user in cloud-init. + properties: + gecos: + description: Gecos specifies the gecos to use for the user + type: string + groups: + description: Groups specifies the additional groups for the + user + type: string + homeDir: + description: HomeDir specifies the home directory to use for + the user + type: string + inactive: + description: Inactive specifies whether to mark the user as + inactive + type: boolean + lockPassword: + description: LockPassword specifies if password login should + be disabled + type: boolean + name: + description: Name specifies the user name + type: string + passwd: + description: Passwd specifies a hashed password for the user + type: string + primaryGroup: + description: PrimaryGroup specifies the primary group for + the user + type: string + shell: + description: Shell specifies the user's shell + type: string + sshAuthorizedKeys: + description: SSHAuthorizedKeys specifies a list of ssh authorized + keys for the user + items: + type: string + type: array + sudo: + description: Sudo specifies a sudo role for the user + type: string + required: + - name + type: object + type: array + type: object + replicas: + description: Number of desired machines. Defaults to 1. When stacked + etcd is used only odd numbers are permitted, as per [etcd best practice](https://etcd.io/docs/v3.3.12/faq/#why-an-odd-number-of-cluster-members). + This is a pointer to distinguish between explicit zero and not specified. + format: int32 + type: integer + version: + description: Version defines the desired Kubernetes version. + minLength: 1 + type: string + required: + - infrastructureTemplate + - kubeadmConfigSpec + - version + type: object + status: + description: KubeadmControlPlaneStatus defines the observed state of KubeadmControlPlane. + properties: + failureMessage: + description: ErrorMessage indicates that there is a terminal problem + reconciling the state, and will be set to a descriptive error message. + type: string + failureReason: + description: FailureReason indicates that there is a terminal problem + reconciling the state, and will be set to a token value suitable for + programmatic interpretation. + type: string + initialized: + description: Initialized denotes whether or not the control plane has + the uploaded kubeadm-config configmap. + type: boolean + ready: + description: Ready denotes that the KubeadmControlPlane API Server is + ready to receive requests. + type: boolean + readyReplicas: + description: Total number of fully running and ready control plane machines. + format: int32 + type: integer + replicas: + description: Total number of non-terminated machines targeted by this + control plane (their labels match the selector). + format: int32 + type: integer + selector: + description: 'Selector is the label selector in string format to avoid + introspection by clients, and is used to provide the CRD-based integration + for the scale subresource and additional integrations for things like + kubectl describe.. The string will be in the same format as the query-param + syntax. More info about label selectors: http://kubernetes.io/docs/user-guide/labels#label-selectors' + type: string + unavailableReplicas: + description: Total number of unavailable machines targeted by this control + plane. This is the total number of machines that are still required + for the deployment to have 100% available capacity. They may either + be machines that are running but not yet ready or machines that still + have not been created. + format: int32 + type: integer + updatedReplicas: + description: Total number of non-terminated machines targeted by this + control plane that have the desired template spec. + format: int32 + type: integer + type: object + type: object + version: v1alpha3 + versions: + - name: v1alpha3 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 2d48f521f3c9..e3432ee21561 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -10,54 +10,70 @@ namespace: capi-system # field above. namePrefix: capi- +# Labels to add to all resources and selectors. +#commonLabels: +# someName: someValue + +resources: +- ../crd +- ../rbac +- ../manager # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in crd/kustomization.yaml -#- ../webhook +- ../webhook # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. -#- ../certmanager +- ../certmanager +# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. +#- ../prometheus patchesStrategicMerge: - manager_image_patch.yaml - manager_label_patch.yaml - manager_pull_policy.yaml + # Protect the /metrics endpoint by putting it behind auth. + # Only one of manager_auth_proxy_patch.yaml and + # manager_prometheus_metrics_patch.yaml should be enabled. +# - manager_auth_proxy_patch.yaml + # If you want your controller-manager to expose the /metrics + # endpoint w/o any authn/z, uncomment the following line and + # comment manager_auth_proxy_patch.yaml. + # Only one of manager_auth_proxy_patch.yaml and + # manager_prometheus_metrics_patch.yaml should be enabled. +#- manager_prometheus_metrics_patch.yaml # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in crd/kustomization.yaml -#- manager_webhook_patch.yaml +- manager_webhook_patch.yaml # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. # Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. # 'CERTMANAGER' needs to be enabled to use ca injection -#- webhookcainjection_patch.yaml +- webhookcainjection_patch.yaml # the following config is for teaching kustomize how to do var substitution -# vars: +vars: # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR -# objref: -# kind: Certificate -# group: certmanager.k8s.io -# version: v1alpha1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldref: -# fieldpath: metadata.namespace -#- name: CERTIFICATE_NAME -# objref: -# kind: Certificate -# group: certmanager.k8s.io -# version: v1alpha1 -# name: serving-cert # this name should match the one in certificate.yaml -#- name: SERVICE_NAMESPACE # namespace of the service -# objref: -# kind: Service -# version: v1 -# name: webhook-service -# fieldref: -# fieldpath: metadata.namespace -#- name: SERVICE_NAME -# objref: -# kind: Service -# version: v1 -# name: webhook-service -resources: -- ../crd -- ../rbac -- ../manager +- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR + objref: + kind: Certificate + group: cert-manager.io + version: v1alpha2 + name: serving-cert # this name should match the one in certificate.yaml + fieldref: + fieldpath: metadata.namespace +- name: CERTIFICATE_NAME + objref: + kind: Certificate + group: cert-manager.io + version: v1alpha2 + name: serving-cert # this name should match the one in certificate.yaml +- name: SERVICE_NAMESPACE # namespace of the service + objref: + kind: Service + version: v1 + name: webhook-service + fieldref: + fieldpath: metadata.namespace +- name: SERVICE_NAME + objref: + kind: Service + version: v1 + name: webhook-service diff --git a/config/default/manager_webhook_patch.yaml b/config/default/manager_webhook_patch.yaml new file mode 100644 index 000000000000..738de350b71e --- /dev/null +++ b/config/default/manager_webhook_patch.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert diff --git a/config/default/webhookcainjection_patch.yaml b/config/default/webhookcainjection_patch.yaml new file mode 100644 index 000000000000..b114287923d4 --- /dev/null +++ b/config/default/webhookcainjection_patch.yaml @@ -0,0 +1,16 @@ +# This patch add annotation to admission webhook config and +# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. +# uncomment the following lines to enable mutating webhook +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + name: mutating-webhook-configuration + annotations: + cert-manager.k8s.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + name: validating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) diff --git a/config/webhook/kustomization.yaml b/config/webhook/kustomization.yaml new file mode 100644 index 000000000000..9cf26134e4d5 --- /dev/null +++ b/config/webhook/kustomization.yaml @@ -0,0 +1,6 @@ +resources: +- manifests.yaml +- service.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml new file mode 100644 index 000000000000..25e21e3c963f --- /dev/null +++ b/config/webhook/kustomizeconfig.yaml @@ -0,0 +1,25 @@ +# the following config is for teaching kustomize where to look at when substituting vars. +# It requires kustomize v2.1.0 or newer to work properly. +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + - kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + +namespace: +- kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true +- kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true + +varReference: +- path: metadata/annotations diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index e69de29bb2d1..e500b8166839 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -0,0 +1,52 @@ + +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + creationTimestamp: null + name: mutating-webhook-configuration +webhooks: +- clientConfig: + caBundle: Cg== + service: + name: webhook-service + namespace: system + path: /mutate-cluster-x-k8s-io-v1alpha3-kubeadmcontrolplane + failurePolicy: Fail + name: default.kubeadmcontrolplane.cluster.x-k8s.io + rules: + - apiGroups: + - cluster.x-k8s.io + apiVersions: + - v1alpha3 + operations: + - CREATE + - UPDATE + resources: + - kubeadmcontrolplane + +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: validating-webhook-configuration +webhooks: +- clientConfig: + caBundle: Cg== + service: + name: webhook-service + namespace: system + path: /validate-cluster-x-k8s-io-v1alpha3-kubeadmcontrolplane + failurePolicy: Fail + name: validation.kubeadmcontrolplane.cluster.x-k8s.io + rules: + - apiGroups: + - cluster.x-k8s.io + apiVersions: + - v1alpha3 + operations: + - CREATE + - UPDATE + resources: + - kubeadmcontrolplane diff --git a/config/webhook/service.yaml b/config/webhook/service.yaml new file mode 100644 index 000000000000..525ca03cf041 --- /dev/null +++ b/config/webhook/service.yaml @@ -0,0 +1,12 @@ + +apiVersion: v1 +kind: Service +metadata: + name: webhook-service + namespace: system +spec: + ports: + - port: 443 + targetPort: webhook-server + selector: + control-plane: capi-controller-manager diff --git a/errors/consts.go b/errors/consts.go index de94f507a409..558a2797aed4 100644 --- a/errors/consts.go +++ b/errors/consts.go @@ -111,3 +111,28 @@ const ( // Example: the ProviderSpec specifies an instance type that doesn't exist. InvalidConfigurationMachineSetError MachineSetStatusError = "InvalidConfiguration" ) + +type KubeadmControlPlaneStatusError string + +const ( + // InvalidConfigurationKubeadmControlPlaneError indicates that the kubeadm control plane + // configuration is invalid. + InvalidConfigurationKubeadmControlPlaneError KubeadmControlPlaneStatusError = "InvalidConfiguration" + + // UnsupportedChangeKubeadmControlPlaneError indicates that the kubeadm control plane + // spec has been updated in an unsupported way that cannot be + // reconciled. + UnsupportedChangeKubeadmControlPlaneError KubeadmControlPlaneStatusError = "UnsupportedChange" + + // CreateKubeadmControlPlaneError indicates that an error was encountered + // when trying to create the kubeadm control plane. + CreateKubeadmControlPlaneError KubeadmControlPlaneStatusError = "CreateError" + + // UpdateKubeadmControlPlaneError indicates that an error was encountered + // when trying to update the kubeadm control plane. + UpdateKubeadmControlPlaneError KubeadmControlPlaneStatusError = "UpdateError" + + // DeleteKubeadmControlPlaneError indicates that an error was encountered + // when trying to delete the kubeadm control plane. + DeleteKubeadmControlPlaneError KubeadmControlPlaneStatusError = "DeleteError" +) diff --git a/errors/kubeadmcontrolplane.go b/errors/kubeadmcontrolplane.go new file mode 100644 index 000000000000..f45accdcc271 --- /dev/null +++ b/errors/kubeadmcontrolplane.go @@ -0,0 +1,30 @@ +/* +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 errors + +// KubeadmControlPlaneError is a more descriptive kind of error that represents an error condition that +// should be set in the KubeadmControlPlane.Status. The "Reason" field is meant for short, +// enum-style constants meant to be interpreted by control planes. The "Message" +// field is meant to be read by humans. +type KubeadmControlPlaneError struct { + Reason KubeadmControlPlaneStatusError + Message string +} + +func (e *KubeadmControlPlaneError) Error() string { + return e.Message +} diff --git a/main.go b/main.go index 7134736e1aa8..04cf90f5c629 100644 --- a/main.go +++ b/main.go @@ -65,6 +65,7 @@ func main() { machineDeploymentConcurrency int kubeadmConfigConcurrency int syncPeriod time.Duration + webhookPort int ) flag.StringVar(&metricsAddr, "metrics-addr", ":8080", @@ -101,6 +102,10 @@ func main() { "The amount of time the bootstrap token will be valid", ) + flag.IntVar(&webhookPort, "webhook-port", 9443, + "Webhook server port (set to 0 to disable)", + ) + flag.Parse() ctrl.SetLogger(klogr.New()) @@ -154,17 +159,64 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "MachineDeployment") os.Exit(1) } - // +kubebuilder:scaffold:builder // Kubeadm controllers. if err = (&kubeadmcontrollers.KubeadmConfigReconciler{ Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("KubeadmConfigReconciler"), + Log: ctrl.Log.WithName("controllers").WithName("KubeadmConfig"), }).SetupWithManager(mgr, concurrency(kubeadmConfigConcurrency)); err != nil { setupLog.Error(err, "unable to create controller", "controller", "KubeadmConfig") os.Exit(1) } + if webhookPort != 0 { + if err = (&clusterv1alpha2.Cluster{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "Cluster") + os.Exit(1) + } + + if err = (&clusterv1alpha2.ClusterList{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "ClusterList") + os.Exit(1) + } + + if err = (&clusterv1alpha2.Machine{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "Machine") + os.Exit(1) + } + + if err = (&clusterv1alpha2.MachineList{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "MachineList") + os.Exit(1) + } + + if err = (&clusterv1alpha2.MachineSet{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "MachineSet") + os.Exit(1) + } + + if err = (&clusterv1alpha2.MachineSetList{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "MachineSetList") + os.Exit(1) + } + + if err = (&clusterv1alpha2.MachineDeployment{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "MachineDeployment") + os.Exit(1) + } + + if err = (&clusterv1alpha2.MachineDeploymentList{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "MachineDeploymentList") + os.Exit(1) + } + + if err = (&clusterv1alpha3.KubeadmControlPlane{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "KubeadmControlPlane") + os.Exit(1) + } + } + // +kubebuilder:scaffold:builder + setupLog.Info("starting manager") if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { setupLog.Error(err, "problem running manager") diff --git a/scripts/ci-integration.sh b/scripts/ci-integration.sh index 1a0d90f89ef2..e4c50a70d4b9 100755 --- a/scripts/ci-integration.sh +++ b/scripts/ci-integration.sh @@ -30,6 +30,7 @@ CRD_YAML="crd.yaml" BOOTSTRAP_CLUSTER_NAME="clusterapi-bootstrap" CONTROLLER_REPO="controller-ci" # use arbitrary repo name since we don't need to publish it EXAMPLE_PROVIDER_REPO="example-provider-ci" +CERT_MANAGER_URL="https://github.com/jetstack/cert-manager/releases/download/v0.11.0/cert-manager.yaml" GOOS=$(go env GOOS) GOARCH=$(go env GOARCH) @@ -131,6 +132,11 @@ main() { prepare_crd_yaml create_bootstrap + kubectl create -f "${CERT_MANAGER_URL}" + set +e + wait_deployment_available "cert-manager-webhook" "cert-manager" + set -e + kubectl create -f "${CRD_YAML}" set +e diff --git a/test/framework/go.sum b/test/framework/go.sum index 556737a5e5a7..c868b5890192 100644 --- a/test/framework/go.sum +++ b/test/framework/go.sum @@ -13,6 +13,7 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -76,6 +77,7 @@ github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 h1:u4bArs140e9+AfE52mFHOXVFnOSBJBRlzTHrOPLOIhE= github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -88,6 +90,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= @@ -100,6 +103,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/ github.com/grpc-ecosystem/go-grpc-prometheus v0.0.0-20170330212424-2500245aa611/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -126,6 +130,7 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -159,14 +164,18 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -254,6 +263,7 @@ golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= @@ -291,6 +301,7 @@ k8s.io/apimachinery v0.0.0-20190817020851-f2f3a405f61d/go.mod h1:3jediapYqJ2w1BF k8s.io/apiserver v0.0.0-20190918200908-1e17798da8c1/go.mod h1:4FuDU+iKPjdsdQSN3GsEKZLB/feQsj1y9dhhBDVV2Ns= k8s.io/client-go v0.0.0-20190918200256-06eb1244587a h1:huOvPq1vO7dkuw9rZPYsLGpFmyGvy6L8q6mDItgkdQ4= k8s.io/client-go v0.0.0-20190918200256-06eb1244587a/go.mod h1:3YAcTbI2ArBRmhHns5vlHRX8YQqvkVYpz+U/N5i1mVU= +k8s.io/cluster-bootstrap v0.0.0-20190516232516-d7d78ab2cfe7 h1:5wvjieVoU4oovHlkeD256q2M2YYi2P01zk6wxSR2zk0= k8s.io/cluster-bootstrap v0.0.0-20190516232516-d7d78ab2cfe7/go.mod h1:iBSm2nwo3OaiuW8VDvc3ySDXK5SKfUrxwPvBloKG7zg= k8s.io/code-generator v0.0.0-20190612205613-18da4a14b22b/go.mod h1:G8bQwmHm2eafm5bgtX67XDZQ8CWKSGu9DekI+yN4Y5I= k8s.io/component-base v0.0.0-20190918200425-ed2f0867c778/go.mod h1:DFWQCXgXVLiWtzFaS17KxHdlUeUymP7FLxZSkmL9/jU= @@ -300,6 +311,7 @@ k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30 h1:TRb4wNWoBVrH9plmkp2q86FIDppkbrEXdXlxU3a3BMI= k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5 h1:VBM/0P5TWxwk+Nw6Z+lAw3DKgO76g90ETOiA6rfLV1Y= From bf2c98ff87315d4ca03572d2d448d59b01f0c902 Mon Sep 17 00:00:00 2001 From: Andy Goldstein Date: Tue, 19 Nov 2019 18:01:02 -0800 Subject: [PATCH 2/2] Plumb webhookPort through to the manager Signed-off-by: Andy Goldstein --- main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/main.go b/main.go index 04cf90f5c629..7d4ca8aec79c 100644 --- a/main.go +++ b/main.go @@ -125,6 +125,7 @@ func main() { Namespace: watchNamespace, SyncPeriod: &syncPeriod, NewClient: newClientFunc, + Port: webhookPort, }) if err != nil { setupLog.Error(err, "unable to start manager")