From f92026c6062b861fc8c1dee8a9492f9d94301c5a Mon Sep 17 00:00:00 2001 From: f10atin9 Date: Wed, 6 Apr 2022 16:44:32 +0800 Subject: [PATCH] add accessor Signed-off-by: f10atin9 --- cmd/controller-manager/app/server.go | 1 + config/ks-core/templates/webhook.yaml | 34 +++ go.mod | 9 +- go.sum | 2 + .../network/webhooks/storageclass_accessor.go | 30 +++ .../kubesphere/storageclass-accessor/LICENSE | 201 +++++++++++++++++ .../apis/accessor/v1alpha1/accessor_types.go | 100 +++++++++ .../accessor/v1alpha1/groupversion_info.go | 36 +++ .../v1alpha1/zz_generated.deepcopy.go | 212 ++++++++++++++++++ .../webhook/certwatcher.go | 148 ++++++++++++ .../storageclass-accessor/webhook/convert.go | 15 ++ .../storageclass-accessor/webhook/pvc.go | 102 +++++++++ .../storageclass-accessor/webhook/scheme.go | 27 +++ .../storageclass-accessor/webhook/selector.go | 94 ++++++++ .../webhook/validation.go | 129 +++++++++++ .../storageclass-accessor/webhook/weebhook.go | 166 ++++++++++++++ vendor/modules.txt | 12 +- 17 files changed, 1310 insertions(+), 8 deletions(-) create mode 100644 pkg/controller/network/webhooks/storageclass_accessor.go create mode 100644 vendor/github.com/kubesphere/storageclass-accessor/LICENSE create mode 100644 vendor/github.com/kubesphere/storageclass-accessor/client/apis/accessor/v1alpha1/accessor_types.go create mode 100644 vendor/github.com/kubesphere/storageclass-accessor/client/apis/accessor/v1alpha1/groupversion_info.go create mode 100644 vendor/github.com/kubesphere/storageclass-accessor/client/apis/accessor/v1alpha1/zz_generated.deepcopy.go create mode 100644 vendor/github.com/kubesphere/storageclass-accessor/webhook/certwatcher.go create mode 100644 vendor/github.com/kubesphere/storageclass-accessor/webhook/convert.go create mode 100644 vendor/github.com/kubesphere/storageclass-accessor/webhook/pvc.go create mode 100644 vendor/github.com/kubesphere/storageclass-accessor/webhook/scheme.go create mode 100644 vendor/github.com/kubesphere/storageclass-accessor/webhook/selector.go create mode 100644 vendor/github.com/kubesphere/storageclass-accessor/webhook/validation.go create mode 100644 vendor/github.com/kubesphere/storageclass-accessor/webhook/weebhook.go diff --git a/cmd/controller-manager/app/server.go b/cmd/controller-manager/app/server.go index bbd03b139..654a2b154 100644 --- a/cmd/controller-manager/app/server.go +++ b/cmd/controller-manager/app/server.go @@ -226,6 +226,7 @@ func run(s *options.KubeSphereControllerManagerOptions, ctx context.Context) err hookServer.Register("/validate-email-iam-kubesphere-io-v1alpha2", &webhook.Admission{Handler: &user.EmailValidator{Client: mgr.GetClient()}}) hookServer.Register("/validate-network-kubesphere-io-v1alpha1", &webhook.Admission{Handler: &webhooks.ValidatingHandler{C: mgr.GetClient()}}) hookServer.Register("/mutate-network-kubesphere-io-v1alpha1", &webhook.Admission{Handler: &webhooks.MutatingHandler{C: mgr.GetClient()}}) + hookServer.Register("/persistentvolumeclaims", &webhook.Admission{Handler: &webhooks.AccessorHandler{C: mgr.GetClient()}}) resourceQuotaAdmission, err := quota.NewResourceQuotaAdmission(mgr.GetClient(), mgr.GetScheme()) if err != nil { diff --git a/config/ks-core/templates/webhook.yaml b/config/ks-core/templates/webhook.yaml index 245f20966..68ada8545 100644 --- a/config/ks-core/templates/webhook.yaml +++ b/config/ks-core/templates/webhook.yaml @@ -120,3 +120,37 @@ webhooks: - pods scope: '*' sideEffects: None + +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: storageclass-accessor.storage.kubesphere.io +webhooks: + - admissionReviewVersions: + - v1beta1 + clientConfig: + caBundle: {{ b64enc $ca.Cert | quote }} + service: + name: ks-controller-manager + namespace: {{ .Release.Namespace }} + path: /persistentvolumeclaims + port: 443 + failurePolicy: Ignore + matchPolicy: Exact + name: storageclass-accessor.storage.kubesphere.io + namespaceSelector: {} + objectSelector: {} + rules: + - apiGroups: + - '*' + apiVersions: + - '*' + operations: + - CREATE + - UPDATE + - DELETE + resources: + - persistentvolumeclaims + scope: '*' + sideEffects: None \ No newline at end of file diff --git a/go.mod b/go.mod index b3f535bac..361d9f63a 100644 --- a/go.mod +++ b/go.mod @@ -61,6 +61,7 @@ require ( github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 github.com/kubesphere/pvc-autoresizer v0.1.1 github.com/kubesphere/sonargo v0.0.2 + github.com/kubesphere/storageclass-accessor v0.2.0 github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/mattn/go-runewidth v0.0.4 // indirect @@ -107,16 +108,16 @@ require ( istio.io/api v0.0.0-20201113182140-d4b7e3fc2b44 istio.io/client-go v0.0.0-20201113183938-0734e976e785 istio.io/gogo-genproto v0.0.0-20201113182723-5b8563d8a012 // indirect - k8s.io/api v0.21.4 + k8s.io/api v0.22.1 k8s.io/apiextensions-apiserver v0.21.4 - k8s.io/apimachinery v0.21.4 + k8s.io/apimachinery v0.22.1 k8s.io/apiserver v0.21.2 k8s.io/cli-runtime v0.21.2 k8s.io/client-go v12.0.0+incompatible k8s.io/code-generator v0.21.2 k8s.io/component-base v0.21.4 k8s.io/klog v1.0.0 - k8s.io/klog/v2 v2.8.0 + k8s.io/klog/v2 v2.9.0 k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e k8s.io/kubectl v0.21.2 k8s.io/metrics v0.21.2 @@ -126,7 +127,7 @@ require ( kubesphere.io/monitoring-dashboard v0.2.2 rsc.io/letsencrypt v0.0.1 // indirect sigs.k8s.io/application v0.8.4-0.20201016185654-c8e2959e57a0 - sigs.k8s.io/controller-runtime v0.9.8-0.20211019125639-aa2b3e68a52d + sigs.k8s.io/controller-runtime v0.10.0 sigs.k8s.io/controller-tools v0.6.2 sigs.k8s.io/kubefed v0.8.1 sigs.k8s.io/kustomize/api v0.8.8 diff --git a/go.sum b/go.sum index 5d8b6f0d2..6374f9975 100644 --- a/go.sum +++ b/go.sum @@ -525,6 +525,8 @@ github.com/kubesphere/pvc-autoresizer v0.1.1 h1:Q0VrvLfTiE1f38EvmFpJdBevwN21X7Br github.com/kubesphere/pvc-autoresizer v0.1.1/go.mod h1:88qz9L1Ov2bvw7L/i5mUT8g5DvBwRCZ60JA2d1WLgB0= github.com/kubesphere/sonargo v0.0.2 h1:hsSRE3sv3mkPcUAeSABdp7rtfcNW2zzeHXzFa01CTkU= github.com/kubesphere/sonargo v0.0.2/go.mod h1:ww8n9ANlDXhX5PBZ18iaRnCgEkXN0GMml3/KZXOZ11w= +github.com/kubesphere/storageclass-accessor v0.2.0 h1:rnzKafhneo8160dh6REm3z1yAEaQWz1x/Lwi3QFVLWE= +github.com/kubesphere/storageclass-accessor v0.2.0/go.mod h1:jqZ3tCiw09yOiPkZ3rDmf6QIpbZJx55McnyRaS0ayCY= github.com/kylelemons/go-gypsy v0.0.0-20160905020020-08cad365cd28/go.mod h1:T/T7jsxVqf9k/zYOqbgNAsANsjxTd1Yq3htjDhQ1H0c= github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= diff --git a/pkg/controller/network/webhooks/storageclass_accessor.go b/pkg/controller/network/webhooks/storageclass_accessor.go new file mode 100644 index 000000000..2ba20b154 --- /dev/null +++ b/pkg/controller/network/webhooks/storageclass_accessor.go @@ -0,0 +1,30 @@ +package webhooks + +import ( + "context" + + accessor "github.com/kubesphere/storageclass-accessor/webhook" + v1 "k8s.io/api/admission/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +type AccessorHandler struct { + C client.Client + decoder *admission.Decoder +} + +func (h *AccessorHandler) InjectDecoder(d *admission.Decoder) error { + h.decoder = d + return nil +} + +func (h *AccessorHandler) Handle(ctx context.Context, req admission.Request) admission.Response { + review := v1.AdmissionReview{ + Request: &req.AdmissionRequest, + } + resp := accessor.AdmitPVC(review) + return admission.Response{ + AdmissionResponse: *resp, + } +} diff --git a/vendor/github.com/kubesphere/storageclass-accessor/LICENSE b/vendor/github.com/kubesphere/storageclass-accessor/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/vendor/github.com/kubesphere/storageclass-accessor/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/kubesphere/storageclass-accessor/client/apis/accessor/v1alpha1/accessor_types.go b/vendor/github.com/kubesphere/storageclass-accessor/client/apis/accessor/v1alpha1/accessor_types.go new file mode 100644 index 000000000..816136683 --- /dev/null +++ b/vendor/github.com/kubesphere/storageclass-accessor/client/apis/accessor/v1alpha1/accessor_types.go @@ -0,0 +1,100 @@ +/* +Copyright 2021 f10atin9. + +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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:validation:Enum=In;NotIn + +type Operator string + +// +kubebuilder:validation:Enum=Name;Status + +type Field string + +const ( + In Operator = "In" + NotIn Operator = "NotIn" + + Name Field = "Name" + Status Field = "Status" +) + +// AccessorSpec defines the desired state of Accessor +type AccessorSpec struct { + StorageClassName string `json:"storageClassName"` + NameSpaceSelector NameSpaceList `json:"namespaceSelector"` + WorkSpaceSelector WorkSpaceList `json:"workspaceSelector"` +} + +type NameSpaceList struct { + LabelSelector []MatchExpressions `json:"labelSelector"` + FieldSelector []FieldExpressions `json:"fieldSelector"` +} + +type FieldExpressions struct { + FieldExpressions []FieldExpression `json:"fieldExpressions"` +} +type MatchExpressions struct { + MatchExpressions []MatchExpression `json:"matchExpressions"` +} + +type MatchExpression struct { + Key string `json:"key"` + Operator Operator `json:"operator"` + Values []string `json:"values"` +} + +type FieldExpression struct { + Field Field `json:"field"` + Operator Operator `json:"operator"` + Values []string `json:"values"` +} + +type WorkSpaceList struct { + LabelSelector []MatchExpressions `json:"labelSelector"` + FieldSelector []FieldExpressions `json:"fieldSelector"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// Accessor is the Schema for the accessors API +// +kubebuilder:resource:scope=Cluster +// +kubebuilder:printcolumn:name="StorageClass",type=string,JSONPath=`.spec.storageClassName` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` +type Accessor struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec AccessorSpec `json:"spec,omitempty"` +} + +//+kubebuilder:object:root=true + +// AccessorList contains a list of Accessor +type AccessorList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Accessor `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Accessor{}, &AccessorList{}) +} diff --git a/vendor/github.com/kubesphere/storageclass-accessor/client/apis/accessor/v1alpha1/groupversion_info.go b/vendor/github.com/kubesphere/storageclass-accessor/client/apis/accessor/v1alpha1/groupversion_info.go new file mode 100644 index 000000000..230bcc3ff --- /dev/null +++ b/vendor/github.com/kubesphere/storageclass-accessor/client/apis/accessor/v1alpha1/groupversion_info.go @@ -0,0 +1,36 @@ +/* +Copyright 2021 f10atin9. + +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 v1alpha1 contains API Schema definitions for the storage v1alpha1 API group +//+kubebuilder:object:generate=true +//+groupName=storage.kubesphere.io +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "storage.kubesphere.io", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/vendor/github.com/kubesphere/storageclass-accessor/client/apis/accessor/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/kubesphere/storageclass-accessor/client/apis/accessor/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 000000000..413ce1067 --- /dev/null +++ b/vendor/github.com/kubesphere/storageclass-accessor/client/apis/accessor/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,212 @@ +// +build !ignore_autogenerated + +/* +Copyright 2021 f10atin9. + +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. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Accessor) DeepCopyInto(out *Accessor) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Accessor. +func (in *Accessor) DeepCopy() *Accessor { + if in == nil { + return nil + } + out := new(Accessor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Accessor) 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 *AccessorList) DeepCopyInto(out *AccessorList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Accessor, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AccessorList. +func (in *AccessorList) DeepCopy() *AccessorList { + if in == nil { + return nil + } + out := new(AccessorList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AccessorList) 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 *AccessorSpec) DeepCopyInto(out *AccessorSpec) { + *out = *in + in.NameSpaceSelector.DeepCopyInto(&out.NameSpaceSelector) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AccessorSpec. +func (in *AccessorSpec) DeepCopy() *AccessorSpec { + if in == nil { + return nil + } + out := new(AccessorSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FieldExpression) DeepCopyInto(out *FieldExpression) { + *out = *in + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FieldExpression. +func (in *FieldExpression) DeepCopy() *FieldExpression { + if in == nil { + return nil + } + out := new(FieldExpression) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FieldExpressions) DeepCopyInto(out *FieldExpressions) { + *out = *in + if in.FieldExpressions != nil { + in, out := &in.FieldExpressions, &out.FieldExpressions + *out = make([]FieldExpression, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FieldExpressions. +func (in *FieldExpressions) DeepCopy() *FieldExpressions { + if in == nil { + return nil + } + out := new(FieldExpressions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MatchExpression) DeepCopyInto(out *MatchExpression) { + *out = *in + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchExpression. +func (in *MatchExpression) DeepCopy() *MatchExpression { + if in == nil { + return nil + } + out := new(MatchExpression) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MatchExpressions) DeepCopyInto(out *MatchExpressions) { + *out = *in + if in.MatchExpressions != nil { + in, out := &in.MatchExpressions, &out.MatchExpressions + *out = make([]MatchExpression, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchExpressions. +func (in *MatchExpressions) DeepCopy() *MatchExpressions { + if in == nil { + return nil + } + out := new(MatchExpressions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NameSpaceList) DeepCopyInto(out *NameSpaceList) { + *out = *in + if in.LabelSelector != nil { + in, out := &in.LabelSelector, &out.LabelSelector + *out = make([]MatchExpressions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.FieldSelector != nil { + in, out := &in.FieldSelector, &out.FieldSelector + *out = make([]FieldExpressions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NameSpaceList. +func (in *NameSpaceList) DeepCopy() *NameSpaceList { + if in == nil { + return nil + } + out := new(NameSpaceList) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/kubesphere/storageclass-accessor/webhook/certwatcher.go b/vendor/github.com/kubesphere/storageclass-accessor/webhook/certwatcher.go new file mode 100644 index 000000000..e7c7da16d --- /dev/null +++ b/vendor/github.com/kubesphere/storageclass-accessor/webhook/certwatcher.go @@ -0,0 +1,148 @@ +package webhook + +import ( + "context" + "crypto/tls" + "sync" + + "github.com/fsnotify/fsnotify" + "k8s.io/klog/v2" +) + +// This file originated from github.com/kubernetes-sigs/controller-runtime/pkg/webhook/internal/certwatcher. +// We cannot import this package as it's an internal one. In addition, we cannot yet easily integrate +// with controller-runtime/pkg/webhook directly, as it would require extensive rework: +// https://github.com/kubernetes-csi/external-snapshotter/issues/422 + +// CertWatcher watches certificate and key files for changes. When either file +// changes, it reads and parses both and calls an optional callback with the new +// certificate. +type CertWatcher struct { + sync.Mutex + + currentCert *tls.Certificate + watcher *fsnotify.Watcher + + certPath string + keyPath string +} + +// NewCertWatcher returns a new CertWatcher watching the given certificate and key. +func NewCertWatcher(certPath, keyPath string) (*CertWatcher, error) { + var err error + + cw := &CertWatcher{ + certPath: certPath, + keyPath: keyPath, + } + + // Initial read of certificate and key. + if err := cw.ReadCertificate(); err != nil { + return nil, err + } + + cw.watcher, err = fsnotify.NewWatcher() + if err != nil { + return nil, err + } + + return cw, nil +} + +// GetCertificate fetches the currently loaded certificate, which may be nil. +func (cw *CertWatcher) GetCertificate(_ *tls.ClientHelloInfo) (*tls.Certificate, error) { + cw.Lock() + defer cw.Unlock() + return cw.currentCert, nil +} + +// Start starts the watch on the certificate and key files. +func (cw *CertWatcher) Start(ctx context.Context) error { + files := []string{cw.certPath, cw.keyPath} + + for _, f := range files { + if err := cw.watcher.Add(f); err != nil { + return err + } + } + + go cw.Watch() + + // Block until the context is done. + <-ctx.Done() + + return cw.watcher.Close() +} + +// Watch reads events from the watcher's channel and reacts to changes. +func (cw *CertWatcher) Watch() { + for { + select { + case event, ok := <-cw.watcher.Events: + // Channel is closed. + if !ok { + return + } + + cw.handleEvent(event) + + case err, ok := <-cw.watcher.Errors: + // Channel is closed. + if !ok { + return + } + + klog.Error(err, "certificate watch error") + } + } +} + +// ReadCertificate reads the certificate and key files from disk, parses them, +// and updates the current certificate on the watcher. If a callback is set, it +// is invoked with the new certificate. +func (cw *CertWatcher) ReadCertificate() error { + cert, err := tls.LoadX509KeyPair(cw.certPath, cw.keyPath) + if err != nil { + return err + } + + cw.Lock() + cw.currentCert = &cert + cw.Unlock() + + klog.Info("Updated current TLS certificate") + + return nil +} + +func (cw *CertWatcher) handleEvent(event fsnotify.Event) { + // Only care about events which may modify the contents of the file. + if !(isWrite(event) || isRemove(event) || isCreate(event)) { + return + } + + klog.V(1).Info("certificate event", "event", event) + + // If the file was removed, re-add the watch. + if isRemove(event) { + if err := cw.watcher.Add(event.Name); err != nil { + klog.Error(err, "error re-watching file") + } + } + + if err := cw.ReadCertificate(); err != nil { + klog.Error(err, "error re-reading certificate") + } +} + +func isWrite(event fsnotify.Event) bool { + return event.Op&fsnotify.Write == fsnotify.Write +} + +func isCreate(event fsnotify.Event) bool { + return event.Op&fsnotify.Create == fsnotify.Create +} + +func isRemove(event fsnotify.Event) bool { + return event.Op&fsnotify.Remove == fsnotify.Remove +} diff --git a/vendor/github.com/kubesphere/storageclass-accessor/webhook/convert.go b/vendor/github.com/kubesphere/storageclass-accessor/webhook/convert.go new file mode 100644 index 000000000..976751c34 --- /dev/null +++ b/vendor/github.com/kubesphere/storageclass-accessor/webhook/convert.go @@ -0,0 +1,15 @@ +package webhook + +import ( + v1 "k8s.io/api/admission/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func toV1AdmissionResponse(err error) *v1.AdmissionResponse { + return &v1.AdmissionResponse{ + Allowed: false, + Result: &metav1.Status{ + Message: err.Error(), + }, + } +} diff --git a/vendor/github.com/kubesphere/storageclass-accessor/webhook/pvc.go b/vendor/github.com/kubesphere/storageclass-accessor/webhook/pvc.go new file mode 100644 index 000000000..ab0073801 --- /dev/null +++ b/vendor/github.com/kubesphere/storageclass-accessor/webhook/pvc.go @@ -0,0 +1,102 @@ +package webhook + +import ( + "context" + admissionv1 "k8s.io/api/admission/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" +) + +type ReqInfo struct { + resource string + name string + namespace string + operator string + storageClassName string +} + +var reviewResponse = &admissionv1.AdmissionResponse{ + Allowed: true, + Result: &metav1.Status{}, +} + +func AdmitPVC(ar admissionv1.AdmissionReview) *admissionv1.AdmissionResponse { + klog.Info("admitting pvc") + + if !(ar.Request.Operation == admissionv1.Delete || ar.Request.Operation == admissionv1.Create) { + return reviewResponse + } + + raw := ar.Request.Object.Raw + + var newPVC *corev1.PersistentVolumeClaim + + switch ar.Request.Operation { + case admissionv1.Create: + deserializer := codecs.UniversalDeserializer() + pvc := &corev1.PersistentVolumeClaim{} + obj, _, err := deserializer.Decode(raw, nil, pvc) + if err != nil { + klog.Error(err) + return toV1AdmissionResponse(err) + } + var ok bool + newPVC, ok = obj.(*corev1.PersistentVolumeClaim) + if !ok { + klog.Error("obj can't exchange to pvc object") + return toV1AdmissionResponse(err) + } + case admissionv1.Delete: + pvcInfo := types.NamespacedName{ + Namespace: ar.Request.Namespace, + Name: ar.Request.Name, + } + cli, err := client.New(config.GetConfigOrDie(), client.Options{}) + if err != nil { + return toV1AdmissionResponse(err) + } + targetPVC := &corev1.PersistentVolumeClaim{} + err = cli.Get(context.Background(), pvcInfo, targetPVC) + if err != nil { + klog.Error("get target Delete PVC from client failed, err:", err) + return toV1AdmissionResponse(err) + } + newPVC = targetPVC + } + + reqPVC := ReqInfo{ + resource: "persistentVolumeClaim", + name: newPVC.Name, + namespace: newPVC.Namespace, + operator: string(ar.Request.Operation), + storageClassName: *newPVC.Spec.StorageClassName, + } + return DecidePVCV1(reqPVC) +} + +func DecidePVCV1(pvc ReqInfo) *admissionv1.AdmissionResponse { + + accessors, err := getAccessors(pvc.storageClassName) + + if err != nil { + klog.Error("get accessor failed, err:", err) + return toV1AdmissionResponse(err) + } else if len(accessors) == 0 { + klog.Info("Not Found accessor for the storageClass:", pvc.storageClassName) + return reviewResponse + } + + for _, accessor := range accessors { + if err = validateNameSpace(pvc, accessor); err != nil { + return toV1AdmissionResponse(err) + } + if err = validateWorkSpace(pvc, accessor); err != nil { + return toV1AdmissionResponse(err) + } + } + return reviewResponse +} diff --git a/vendor/github.com/kubesphere/storageclass-accessor/webhook/scheme.go b/vendor/github.com/kubesphere/storageclass-accessor/webhook/scheme.go new file mode 100644 index 000000000..9db37050c --- /dev/null +++ b/vendor/github.com/kubesphere/storageclass-accessor/webhook/scheme.go @@ -0,0 +1,27 @@ +package webhook + +import ( + admissionv1 "k8s.io/api/admission/v1" + admissionv1beta1 "k8s.io/api/admission/v1beta1" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var scheme = runtime.NewScheme() +var codecs = serializer.NewCodecFactory(scheme) + +func init() { + addToScheme(scheme) +} + +func addToScheme(scheme *runtime.Scheme) { + utilruntime.Must(corev1.AddToScheme(scheme)) + utilruntime.Must(admissionv1beta1.AddToScheme(scheme)) + utilruntime.Must(admissionregistrationv1beta1.AddToScheme(scheme)) + utilruntime.Must(admissionv1.AddToScheme(scheme)) + utilruntime.Must(admissionregistrationv1.AddToScheme(scheme)) +} diff --git a/vendor/github.com/kubesphere/storageclass-accessor/webhook/selector.go b/vendor/github.com/kubesphere/storageclass-accessor/webhook/selector.go new file mode 100644 index 000000000..4a0038952 --- /dev/null +++ b/vendor/github.com/kubesphere/storageclass-accessor/webhook/selector.go @@ -0,0 +1,94 @@ +package webhook + +import ( + "github.com/kubesphere/storageclass-accessor/client/apis/accessor/v1alpha1" + corev1 "k8s.io/api/core/v1" + workspacev1alpha1 "kubesphere.io/api/tenant/v1alpha1" +) + +func matchLabel(info map[string]string, expressions []v1alpha1.MatchExpressions) bool { + if len(expressions) == 0 { + return true + } + + for _, rule := range expressions { + rulePass := true + for _, item := range rule.MatchExpressions { + switch item.Operator { + case v1alpha1.In: + rulePass = rulePass && inList(info[item.Key], item.Values) + case v1alpha1.NotIn: + rulePass = rulePass && !inList(info[item.Key], item.Values) + } + } + if rulePass { + return rulePass + } + } + return false +} + +func matchField(ns *corev1.Namespace, expressions []v1alpha1.FieldExpressions) bool { + //If not set limit, default pass + if len(expressions) == 0 { + return true + } + + for _, rule := range expressions { + rulePass := true + for _, item := range rule.FieldExpressions { + var val string + switch item.Field { + case v1alpha1.Name: + val = ns.Name + case v1alpha1.Status: + val = string(ns.Status.Phase) + } + switch item.Operator { + case v1alpha1.In: + rulePass = rulePass && inList(val, item.Values) + case v1alpha1.NotIn: + rulePass = rulePass && !inList(val, item.Values) + } + } + if rulePass { + return rulePass + } + } + return false +} + +func wsMatchField(ws *workspacev1alpha1.Workspace, expressions []v1alpha1.FieldExpressions) bool { + if len(expressions) == 0 { + return true + } + + for _, rule := range expressions { + pass := true + for _, item := range rule.FieldExpressions { + switch item.Field { + case v1alpha1.Status: + continue + } + switch item.Operator { + case v1alpha1.In: + pass = pass && inList(ws.Name, item.Values) + case v1alpha1.NotIn: + pass = pass && !inList(ws.Name, item.Values) + } + } + if pass { + return pass + } + } + return false +} + +func inList(val string, list []string) bool { + for _, elements := range list { + if val == elements { + return true + } + } + return false +} diff --git a/vendor/github.com/kubesphere/storageclass-accessor/webhook/validation.go b/vendor/github.com/kubesphere/storageclass-accessor/webhook/validation.go new file mode 100644 index 000000000..c1ff4ee6f --- /dev/null +++ b/vendor/github.com/kubesphere/storageclass-accessor/webhook/validation.go @@ -0,0 +1,129 @@ +package webhook + +import ( + "context" + "fmt" + "github.com/kubesphere/storageclass-accessor/client/apis/accessor/v1alpha1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/klog/v2" + workspacev1alpha1 "kubesphere.io/api/tenant/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" +) + +func validateNameSpace(reqResource ReqInfo, accessor *v1alpha1.Accessor) error { + klog.Info("start validate namespace") + //accessor, err := getAccessor() + ns, err := getNameSpace(reqResource.namespace) + if err != nil { + klog.Error(err) + return err + } + var fieldPass, labelPass bool + fieldPass = matchField(ns, accessor.Spec.NameSpaceSelector.FieldSelector) + labelPass = matchLabel(ns.Labels, accessor.Spec.NameSpaceSelector.LabelSelector) + if fieldPass && labelPass { + return nil + } + + klog.Error(fmt.Sprintf("%s %s does not allowed %s in the namespace: %s", reqResource.resource, reqResource.name, reqResource.operator, reqResource.namespace)) + return fmt.Errorf("The storageClass: %s does not allowed %s %s %s in the namespace: %s ", reqResource.storageClassName, reqResource.operator, reqResource.resource, reqResource.name, reqResource.namespace) +} + +func validateWorkSpace(reqResource ReqInfo, accessor *v1alpha1.Accessor) error { + klog.Info("start validate workspace") + + ns, err := getNameSpace(reqResource.namespace) + if err != nil { + klog.Error(err) + return err + } + if wsName, ok := ns.Labels["kubesphere.io/workspace"]; ok { + var ws *workspacev1alpha1.Workspace + ws, err = getWorkSpace(wsName) + if err != nil { + klog.Error("Cannot get the workspace") + } + var fieldPass, labelPass bool + fieldPass = wsMatchField(ws, accessor.Spec.WorkSpaceSelector.FieldSelector) + labelPass = matchLabel(ns.Labels, accessor.Spec.WorkSpaceSelector.LabelSelector) + if fieldPass && labelPass { + return nil + } + + klog.Error(fmt.Sprintf("%s %s does not allowed %s in the workspace: %s", reqResource.resource, reqResource.name, reqResource.operator, wsName)) + return fmt.Errorf("The storageClass: %s does not allowed %s %s %s in the workspace: %s ", reqResource.storageClassName, reqResource.operator, reqResource.resource, reqResource.name, wsName) + } + klog.Info("Unable to get workspace information, skipped.") + return nil +} + +func getNameSpace(nameSpaceName string) (*corev1.Namespace, error) { + nsClient, err := client.New(config.GetConfigOrDie(), client.Options{}) + if err != nil { + return nil, err + } + ns := &corev1.Namespace{} + err = nsClient.Get(context.Background(), types.NamespacedName{Namespace: "", Name: nameSpaceName}, ns) + if err != nil { + klog.Error("client get namespace failed, err:", err) + return nil, err + } + return ns, nil +} + +func getAccessors(storageClassName string) ([]*v1alpha1.Accessor, error) { + // get config + cfg, err := config.GetConfig() + if err != nil { + return nil, err + } + var cli client.Client + opts := client.Options{} + scheme := runtime.NewScheme() + _ = v1alpha1.AddToScheme(scheme) + opts.Scheme = scheme + cli, err = client.New(cfg, opts) + if err != nil { + return nil, err + } + accessorList := &v1alpha1.AccessorList{} + + var listOpt []client.ListOption + err = cli.List(context.Background(), accessorList, listOpt...) + if err != nil { + // TODO If not found , pass or not? + return nil, err + } + list := make([]*v1alpha1.Accessor, 0) + for _, accessor := range accessorList.Items { + if accessor.Spec.StorageClassName == storageClassName { + list = append(list, &accessor) + } + } + return list, nil +} + +func getWorkSpace(workspaceName string) (*workspacev1alpha1.Workspace, error) { + cfg, err := config.GetConfig() + if err != nil { + return nil, err + } + var cli client.Client + opts := client.Options{} + scheme := runtime.NewScheme() + _ = workspacev1alpha1.AddToScheme(scheme) + opts.Scheme = scheme + cli, err = client.New(cfg, opts) + if err != nil { + return nil, err + } + workspace := &workspacev1alpha1.Workspace{} + err = cli.Get(context.Background(), types.NamespacedName{Namespace: "", Name: workspaceName}, workspace) + if err != nil { + klog.Error("can't get the workspace by name, err:", err) + } + return workspace, err +} diff --git a/vendor/github.com/kubesphere/storageclass-accessor/webhook/weebhook.go b/vendor/github.com/kubesphere/storageclass-accessor/webhook/weebhook.go new file mode 100644 index 000000000..2407aae20 --- /dev/null +++ b/vendor/github.com/kubesphere/storageclass-accessor/webhook/weebhook.go @@ -0,0 +1,166 @@ +package webhook + +import ( + "context" + "crypto/tls" + "encoding/json" + "fmt" + "github.com/spf13/cobra" + "io/ioutil" + v1 "k8s.io/api/admission/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/klog/v2" + "net/http" +) + +var ( + certFile string + keyFile string + port int +) + +// CmdWebhook is user by Cobra +var CmdWebhook = &cobra.Command{ + Use: "storageClass-webhook", + Args: cobra.MaximumNArgs(0), + Run: main, +} + +func init() { + CmdWebhook.Flags().StringVar(&certFile, "tls-cert-file", "", + "File containing the x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert). Required.") + CmdWebhook.Flags().StringVar(&keyFile, "tls-private-key-file", "", + "File containing the x509 private key matching --tls-cert-file. Required.") + CmdWebhook.Flags().IntVar(&port, "port", 443, + "Secure port that the webhook listens on") + CmdWebhook.MarkFlagRequired("tls-cert-file") + CmdWebhook.MarkFlagRequired("tls-private-key-file") +} + +// admitV1beta1Func handles a v1 admission +type admitV1Func func(v1.AdmissionReview) *v1.AdmissionResponse + +// admitHandler is a handler, for both validators and mutators, that supports multiple admission review versions +type admitHandler struct { + v1 admitV1Func +} + +func newDelegateToV1AdmitHandler(f admitV1Func) admitHandler { + return admitHandler{v1: f} +} + +func server(w http.ResponseWriter, r *http.Request, admit admitHandler) { + var body []byte + if r.Body == nil { + msg := "Expected request body to be non-empty" + klog.Error(msg) + http.Error(w, msg, http.StatusBadRequest) + } + + data, err := ioutil.ReadAll(r.Body) + if err != nil { + msg := fmt.Sprintf("Request could not be decoded: %v", err) + klog.Error(msg) + http.Error(w, msg, http.StatusBadRequest) + } + body = data + + // verify the content type is accurate + contentType := r.Header.Get("Content-Type") + if contentType != "application/json" { + msg := fmt.Sprintf("contentType=%s, expect application/json", contentType) + klog.Errorf(msg) + http.Error(w, msg, http.StatusBadRequest) + return + } + + klog.V(2).Info(fmt.Sprintf("handling request: %s", body)) + + deserializer := codecs.UniversalDeserializer() + obj, gvk, err := deserializer.Decode(body, nil, nil) + if err != nil { + msg := fmt.Sprintf("Request could not be decoded: %v", err) + klog.Error(msg) + http.Error(w, msg, http.StatusBadRequest) + return + } + + var responseObj runtime.Object + switch *gvk { + // TODO v1beta1 admissionReview + case v1.SchemeGroupVersion.WithKind("AdmissionReview"): + requestedAdmissionReview, ok := obj.(*v1.AdmissionReview) + if !ok { + msg := fmt.Sprintf("Expected v1.AdmissionReview but got: %T", obj) + klog.Errorf(msg) + http.Error(w, msg, http.StatusBadRequest) + return + } + responseAdmissionReview := &v1.AdmissionReview{} + responseAdmissionReview.SetGroupVersionKind(*gvk) + responseAdmissionReview.Response = admit.v1(*requestedAdmissionReview) + responseAdmissionReview.Response.UID = requestedAdmissionReview.Request.UID + responseObj = responseAdmissionReview + default: + msg := fmt.Sprintf("Unsupported group version kind: %v", gvk) + klog.Error(msg) + http.Error(w, msg, http.StatusBadRequest) + return + } + + klog.V(2).Info(fmt.Sprintf("sending response: %v", responseObj)) + + respBytes, err := json.Marshal(responseObj) + if err != nil { + klog.Error(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + if _, err := w.Write(respBytes); err != nil { + klog.Error(err) + } +} + +func serverPVCRequest(w http.ResponseWriter, r *http.Request) { + server(w, r, newDelegateToV1AdmitHandler(AdmitPVC)) +} + +func startServer(ctx context.Context, tlsConfig *tls.Config, cw *CertWatcher) error { + go func() { + klog.Info("Starting certificate watcher") + if err := cw.Start(ctx); err != nil { + klog.Errorf("certificate watcher error: %v", err) + } + }() + + mux := http.NewServeMux() + mux.HandleFunc("/persistentvolumeclaims", serverPVCRequest) + srv := &http.Server{ + Handler: mux, + TLSConfig: tlsConfig, + } + + // listener is always closed by srv.Serve + listener, err := tls.Listen("tcp", fmt.Sprintf(":%d", port), tlsConfig) + if err != nil { + return err + } + return srv.Serve(listener) +} +func main(cmd *cobra.Command, args []string) { + // Create new cert watcher + ctx, cancel := context.WithCancel(cmd.Context()) + defer cancel() + cw, err := NewCertWatcher(certFile, keyFile) + if err != nil { + klog.Fatalf("failed to initialize new cert watcher: %v", err) + } + tslConfig := &tls.Config{ + GetCertificate: cw.GetCertificate, + } + + if err := startServer(ctx, tslConfig, cw); err != nil { + klog.Fatalf("server stopped: %v", err) + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index f186cb868..ec189ceed 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -511,6 +511,10 @@ github.com/kubesphere/pvc-autoresizer/runners # github.com/kubesphere/sonargo v0.0.2 => github.com/kubesphere/sonargo v0.0.2 ## explicit github.com/kubesphere/sonargo/sonar +# github.com/kubesphere/storageclass-accessor v0.2.0 +## explicit +github.com/kubesphere/storageclass-accessor/client/apis/accessor/v1alpha1 +github.com/kubesphere/storageclass-accessor/webhook # github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 => github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 ## explicit github.com/lann/builder @@ -1327,7 +1331,7 @@ istio.io/client-go/pkg/listers/security/v1beta1 ## explicit istio.io/gogo-genproto/googleapis/google/api istio.io/gogo-genproto/googleapis/google/rpc -# k8s.io/api v0.21.4 => k8s.io/api v0.21.2 +# k8s.io/api v0.22.1 => k8s.io/api v0.21.2 ## explicit k8s.io/api/admission/v1 k8s.io/api/admission/v1beta1 @@ -1394,7 +1398,7 @@ k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions/apiextensio k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions/internalinterfaces k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/v1 k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/v1beta1 -# k8s.io/apimachinery v0.21.4 => k8s.io/apimachinery v0.21.2 +# k8s.io/apimachinery v0.22.1 => k8s.io/apimachinery v0.21.2 ## explicit k8s.io/apimachinery/pkg/api/equality k8s.io/apimachinery/pkg/api/errors @@ -1916,7 +1920,7 @@ k8s.io/gengo/types ## explicit k8s.io/klog k8s.io/klog/klogr -# k8s.io/klog/v2 v2.8.0 => k8s.io/klog/v2 v2.8.0 +# k8s.io/klog/v2 v2.9.0 => k8s.io/klog/v2 v2.8.0 ## explicit k8s.io/klog/v2 # k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e => k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 @@ -2015,7 +2019,7 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client/proto/client # sigs.k8s.io/application v0.8.4-0.20201016185654-c8e2959e57a0 => sigs.k8s.io/application v0.8.4-0.20201016185654-c8e2959e57a0 ## explicit sigs.k8s.io/application/api/v1beta1 -# sigs.k8s.io/controller-runtime v0.9.8-0.20211019125639-aa2b3e68a52d => sigs.k8s.io/controller-runtime v0.9.8-0.20211019125639-aa2b3e68a52d +# sigs.k8s.io/controller-runtime v0.10.0 => sigs.k8s.io/controller-runtime v0.9.8-0.20211019125639-aa2b3e68a52d ## explicit sigs.k8s.io/controller-runtime sigs.k8s.io/controller-runtime/pkg/builder