diff --git a/Gopkg.lock b/Gopkg.lock index 605e238f58d..73b0a64c618 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1540,7 +1540,7 @@ revision = "a45bc6067dfdd3d84ec6d0993c141170c04f7f37" [[projects]] - digest = "1:c0be66e7be8e06471a6495d7c3566649a875c29a1ef5f3a68a8f0bdd853085d3" + digest = "1:e562f88eb9b9e8684984e27c9b825b923289551f335b8d1549023ffe42d9eb25" name = "sigs.k8s.io/controller-runtime" packages = [ "pkg/cache", @@ -1562,7 +1562,7 @@ "pkg/webhook/internal/metrics", ] pruneopts = "NUT" - revision = "89c373a86c19b6e5fc8f7b0c49a671039b8188c9" + revision = "8d94f663b1f552f74805cd8c44a1e03387f5a5d2" [[projects]] digest = "1:8730e0150dfb2b7e173890c8b9868e7a273082ef8e39f4940e3506a481cf895c" diff --git a/Gopkg.toml b/Gopkg.toml index 41fc9e55b3e..c9eb721a54f 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -32,7 +32,7 @@ required = [ [[constraint]] name="sigs.k8s.io/controller-runtime" - revision="89c373a86c19b6e5fc8f7b0c49a671039b8188c9" + revision="8d94f663b1f552f74805cd8c44a1e03387f5a5d2" # All dependencies of Kubernetes from branch release-1.13 converted to override clauses. This include dependencies that # are not used in this project. See diff --git a/charts/catalog/templates/rbac.yaml b/charts/catalog/templates/rbac.yaml index 30d3561ef3e..87e45d568ed 100644 --- a/charts/catalog/templates/rbac.yaml +++ b/charts/catalog/templates/rbac.yaml @@ -38,9 +38,9 @@ items: verbs: ["get","list","watch"] - apiGroups: ["servicecatalog.k8s.io"] resources: ["serviceinstances","servicebindings"] - verbs: ["get","list","watch", "update"] + verbs: ["get","list","watch", "update", "patch"] - apiGroups: ["servicecatalog.k8s.io"] - resources: ["clusterservicebrokers/status","clusterserviceclasses/status","clusterserviceplans/status","serviceinstances/status","serviceinstances/reference","servicebindings/status"] + resources: ["clusterservicebrokers/status","clusterserviceclasses/status","clusterserviceplans/status","serviceinstances/status","servicebindings/status"] verbs: ["update"] {{- if not .Values.namespacedServiceBrokerDisabled }} - apiGroups: ["servicecatalog.k8s.io"] diff --git a/charts/catalog/templates/webhook-deployment.yaml b/charts/catalog/templates/webhook-deployment.yaml index adb5252b23f..53703854b66 100644 --- a/charts/catalog/templates/webhook-deployment.yaml +++ b/charts/catalog/templates/webhook-deployment.yaml @@ -28,6 +28,7 @@ spec: {{ toYaml .Values.webhook.annotations | indent 8 }} {{- end }} spec: + serviceAccountName: "{{ .Values.webhook.serviceAccount }}" containers: - name: svr image: {{ .Values.image }} diff --git a/charts/catalog/templates/webhook-register.yaml b/charts/catalog/templates/webhook-register.yaml index 843e5dbb352..8579fd2ae7e 100644 --- a/charts/catalog/templates/webhook-register.yaml +++ b/charts/catalog/templates/webhook-register.yaml @@ -22,6 +22,58 @@ webhooks: apiGroups: ["servicecatalog.k8s.io"] apiVersions: ["v1beta1"] resources: ["clusterservicebrokers"] +- name: mutating.clusterserviceclasses.servicecatalog.k8s.io + clientConfig: + caBundle: {{ b64enc $ca.Cert }} + service: + name: {{ template "fullname" . }}-webhook + namespace: "{{ .Release.Namespace }}" + path: "/mutating-clusterserviceclasses" + failurePolicy: Fail + rules: + - operations: [ "CREATE", "UPDATE" ] + apiGroups: ["servicecatalog.k8s.io"] + apiVersions: ["v1beta1"] + resources: ["clusterserviceclasses"] +- name: mutating.serviceclasses.servicecatalog.k8s.io + clientConfig: + caBundle: {{ b64enc $ca.Cert }} + service: + name: {{ template "fullname" . }}-webhook + namespace: "{{ .Release.Namespace }}" + path: "/mutating-serviceclasses" + failurePolicy: Fail + rules: + - operations: [ "CREATE", "UPDATE" ] + apiGroups: ["servicecatalog.k8s.io"] + apiVersions: ["v1beta1"] + resources: ["serviceclasses"] +- name: mutating.clusterserviceplans.servicecatalog.k8s.io + clientConfig: + caBundle: {{ b64enc $ca.Cert }} + service: + name: {{ template "fullname" . }}-webhook + namespace: "{{ .Release.Namespace }}" + path: "/mutating-clusterserviceplans" + failurePolicy: Fail + rules: + - operations: [ "CREATE", "UPDATE" ] + apiGroups: ["servicecatalog.k8s.io"] + apiVersions: ["v1beta1"] + resources: ["clusterserviceplans"] +- name: mutating.serviceplans.servicecatalog.k8s.io + clientConfig: + caBundle: {{ b64enc $ca.Cert }} + service: + name: {{ template "fullname" . }}-webhook + namespace: "{{ .Release.Namespace }}" + path: "/mutating-serviceplans" + failurePolicy: Fail + rules: + - operations: [ "CREATE", "UPDATE" ] + apiGroups: ["servicecatalog.k8s.io"] + apiVersions: ["v1beta1"] + resources: ["serviceplans"] - name: mutating.servicebindings.servicecatalog.k8s.io clientConfig: caBundle: {{ b64enc $ca.Cert }} diff --git a/charts/catalog/values.yaml b/charts/catalog/values.yaml index a0f28fff71a..59bb6a75188 100644 --- a/charts/catalog/values.yaml +++ b/charts/catalog/values.yaml @@ -1,6 +1,6 @@ # Default values for Service Catalog # service-catalog image to use -image: eu.gcr.io/kyma-project/develop/service-catalog/service-catalog-amd64:crd-0.0.1 +image: eu.gcr.io/kyma-project/develop/service-catalog/service-catalog-amd64:crd-0.0.2 # imagePullPolicy for the service-catalog; valid values are "IfNotPresent", # "Never", and "Always" imagePullPolicy: Always @@ -61,7 +61,7 @@ controllerManager: # Whether or not the controller supports a --broker-relist-interval flag. If this is # set to true, brokerRelistInterval will be used as the value for that flag brokerRelistIntervalActivated: true - # The maximum amount of time to back-off while polling an OSB API operation; format is a duration (`20m`, `1h`, etc) + # The maximum amount of time to back-off while polling an OSB API operation; format is a duration (`20m`, `1h`, etc) operationPollingMaximumBackoffDuration: 20m # enables profiling via web interface host:port/debug/pprof/ profiling: diff --git a/cmd/svcat/svcat_test.go b/cmd/svcat/svcat_test.go index 23ac841aa18..261e0f824e6 100644 --- a/cmd/svcat/svcat_test.go +++ b/cmd/svcat/svcat_test.go @@ -90,6 +90,9 @@ func TestGetSvcatWithNamespacedBrokerFeatureDisabled(t *testing.T) { &v1beta1.ClusterServiceClass{ ObjectMeta: metav1.ObjectMeta{ Name: "my-cluster-class", + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "my-cluster-class", + }, }, Spec: v1beta1.ClusterServiceClassSpec{ CommonServiceClassSpec: v1beta1.CommonServiceClassSpec{ @@ -100,6 +103,9 @@ func TestGetSvcatWithNamespacedBrokerFeatureDisabled(t *testing.T) { &v1beta1.ClusterServicePlan{ ObjectMeta: metav1.ObjectMeta{ Name: "my-cluster-plan", + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "my-cluster-plan", + }, }, Spec: v1beta1.ClusterServicePlanSpec{ CommonServicePlanSpec: v1beta1.CommonServicePlanSpec{ @@ -666,7 +672,9 @@ func apihandler(w http.ResponseWriter, r *http.Request) { match = filepath.Join("core", coreMatch[1]) } - match = strings.Replace(match, "?", "_", -1) // windows doesn't allow '?' in filenames + match = strings.Replace(match, "?", "_", -1) // windows doesn't allow '?' in filenames + match = strings.Replace(match, "%2F", "_", -1) // "/" is not allowed in filenames + relpath, err := url.PathUnescape(match) if err != nil { w.WriteHeader(500) diff --git a/cmd/svcat/testdata/responses/catalog/clusterserviceplans_fieldSelector=spec.clusterServiceClassRef.name=4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468,spec.externalName=default.json b/cmd/svcat/testdata/responses/catalog/clusterserviceplans_labelSelector=servicecatalog.k8s.io_spec.clusterServiceClassRef.name=4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468,servicecatalog.k8s.io_spec.externalName=default.json similarity index 100% rename from cmd/svcat/testdata/responses/catalog/clusterserviceplans_fieldSelector=spec.clusterServiceClassRef.name=4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468,spec.externalName=default.json rename to cmd/svcat/testdata/responses/catalog/clusterserviceplans_labelSelector=servicecatalog.k8s.io_spec.clusterServiceClassRef.name=4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468,servicecatalog.k8s.io_spec.externalName=default.json diff --git a/cmd/svcat/testdata/responses/catalog/clusterserviceplans_fieldSelector=spec.clusterServiceClassRef.name=4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468.json b/cmd/svcat/testdata/responses/catalog/clusterserviceplans_labelSelector=servicecatalog.k8s.io_spec.clusterServiceClassRef.name=4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468.json similarity index 100% rename from cmd/svcat/testdata/responses/catalog/clusterserviceplans_fieldSelector=spec.clusterServiceClassRef.name=4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468.json rename to cmd/svcat/testdata/responses/catalog/clusterserviceplans_labelSelector=servicecatalog.k8s.io_spec.clusterServiceClassRef.name=4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468.json diff --git a/cmd/svcat/testdata/responses/catalog/clusterserviceplans_fieldSelector=spec.externalName=default.json b/cmd/svcat/testdata/responses/catalog/clusterserviceplans_labelSelector=servicecatalog.k8s.io_spec.externalName=default.json similarity index 100% rename from cmd/svcat/testdata/responses/catalog/clusterserviceplans_fieldSelector=spec.externalName=default.json rename to cmd/svcat/testdata/responses/catalog/clusterserviceplans_labelSelector=servicecatalog.k8s.io_spec.externalName=default.json diff --git a/cmd/svcat/testdata/responses/catalog/clusterserviceplans_fieldSelector=spec.externalName=premium.json b/cmd/svcat/testdata/responses/catalog/clusterserviceplans_labelSelector=servicecatalog.k8s.io_spec.externalName=premium.json similarity index 100% rename from cmd/svcat/testdata/responses/catalog/clusterserviceplans_fieldSelector=spec.externalName=premium.json rename to cmd/svcat/testdata/responses/catalog/clusterserviceplans_labelSelector=servicecatalog.k8s.io_spec.externalName=premium.json diff --git a/cmd/svcat/testdata/responses/catalog/namespaces/default/serviceplans_fieldSelector=spec.externalName=namespacedplan.json b/cmd/svcat/testdata/responses/catalog/namespaces/default/serviceplans_labelSelector=servicecatalog.k8s.io_spec.externalName=namespacedplan,servicecatalog.k8s.io_spec.serviceClassRef.name=4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468.json similarity index 100% rename from cmd/svcat/testdata/responses/catalog/namespaces/default/serviceplans_fieldSelector=spec.externalName=namespacedplan.json rename to cmd/svcat/testdata/responses/catalog/namespaces/default/serviceplans_labelSelector=servicecatalog.k8s.io_spec.externalName=namespacedplan,servicecatalog.k8s.io_spec.serviceClassRef.name=4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468.json diff --git a/cmd/svcat/testdata/responses/catalog/namespaces/default/serviceplans_fieldSelector=spec.serviceClassRef.name=4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468,spec.externalName=namespacedplan.json b/cmd/svcat/testdata/responses/catalog/namespaces/default/serviceplans_labelSelector=servicecatalog.k8s.io_spec.externalName=namespacedplan.json similarity index 100% rename from cmd/svcat/testdata/responses/catalog/namespaces/default/serviceplans_fieldSelector=spec.serviceClassRef.name=4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468,spec.externalName=namespacedplan.json rename to cmd/svcat/testdata/responses/catalog/namespaces/default/serviceplans_labelSelector=servicecatalog.k8s.io_spec.externalName=namespacedplan.json diff --git a/cmd/svcat/testdata/responses/catalog/serviceinstances_fieldSelector=spec.clusterServicePlanRef.name=86064792-7ea2-467b-af93-ac9694d96d52.json b/cmd/svcat/testdata/responses/catalog/serviceinstances_labelSelector=servicecatalog.k8s.io_spec.clusterServicePlanRef.name=86064792-7ea2-467b-af93-ac9694d96d52.json similarity index 100% rename from cmd/svcat/testdata/responses/catalog/serviceinstances_fieldSelector=spec.clusterServicePlanRef.name=86064792-7ea2-467b-af93-ac9694d96d52.json rename to cmd/svcat/testdata/responses/catalog/serviceinstances_labelSelector=servicecatalog.k8s.io_spec.clusterServicePlanRef.name=86064792-7ea2-467b-af93-ac9694d96d52.json diff --git a/cmd/svcat/testdata/responses/catalog/serviceinstances_fieldSelector=spec.clusterServicePlanRef.name=cc0d7529-18e8-416d-8946-6f7456acd589.json b/cmd/svcat/testdata/responses/catalog/serviceinstances_labelSelector=servicecatalog.k8s.io_spec.clusterServicePlanRef.name=cc0d7529-18e8-416d-8946-6f7456acd589.json similarity index 100% rename from cmd/svcat/testdata/responses/catalog/serviceinstances_fieldSelector=spec.clusterServicePlanRef.name=cc0d7529-18e8-416d-8946-6f7456acd589.json rename to cmd/svcat/testdata/responses/catalog/serviceinstances_labelSelector=servicecatalog.k8s.io_spec.clusterServicePlanRef.name=cc0d7529-18e8-416d-8946-6f7456acd589.json diff --git a/cmd/webhook/server/webhook.go b/cmd/webhook/server/webhook.go index b21b01ab199..b21b2639742 100644 --- a/cmd/webhook/server/webhook.go +++ b/cmd/webhook/server/webhook.go @@ -22,9 +22,14 @@ import ( scTypes "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" csbmutation "github.com/kubernetes-incubator/service-catalog/pkg/webhook/servicecatalog/clusterservicebroker/mutation" + cscmutation "github.com/kubernetes-incubator/service-catalog/pkg/webhook/servicecatalog/clusterserviceclass/mutation" + cspmutation "github.com/kubernetes-incubator/service-catalog/pkg/webhook/servicecatalog/clusterserviceplan/mutation" + sbmutation "github.com/kubernetes-incubator/service-catalog/pkg/webhook/servicecatalog/servicebinding/mutation" brmutation "github.com/kubernetes-incubator/service-catalog/pkg/webhook/servicecatalog/servicebroker/mutation" + scmutation "github.com/kubernetes-incubator/service-catalog/pkg/webhook/servicecatalog/serviceclass/mutation" simutation "github.com/kubernetes-incubator/service-catalog/pkg/webhook/servicecatalog/serviceinstance/mutation" + spmutation "github.com/kubernetes-incubator/service-catalog/pkg/webhook/servicecatalog/serviceplan/mutation" "github.com/pkg/errors" "k8s.io/apiserver/pkg/server/healthz" @@ -59,15 +64,20 @@ func run(opts *WebhookServerOptions, stopCh <-chan struct{}) error { // setup webhook server webhookSvr := &webhook.Server{ - Port: int32(opts.SecureServingOptions.BindPort), + Port: opts.SecureServingOptions.BindPort, CertDir: opts.SecureServingOptions.ServerCert.CertDirectory, } webhooks := map[string]admission.Handler{ "/mutating-clusterservicebrokers": &csbmutation.CreateUpdateHandler{}, - "/mutating-servicebindings": &sbmutation.CreateUpdateHandler{}, - "/mutating-servicebrokers": &brmutation.CreateUpdateHandler{}, - "/mutating-serviceinstances": &simutation.CreateUpdateHandler{}, + "/mutating-clusterserviceclasses": &cscmutation.CreateUpdateHandler{}, + "/mutating-clusterserviceplans": &cspmutation.CreateUpdateHandler{}, + + "/mutating-servicebindings": &sbmutation.CreateUpdateHandler{}, + "/mutating-servicebrokers": &brmutation.CreateUpdateHandler{}, + "/mutating-serviceclasses": &scmutation.CreateUpdateHandler{}, + "/mutating-serviceinstances": &simutation.CreateUpdateHandler{}, + "/mutating-serviceplans": &spmutation.CreateUpdateHandler{}, } for path, handler := range webhooks { diff --git a/contrib/hack/crd/README.md b/contrib/hack/crd/README.md new file mode 100644 index 00000000000..3e57a11adfa --- /dev/null +++ b/contrib/hack/crd/README.md @@ -0,0 +1,127 @@ +## Cookbook for CRDs POC + +Execute all commands from the cookbook in the `hack` directory. + +### Bootstrap local environment for testing + +1. In one shell execute: +```bash +./bin/bootstrap-testing-environment.sh +``` + +Under the hood this script is: +- creating minikube +- installing tiller +- installing Service Catalog +- installing Helm Broker +- installing Binding Usage Controller +- registering Helm Broker in Service Catalog with http://localhost:8081 +- exposing the Helm Broker to your localhost on port 8081, so your controller in step 2 can access all broker endpoints + +2. When step one is finished then on the other shell execute: +```bash +./bin/run-controller.sh +``` + +**Now you are ready to go!** + +When you execute `svcat get classes`, then you should see: +```bash + NAME NAMESPACE DESCRIPTION ++----------------------+-----------+------------------------------------------+ + azure-service-broker Extends the Service Catalog with Azure + services + redis Redis by Helm Broker (Experimental) + gcp-service-broker Extends the Service Catalog with Google + Cloud Platform services +``` + +### Testing Scenario + +Follow these steps: + +1. Export the name of the Namespace. +```bash +export namespace="qa" +``` +2. Create a Redis instance. +```bash +kubectl create -f assets/scenario/redis-instance-manual.yaml -n $namespace +``` +3. Check if the Redis instance is already provisioned. +```bash +watch -n 1 "kubectl get serviceinstance/redis -n $namespace -o jsonpath='{ .status.conditions[0].reason }'" +``` +4. Create Secrets for the Redis instance. +```bash +kubectl create -f assets/scenario/redis-instance-binding-manual.yaml -n $namespace +``` +5. Create a deploy. +```bash +kubectl create -f assets/scenario/redis-client.yaml -n $namespace +``` +6. Create a Binding Usage with **APP_** prefix. +```bash +kubectl create -f assets/scenario/service-binding-usage.yaml -n $namespace +``` +7. Wait until the Pod is ready. +```bash +kubectl get po -l app=redis-client -n $namespace -o jsonpath='{ .items[*].status.conditions[?(@.type=="Ready")].status }' +``` +8. Export the name of the Pod. +```bash +export POD_NAME=$(kubectl get po -l app=redis-client -n $namespace -o jsonpath='{ .items[*].metadata.name }') +``` +9. Execute the `check-redis` script on the Pod. +```bash +kubectl exec ${POD_NAME} -n $namespace /check-redis.sh +``` + +The information and statistics about the Redis server appear. + + +### Documentation + +- [Design of the Service Catalog](https://svc-cat.io/docs/design/) +- [Service Catalog Developer Guide](https://svc-cat.io/docs/devguide/) +- [Service Catalog Code & Documentation Standards](https://svc-cat.io/docs/code-standards/) + + +### Old way of running controller locally + +#### Prerequisites + +Kyma installed on your cluster but without the ServiceCatalog. + +#### Steps + +1. Install ServiceCatalog chart +```bash +helm install --name catalog --namespace kyma-system charts/catalog/ --wait +``` + +2. Register Helm Broker +```bash +kubectl apply -f ./assets/helm-broker.yaml +``` + +3. Export the name of the HelmBroker Pod. +```bash +export HB_POD_NAME=$(kubectl get po -l app=helm-broker -n kyma-system -o jsonpath='{ .items[*].metadata.name }') +``` + +4. Expose helm-broker service +```bash +kubectl port-forward -n kyma-system pod/${HB_POD_NAME} 8081:8080 +``` + +5. Scale down controller manager + +```bash +kubectl -n kyma-system scale deploy --replicas=0 catalog-catalog-controller-manager +``` + +6. Run the Service Catalog controller-manager +```bash +./bin/run-controller.sh +``` diff --git a/contrib/hack/crd/assets/buc-chart.tgz b/contrib/hack/crd/assets/buc-chart.tgz new file mode 100644 index 00000000000..ed19b1d261c Binary files /dev/null and b/contrib/hack/crd/assets/buc-chart.tgz differ diff --git a/contrib/hack/crd/assets/helm-broker-chart.tgz b/contrib/hack/crd/assets/helm-broker-chart.tgz new file mode 100644 index 00000000000..ab4b4bfe5fb Binary files /dev/null and b/contrib/hack/crd/assets/helm-broker-chart.tgz differ diff --git a/contrib/hack/crd/assets/helm-broker.yaml b/contrib/hack/crd/assets/helm-broker.yaml new file mode 100644 index 00000000000..452d04f5cea --- /dev/null +++ b/contrib/hack/crd/assets/helm-broker.yaml @@ -0,0 +1,7 @@ +apiVersion: servicecatalog.k8s.io/v1beta1 +kind: ClusterServiceBroker +metadata: + name: helm-broker +spec: + relistRequests: 1 + url: http://localhost:8081/ diff --git a/contrib/hack/crd/assets/pod-preset-chart.tgz b/contrib/hack/crd/assets/pod-preset-chart.tgz new file mode 100644 index 00000000000..887ba42581b Binary files /dev/null and b/contrib/hack/crd/assets/pod-preset-chart.tgz differ diff --git a/contrib/hack/crd/assets/scenario/redis-client.yaml b/contrib/hack/crd/assets/scenario/redis-client.yaml new file mode 100644 index 00000000000..60c688c14e6 --- /dev/null +++ b/contrib/hack/crd/assets/scenario/redis-client.yaml @@ -0,0 +1,17 @@ +apiVersion: apps/v1beta1 +kind: Deployment +metadata: + name: redis-client +spec: + replicas: 1 + template: + metadata: + labels: + app: redis-client + spec: + containers: + - name: redis-client + image: "appregistry/redis:3.2.9-r2" + env: + - name: ALLOW_EMPTY_PASSWORD + value: "yes" diff --git a/contrib/hack/crd/assets/scenario/redis-instance-binding-manual.yaml b/contrib/hack/crd/assets/scenario/redis-instance-binding-manual.yaml new file mode 100644 index 00000000000..c0a45530f9a --- /dev/null +++ b/contrib/hack/crd/assets/scenario/redis-instance-binding-manual.yaml @@ -0,0 +1,11 @@ +apiVersion: servicecatalog.k8s.io/v1beta1 +kind: ServiceBinding +metadata: + name: redis-instance-credential + finalizers: + - kubernetes-incubator/service-catalog +spec: + externalID: fa1f93db-50e9-49ec-aa34-bdbafa2f1c46 + secretName: redis-instance-credential + instanceRef: + name: redis diff --git a/contrib/hack/crd/assets/scenario/redis-instance-binding.yaml b/contrib/hack/crd/assets/scenario/redis-instance-binding.yaml new file mode 100644 index 00000000000..1c43c645d45 --- /dev/null +++ b/contrib/hack/crd/assets/scenario/redis-instance-binding.yaml @@ -0,0 +1,7 @@ +apiVersion: servicecatalog.k8s.io/v1beta1 +kind: ServiceBinding +metadata: + name: redis-instance-credential +spec: + instanceRef: + name: redis diff --git a/contrib/hack/crd/assets/scenario/redis-instance-manual.yaml b/contrib/hack/crd/assets/scenario/redis-instance-manual.yaml new file mode 100644 index 00000000000..0ac370aa3f9 --- /dev/null +++ b/contrib/hack/crd/assets/scenario/redis-instance-manual.yaml @@ -0,0 +1,10 @@ +apiVersion: servicecatalog.k8s.io/v1beta1 +kind: ServiceInstance +metadata: + name: redis + finalizers: + - kubernetes-incubator/service-catalog +spec: + clusterServiceClassExternalName: redis + clusterServicePlanExternalName: micro + externalID: 98442e82-b509-48c3-89a4-9011896debf6 diff --git a/contrib/hack/crd/assets/scenario/redis-instance.yaml b/contrib/hack/crd/assets/scenario/redis-instance.yaml new file mode 100644 index 00000000000..6032ba9ebf4 --- /dev/null +++ b/contrib/hack/crd/assets/scenario/redis-instance.yaml @@ -0,0 +1,7 @@ +apiVersion: servicecatalog.k8s.io/v1beta1 +kind: ServiceInstance +metadata: + name: redis +spec: + clusterServiceClassExternalName: redis + clusterServicePlanExternalName: micro diff --git a/contrib/hack/crd/assets/scenario/service-binding-usage.yaml b/contrib/hack/crd/assets/scenario/service-binding-usage.yaml new file mode 100644 index 00000000000..7002cb654ff --- /dev/null +++ b/contrib/hack/crd/assets/scenario/service-binding-usage.yaml @@ -0,0 +1,11 @@ +apiVersion: servicecatalog.kyma-project.io/v1alpha1 +kind: ServiceBindingUsage +metadata: + name: deploy-redis-client +spec: + serviceBindingRef: + name: redis-instance-credential + usedBy: + kind: deployment + name: redis-client + diff --git a/contrib/hack/crd/assets/tiller.yaml b/contrib/hack/crd/assets/tiller.yaml new file mode 100644 index 00000000000..c7176eb2f93 --- /dev/null +++ b/contrib/hack/crd/assets/tiller.yaml @@ -0,0 +1,96 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tiller + namespace: kube-system +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: tiller-cluster-admin +subjects: +- kind: ServiceAccount + name: tiller + namespace: kube-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + labels: + app: helm + name: tiller + name: tiller-deploy + namespace: kube-system +spec: + selector: + matchLabels: + app: helm + name: tiller + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 + template: + metadata: + labels: + app: helm + name: tiller + spec: + serviceAccountName: tiller + + initContainers: + - name: init-tiller + image: eu.gcr.io/kyma-project/alpine-net:0.2.74 + imagePullPolicy: IfNotPresent + command: ['sh', '-c', 'until nc -zv kube-dns.kube-system.svc.cluster.local 53; do echo waiting for k8s readiness; sleep 2; done;'] + + containers: + - name: tiller + image: gcr.io/kubernetes-helm/tiller:v2.10.0 + imagePullPolicy: IfNotPresent + env: + - name: TILLER_NAMESPACE + value: kube-system + + ports: + - containerPort: 44134 + name: tiller + livenessProbe: + httpGet: + path: /liveness + port: 44135 + initialDelaySeconds: 1 + timeoutSeconds: 1 + + readinessProbe: + httpGet: + path: /readiness + port: 44135 + initialDelaySeconds: 1 + timeoutSeconds: 1 + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: helm + name: tiller + name: tiller-deploy + namespace: kube-system + annotations: + auth.istio.io/44134: NONE + +spec: + selector: + app: helm + name: tiller + ports: + - name: tiller + port: 44134 + targetPort: tiller + type: ClusterIP diff --git a/contrib/hack/crd/bin/bootstrap-testing-environment.sh b/contrib/hack/crd/bin/bootstrap-testing-environment.sh new file mode 100755 index 00000000000..b44a32b16ae --- /dev/null +++ b/contrib/hack/crd/bin/bootstrap-testing-environment.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# 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. + +set -u +set -o errexit + +CURRENT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) + +echo "- Initialize Minikube" +bash ${CURRENT_DIR}/minikube.sh + +echo "- Installing Tiller..." +kubectl apply -f ${CURRENT_DIR}/../assets/tiller.yaml + +bash ${CURRENT_DIR}/is-ready.sh kube-system name tiller + +echo "- Installing SC" +helm install --name catalog --namespace kyma-system ${CURRENT_DIR}/../../../../charts/catalog/ --wait + +echo "- Installing Pod Preset Helm Chart" +helm install ${CURRENT_DIR}/../assets/pod-preset-chart.tgz --name podpreset --namespace kyma-system --wait + +echo "- Installing Helm Broker Helm Chart" +helm install ${CURRENT_DIR}/../assets/helm-broker-chart.tgz --name helm-broker --namespace kyma-system --wait +echo "- Installing BUC Helm Chart" +helm install ${CURRENT_DIR}/../assets/buc-chart.tgz --name buc --namespace kyma-system --wait + +echo "- Register Helm Broker in Service Catalog" +kubectl apply -f ${CURRENT_DIR}/../assets/helm-broker.yaml + +echo "- Scale down controller manager" +kubectl -n kyma-system scale deploy --replicas=0 catalog-catalog-controller-manager + +echo "- Expose Helm Broker to localhost on port 8081" +export HB_POD_NAME=$(kubectl get po -l app=helm-broker -n kyma-system -o jsonpath='{ .items[*].metadata.name }') +kubectl port-forward -n kyma-system pod/${HB_POD_NAME} 8081:8080 diff --git a/contrib/hack/crd/bin/is-ready.sh b/contrib/hack/crd/bin/is-ready.sh new file mode 100755 index 00000000000..6c306c0862a --- /dev/null +++ b/contrib/hack/crd/bin/is-ready.sh @@ -0,0 +1,83 @@ +#!/bin/bash +# 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. + +################################################################################ +# +# Validate if specified POD is up and ready +# $1 - namespace +# $2 - pod's label name +# $3 - pod's label value +# Sample: bash isready.sh kube-system tiller +# +################################################################################ + +# Just to satisfy make verify +# I am not sure if enabling it is safe, it might brake the script below +set -o errexit +set +o errexit + +#Checking if POD is already deployed +trap "exit" INT +while : +do + if [[ $(kubectl get pods -n "$1" -l "$2"="$3" -o jsonpath='{.items[*].metadata.name}') ]] + then + echo "$3 is deployed..." + break + else + echo "$3 is not deployed - waiting 5s..." + sleep 5 + fi +done + + +#Checking if POD is ready to operate +for POD in $(kubectl get pods -n "$1" -l "$2"="$3" -o jsonpath='{.items[*].metadata.name}') +do + trap "exit" INT + while : + do + if [ "$(kubectl get pod "$POD" -n "$1" -o jsonpath='{.status.containerStatuses[0].ready}')" = "true" ] + then + echo "$POD is running..." + break + else + echo "$POD is not running - waiting 5s..." $(kubectl get event -n "$1" -o go-template='{{range .items}}{{if eq .involvedObject.name "'$POD'"}}{{.message}}{{"\n"}}{{end}}{{end}}' | tail -1) + sleep 5 + fi + done +done + +#checking only if kube-dns is checked +if [ "$3" = "kube-dns" ] +then + + for POD in $3 + do + trap "exit" INT + while : + do + if [[ "$(kubectl get ep $3 -n $1 -o jsonpath='{.subsets[0].addresses[0].ip}')" ]] + then + echo "kubedns endpoint IP assigned" + break + else + echo "kubedns endpoint IP is not assigned yet - waiting 5s..." + sleep 5 + fi + done + done + +fi diff --git a/contrib/hack/crd/bin/minikube.sh b/contrib/hack/crd/bin/minikube.sh new file mode 100755 index 00000000000..e1d84bb2ad3 --- /dev/null +++ b/contrib/hack/crd/bin/minikube.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# 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. + +set -o errexit + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +RESOURCES_DIR="${CURRENT_DIR}/../resources" + +MINIKUBE_VERSION=0.33.0 +KUBERNETES_VERSION=1.11.5 +KUBECTL_CLI_VERSION=1.11.0 +VM_DRIVER=hyperkit +DISK_SIZE=20g +MEMORY=8192 + +#TODO refactor to use minikube status! +function waitForMinikubeToBeUp() { + set +o errexit + + echo "Waiting for minikube to be up..." + + LIMIT=15 + COUNTER=0 + + while [ ${COUNTER} -lt ${LIMIT} ] && [ -z "$STATUS" ]; do + (( COUNTER++ )) + echo -e "Keep calm, there are $LIMIT possibilities and so far it is attempt number $COUNTER" + STATUS="$(kubectl get namespaces || :)" + sleep 1 + done + + # In case apiserver is not available get minikube logs + if [[ -z "$STATUS" ]] && [[ "$VM_DRIVER" = "none" ]]; then + cat /var/lib/minikube/minikube.err + fi + + set -o errexit + + echo "Minikube is up" +} + +function increaseFsInotifyMaxUserInstances() { + # Default value of 128 is not enough to perform “kubectl log -f” from pods, hence increased to 524288 + if [[ "$VM_DRIVER" != "none" ]]; then + minikube ssh -- "sudo sysctl -w fs.inotify.max_user_instances=524288" + echo "fs.inotify.max_user_instances is increased" + fi +} + +function applyDefaultRbacRole() { + kubectl apply -f "${RESOURCES_DIR}/default-sa-rbac-role.yaml" +} + +function start() { + minikube start \ + --memory $MEMORY \ + --cpus 4 \ + --extra-config=apiserver.authorization-mode=RBAC \ + --extra-config=apiserver.cors-allowed-origins="http://*" \ + --extra-config=apiserver.enable-admission-plugins="DefaultStorageClass,LimitRanger,MutatingAdmissionWebhook,NamespaceExists,NamespaceLifecycle,ResourceQuota,ServiceAccount,ValidatingAdmissionWebhook" \ + --kubernetes-version=v$KUBERNETES_VERSION \ + --vm-driver=$VM_DRIVER \ + --disk-size=$DISK_SIZE \ + --bootstrapper=kubeadm + + waitForMinikubeToBeUp + + increaseFsInotifyMaxUserInstances + +} + +start diff --git a/contrib/hack/crd/bin/run-controller.sh b/contrib/hack/crd/bin/run-controller.sh new file mode 100755 index 00000000000..af060f821b1 --- /dev/null +++ b/contrib/hack/crd/bin/run-controller.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# 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. + +set -u +set -o errexit + +readonly ROOT_PATH=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) + +go run ${ROOT_PATH}/../../../../cmd/service-catalog/main.go controller-manager \ +--secure-port="8444" \ +--cluster-id-configmap-namespace="default" \ +--leader-elect="false" \ +-v="6" \ +--resync-interval="5m" \ +--broker-relist-interval="24h" \ +--operation-polling-maximum-backoff-duration="20m" \ +--k8s-kubeconfig="${KUBECONFIG}" \ +--service-catalog-kubeconfig="${KUBECONFIG}" \ +--cert-dir="${ROOT_PATH}/../../../../tmp/" \ +--feature-gates="OriginatingIdentity=true" \ +--feature-gates="ServicePlanDefaults=false" \ No newline at end of file diff --git a/pkg/apis/servicecatalog/plan_reference.go b/pkg/apis/servicecatalog/plan_reference.go index 4dcee076de1..f93e4905a61 100644 --- a/pkg/apis/servicecatalog/plan_reference.go +++ b/pkg/apis/servicecatalog/plan_reference.go @@ -131,57 +131,57 @@ func (pr PlanReference) GetSpecifiedServicePlan() string { return "" } -// GetClusterServiceClassFilterFieldName returns the appropriate field name for filtering +// GetClusterServiceClassFilterLabelName returns the appropriate field name for filtering // a list of service catalog classes by the PlanReference. -func (pr PlanReference) GetClusterServiceClassFilterFieldName() string { +func (pr PlanReference) GetClusterServiceClassFilterLabelName() string { if pr.ClusterServiceClassExternalName != "" { - return "spec.externalName" + return GroupName + "/spec.externalName" } if pr.ClusterServiceClassExternalID != "" { - return "spec.externalID" + return GroupName + "/spec.externalID" } return "" } -// GetClusterServicePlanFilterFieldName returns the appropriate field name for filtering +// GetClusterServicePlanFilterLabelName returns the appropriate field name for filtering // a list of service catalog plans by the PlanReference. -func (pr PlanReference) GetClusterServicePlanFilterFieldName() string { +func (pr PlanReference) GetClusterServicePlanFilterLabelName() string { if pr.ClusterServicePlanExternalName != "" { - return "spec.externalName" + return GroupName + "/spec.externalName" } if pr.ClusterServicePlanExternalID != "" { - return "spec.externalID" + return GroupName + "/spec.externalID" } return "" } -// GetServiceClassFilterFieldName returns the appropriate field name for filtering +// GetServiceClassFilterLabelName returns the appropriate field name for filtering // a list of service catalog classes by the PlanReference. -func (pr PlanReference) GetServiceClassFilterFieldName() string { +func (pr PlanReference) GetServiceClassFilterLabelName() string { if pr.ServiceClassExternalName != "" { - return "spec.externalName" + return GroupName + "/spec.externalName" } if pr.ServiceClassExternalID != "" { - return "spec.externalID" + return GroupName + "/spec.externalID" } return "" } -// GetServicePlanFilterFieldName returns the appropriate field name for filtering +// GetServicePlanFilterLabelName returns the appropriate field name for filtering // a list of service catalog plans by the PlanReference. -func (pr PlanReference) GetServicePlanFilterFieldName() string { +func (pr PlanReference) GetServicePlanFilterLabelName() string { if pr.ServicePlanExternalName != "" { - return "spec.externalName" + return GroupName + "/spec.externalName" } if pr.ServicePlanExternalID != "" { - return "spec.externalID" + return GroupName + "/spec.externalID" } return "" diff --git a/pkg/apis/servicecatalog/v1beta1/plan_reference.go b/pkg/apis/servicecatalog/v1beta1/plan_reference.go index 3180cf95649..c8219adf9ad 100644 --- a/pkg/apis/servicecatalog/v1beta1/plan_reference.go +++ b/pkg/apis/servicecatalog/v1beta1/plan_reference.go @@ -131,57 +131,57 @@ func (pr PlanReference) GetSpecifiedServicePlan() string { return "" } -// GetClusterServiceClassFilterFieldName returns the appropriate field name for filtering +// GetClusterServiceClassFilterLabelName returns the appropriate label name for filtering // a list of service catalog classes by the PlanReference. -func (pr PlanReference) GetClusterServiceClassFilterFieldName() string { +func (pr PlanReference) GetClusterServiceClassFilterLabelName() string { if pr.ClusterServiceClassExternalName != "" { - return "spec.externalName" + return GroupName + "/" + FilterSpecExternalName } if pr.ClusterServiceClassExternalID != "" { - return "spec.externalID" + return GroupName + "/" + FilterSpecExternalID } return "" } -// GetClusterServicePlanFilterFieldName returns the appropriate field name for filtering +// GetClusterServicePlanFilterLabelName returns the appropriate label name for filtering // a list of service catalog plans by the PlanReference. -func (pr PlanReference) GetClusterServicePlanFilterFieldName() string { +func (pr PlanReference) GetClusterServicePlanFilterLabelName() string { if pr.ClusterServicePlanExternalName != "" { - return "spec.externalName" + return GroupName + "/" + FilterSpecExternalName } if pr.ClusterServicePlanExternalID != "" { - return "spec.externalID" + return GroupName + "/" + FilterSpecExternalID } return "" } -// GetServiceClassFilterFieldName returns the appropriate field name for filtering +// GetServiceClassFilterLabelName returns the appropriate label name for filtering // a list of service catalog classes by the PlanReference. -func (pr PlanReference) GetServiceClassFilterFieldName() string { +func (pr PlanReference) GetServiceClassFilterLabelName() string { if pr.ServiceClassExternalName != "" { - return "spec.externalName" + return GroupName + "/" + FilterSpecExternalName } if pr.ServiceClassExternalID != "" { - return "spec.externalID" + return GroupName + "/" + FilterSpecExternalID } return "" } -// GetServicePlanFilterFieldName returns the appropriate field name for filtering +// GetServicePlanFilterLabelName returns the appropriate label name for filtering // a list of service catalog plans by the PlanReference. -func (pr PlanReference) GetServicePlanFilterFieldName() string { +func (pr PlanReference) GetServicePlanFilterLabelName() string { if pr.ServicePlanExternalName != "" { - return "spec.externalName" + return GroupName + "/" + FilterSpecExternalName } if pr.ServicePlanExternalID != "" { - return "spec.externalID" + return GroupName + "/" + FilterSpecExternalID } return "" diff --git a/pkg/apis/servicecatalog/v1beta1/types.go b/pkg/apis/servicecatalog/v1beta1/types.go index 4f8873e0eb8..f2c1475728b 100644 --- a/pkg/apis/servicecatalog/v1beta1/types.go +++ b/pkg/apis/servicecatalog/v1beta1/types.go @@ -1385,11 +1385,25 @@ const ( // SpecExternalID is the external id of the object. FilterSpecExternalID = "spec.externalID" // SpecServiceBrokerName is used for ServiceClasses, the parent service broker name. + FilterSpecServiceBrokerName = "spec.serviceBrokerName" - // SpecClusterServiceClassName is only used for plans, the parent service class name. - FilterSpecClusterServiceClassName = "spec.clusterServiceClass.name" + // SpecClusterServiceBrokerName is used for ClusterServiceClasses, the parent service broker name. + FilterSpecClusterServiceBrokerName = "spec.clusterServiceBrokerName" + // SpecServiceClassName is only used for plans, the parent service class name. FilterSpecServiceClassName = "spec.serviceClass.name" + // SpecClusterServiceClassName is only used for plans, the parent service class name. + FilterSpecClusterServiceClassName = "spec.clusterServiceClass.name" + // SpecClusterServiceClassRefName is only used for plans, the parent service class name. + FilterSpecServiceClassRefName = "spec.serviceClassRef.name" + // SpecClusterServiceClassRefName is only used for plans, the parent service class name. + FilterSpecClusterServiceClassRefName = "spec.clusterServiceClassRef.name" + + // SpecServicePlanRefName is only used for instances. + FilterSpecServicePlanRefName = "spec.servicePlanRef.name" + // SpecClusterServiceClassRefName is only used for instances. + FilterSpecClusterServicePlanRefName = "spec.clusterServicePlanRef.name" + // FilterSpecFree is only used for plans, determines if the plan is free. FilterSpecFree = "spec.free" ) diff --git a/pkg/client/clientset_generated/clientset/typed/servicecatalog/v1beta1/serviceinstance_expansion.go b/pkg/client/clientset_generated/clientset/typed/servicecatalog/v1beta1/serviceinstance_expansion.go index 0f0312fdf43..59b5194697d 100644 --- a/pkg/client/clientset_generated/clientset/typed/servicecatalog/v1beta1/serviceinstance_expansion.go +++ b/pkg/client/clientset_generated/clientset/typed/servicecatalog/v1beta1/serviceinstance_expansion.go @@ -17,7 +17,9 @@ limitations under the License. package v1beta1 import ( + "encoding/json" "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + "k8s.io/apimachinery/pkg/types" ) // The ServiceInstanceExpansion interface allows setting the References @@ -28,13 +30,41 @@ type ServiceInstanceExpansion interface { func (c *serviceInstances) UpdateReferences(serviceInstance *v1beta1.ServiceInstance) (result *v1beta1.ServiceInstance, err error) { result = &v1beta1.ServiceInstance{} - err = c.client.Put(). + + // TODO(mszostok): replace the subresource "resource" with custom patch + // This is a temporary fix, to make the POC running - https://github.com/kyma-project/kyma/issues/2836 + type serviceInstanceSpecRefPatch struct { + ClusterServiceClassRef *v1beta1.ClusterObjectReference `json:"clusterServiceClassRef,omitempty"` + ClusterServicePlanRef *v1beta1.ClusterObjectReference `json:"clusterServicePlanRef,omitempty"` + ServiceClassRef *v1beta1.LocalObjectReference `json:"serviceClassRef,omitempty"` + ServicePlanRef *v1beta1.LocalObjectReference `json:"servicePlanRef,omitempty"` + } + type serviceInstanceRefPatch struct { + Spec serviceInstanceSpecRefPatch `json:"spec"` + } + + patchedSvc := serviceInstanceRefPatch{ + Spec: serviceInstanceSpecRefPatch{ + + serviceInstance.Spec.ClusterServiceClassRef, + serviceInstance.Spec.ClusterServicePlanRef, + serviceInstance.Spec.ServiceClassRef, + serviceInstance.Spec.ServicePlanRef, + }, + } + + encoded, err := json.Marshal(patchedSvc) + if err != nil { + return result, err + } + + err = c.client.Patch(types.MergePatchType). Namespace(serviceInstance.Namespace). Resource("serviceinstances"). Name(serviceInstance.Name). - SubResource("reference"). - Body(serviceInstance). + Body(encoded). Do(). Into(result) + return } diff --git a/pkg/controller/controller_clusterservicebroker.go b/pkg/controller/controller_clusterservicebroker.go index c62547a8749..0570ef4e52c 100644 --- a/pkg/controller/controller_clusterservicebroker.go +++ b/pkg/controller/controller_clusterservicebroker.go @@ -26,7 +26,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/tools/cache" @@ -213,10 +213,12 @@ func (c *controller) reconcileClusterServiceBroker(broker *v1beta1.ClusterServic if broker.Status.OperationStartTime != nil { toUpdate := broker.DeepCopy() toUpdate.Status.OperationStartTime = nil - if _, err := c.serviceCatalogClient.ClusterServiceBrokers().UpdateStatus(toUpdate); err != nil { + updated, err := c.serviceCatalogClient.ClusterServiceBrokers().UpdateStatus(toUpdate) + if err != nil { klog.Error(pcb.Messagef("Error updating operation start time: %v", err)) return err } + broker = updated } // get the existing services and plans for this broker so that we can @@ -716,11 +718,15 @@ func (c *controller) updateClusterServiceBrokerFinalizers( } func (c *controller) getCurrentServiceClassesAndPlansForBroker(broker *v1beta1.ClusterServiceBroker) ([]v1beta1.ClusterServiceClass, []v1beta1.ClusterServicePlan, error) { - fieldSet := fields.Set{ - "spec.clusterServiceBrokerName": broker.Name, + pcb := pretty.NewClusterServiceBrokerContextBuilder(broker) + + labelSelector := labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: broker.Name, + }).String() + + listOpts := metav1.ListOptions{ + LabelSelector: labelSelector, } - fieldSelector := fields.SelectorFromSet(fieldSet).String() - listOpts := metav1.ListOptions{FieldSelector: fieldSelector} existingServiceClasses, err := c.serviceCatalogClient.ClusterServiceClasses().List(listOpts) if err != nil { @@ -737,6 +743,7 @@ func (c *controller) getCurrentServiceClassesAndPlansForBroker(broker *v1beta1.C return nil, nil, err } + klog.Info(pcb.Messagef("Found %d ServiceClasses", len(existingServiceClasses.Items))) existingServicePlans, err := c.serviceCatalogClient.ClusterServicePlans().List(listOpts) if err != nil { @@ -753,6 +760,7 @@ func (c *controller) getCurrentServiceClassesAndPlansForBroker(broker *v1beta1.C return nil, nil, err } + klog.Info(pcb.Messagef("Found %d ServicePlans", len(existingServicePlans.Items))) return existingServiceClasses.Items, existingServicePlans.Items, nil } diff --git a/pkg/controller/controller_clusterservicebroker_test.go b/pkg/controller/controller_clusterservicebroker_test.go index 5bc3455b0c5..6c39334a032 100644 --- a/pkg/controller/controller_clusterservicebroker_test.go +++ b/pkg/controller/controller_clusterservicebroker_test.go @@ -265,8 +265,10 @@ func TestReconcileClusterServiceBrokerExistingServiceClassAndServicePlan(t *test assertGetCatalog(t, brokerActions[0]) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", "test-clusterservicebroker"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + }), + Fields: fields.Everything(), } actions := fakeCatalogClient.Actions() @@ -314,8 +316,10 @@ func TestReconcileClusterServiceBrokerRemovedClusterServiceClass(t *testing.T) { assertGetCatalog(t, brokerActions[0]) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", "test-clusterservicebroker"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + }), + Fields: fields.Everything(), } actions := fakeCatalogClient.Actions() @@ -382,8 +386,10 @@ func TestReconcileClusterServiceBrokerRemovedAndRestoredClusterServiceClass(t *t assertGetCatalog(t, brokerActions[0]) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", "test-clusterservicebroker"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + }), + Fields: fields.Everything(), } actions := fakeCatalogClient.Actions() @@ -439,8 +445,10 @@ func TestReconcileClusterServiceBrokerRemovedClusterServicePlan(t *testing.T) { assertGetCatalog(t, brokerActions[0]) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", "test-clusterservicebroker"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + }), + Fields: fields.Everything(), } actions := fakeCatalogClient.Actions() @@ -483,8 +491,10 @@ func TestReconcileClusterServiceBrokerExistingClusterServiceClassDifferentBroker assertNumberOfActions(t, actions, 3) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", "test-clusterservicebroker"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + }), + Fields: fields.Everything(), } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions) @@ -535,8 +545,10 @@ func TestReconcileClusterServiceBrokerExistingClusterServicePlanDifferentClass(t assertNumberOfActions(t, actions, 4) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", "test-clusterservicebroker"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + }), + Fields: fields.Everything(), } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions) @@ -662,8 +674,10 @@ func TestReconcileClusterServiceBrokerDelete(t *testing.T) { assertNumberOfActions(t, catalogActions, 7) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", broker.Name), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + }), + Fields: fields.Everything(), } assertList(t, catalogActions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) assertList(t, catalogActions[1], &v1beta1.ClusterServicePlan{}, listRestrictions) @@ -774,8 +788,10 @@ func TestReconcileClusterServiceBrokerZeroServices(t *testing.T) { assertNumberOfActions(t, actions, 3) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", broker.Name), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + }), + Fields: fields.Everything(), } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions) @@ -946,8 +962,10 @@ func TestReconcileClusterServiceBrokerWithReconcileError(t *testing.T) { assertNumberOfActions(t, actions, 4) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", broker.Name), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + }), + Fields: fields.Everything(), } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions) @@ -959,7 +977,7 @@ func TestReconcileClusterServiceBrokerWithReconcileError(t *testing.T) { if !ok { t.Fatalf("couldn't convert to a ClusterServiceClass: %+v", createSCAction.GetObject()) } - if e, a := getTestClusterServiceClass(), createdSC; !reflect.DeepEqual(e, a) { + if e, a := getTestClusterServiceClassWithoutLabels(), createdSC; !reflect.DeepEqual(e, a) { t.Fatalf("unexpected diff for created ClusterServiceClass: %v,\n\nEXPECTED: %+v\n\nACTUAL: %+v", diff.ObjectReflectDiff(e, a), e, a) } updatedClusterServiceBroker := assertUpdateStatus(t, actions[3], broker) @@ -991,11 +1009,18 @@ func TestReconcileClusterServiceBrokerSuccessOnFinalRetry(t *testing.T) { startTime := metav1.NewTime(time.Now().Add(-7 * 24 * time.Hour)) broker.Status.OperationStartTime = &startTime + // simulate real update and return updated object, + // without that fake client will return empty ClusterServiceBrokers struct + fakeCatalogClient.AddReactor("update", "clusterservicebrokers", func(action clientgotesting.Action) (bool, runtime.Object, error) { + e := action.(clientgotesting.UpdateAction) + return true, e.GetObject(), nil + }) if err := reconcileClusterServiceBroker(t, testController, broker); err != nil { t.Fatalf("This should not fail : %v", err) } brokerActions := fakeClusterServiceBrokerClient.Actions() + assertNumberOfBrokerActions(t, brokerActions, 1) assertGetCatalog(t, brokerActions[0]) @@ -1003,8 +1028,10 @@ func TestReconcileClusterServiceBrokerSuccessOnFinalRetry(t *testing.T) { assertNumberOfActions(t, actions, 7) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", broker.Name), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + }), + Fields: fields.Everything(), } // first action should be an update action to clear OperationStartTime @@ -1101,8 +1128,10 @@ func TestReconcileClusterServiceBrokerWithStatusUpdateError(t *testing.T) { assertNumberOfActions(t, actions, 6) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", broker.Name), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + }), + Fields: fields.Everything(), } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) diff --git a/pkg/controller/controller_clusterserviceclass.go b/pkg/controller/controller_clusterserviceclass.go index 6b56115a88c..0e7a9a1637d 100644 --- a/pkg/controller/controller_clusterserviceclass.go +++ b/pkg/controller/controller_clusterserviceclass.go @@ -18,11 +18,11 @@ package controller import ( "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/klog" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" "k8s.io/client-go/tools/cache" ) @@ -81,6 +81,7 @@ func (c *controller) reconcileClusterServiceClass(serviceClass *v1beta1.ClusterS if err != nil { return err } + klog.Infof("Found %d ServiceInstances", len(serviceInstances.Items)) if len(serviceInstances.Items) != 0 { return nil @@ -91,11 +92,13 @@ func (c *controller) reconcileClusterServiceClass(serviceClass *v1beta1.ClusterS } func (c *controller) findServiceInstancesOnClusterServiceClass(serviceClass *v1beta1.ClusterServiceClass) (*v1beta1.ServiceInstanceList, error) { - fieldSet := fields.Set{ - "spec.clusterServiceClassRef.name": serviceClass.Name, + labelSelector := labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: serviceClass.Name, + }).String() + + listOpts := metav1.ListOptions{ + LabelSelector: labelSelector, } - fieldSelector := fields.SelectorFromSet(fieldSet).String() - listOpts := metav1.ListOptions{FieldSelector: fieldSelector} return c.serviceCatalogClient.ServiceInstances(metav1.NamespaceAll).List(listOpts) } diff --git a/pkg/controller/controller_clusterserviceclass_test.go b/pkg/controller/controller_clusterserviceclass_test.go index 73503d6985c..470f2c3d48a 100644 --- a/pkg/controller/controller_clusterserviceclass_test.go +++ b/pkg/controller/controller_clusterserviceclass_test.go @@ -58,8 +58,10 @@ func TestReconcileClusterServiceClassRemovedFromCatalog(t *testing.T) { shouldError: false, catalogActionsCheckFunc: func(t *testing.T, actions []clientgotesting.Action) { listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServiceClassRef.name", "cscguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: "cscguid", + }), + Fields: fields.Everything(), } assertNumberOfActions(t, actions, 1) @@ -73,8 +75,10 @@ func TestReconcileClusterServiceClassRemovedFromCatalog(t *testing.T) { shouldError: false, catalogActionsCheckFunc: func(t *testing.T, actions []clientgotesting.Action) { listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServiceClassRef.name", "cscguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: "cscguid", + }), + Fields: fields.Everything(), } assertNumberOfActions(t, actions, 2) @@ -95,8 +99,10 @@ func TestReconcileClusterServiceClassRemovedFromCatalog(t *testing.T) { errText: strPtr("oops"), catalogActionsCheckFunc: func(t *testing.T, actions []clientgotesting.Action) { listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServiceClassRef.name", "cscguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: "cscguid", + }), + Fields: fields.Everything(), } assertNumberOfActions(t, actions, 2) diff --git a/pkg/controller/controller_clusterserviceplan.go b/pkg/controller/controller_clusterserviceplan.go index a85c453183d..ba738d81238 100644 --- a/pkg/controller/controller_clusterserviceplan.go +++ b/pkg/controller/controller_clusterserviceplan.go @@ -18,11 +18,11 @@ package controller import ( "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/klog" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" "k8s.io/client-go/tools/cache" ) @@ -82,6 +82,7 @@ func (c *controller) reconcileClusterServicePlan(clusterServicePlan *v1beta1.Clu if err != nil { return err } + klog.Infof("Found %d ServiceInstances", len(serviceInstances.Items)) if len(serviceInstances.Items) != 0 { return nil @@ -92,11 +93,13 @@ func (c *controller) reconcileClusterServicePlan(clusterServicePlan *v1beta1.Clu } func (c *controller) findServiceInstancesOnClusterServicePlan(clusterServicePlan *v1beta1.ClusterServicePlan) (*v1beta1.ServiceInstanceList, error) { - fieldSet := fields.Set{ - "spec.clusterServicePlanRef.name": clusterServicePlan.Name, + labelSelector := labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServicePlanRefName: clusterServicePlan.Name, + }).String() + + listOpts := metav1.ListOptions{ + LabelSelector: labelSelector, } - fieldSelector := fields.SelectorFromSet(fieldSet).String() - listOpts := metav1.ListOptions{FieldSelector: fieldSelector} return c.serviceCatalogClient.ServiceInstances(metav1.NamespaceAll).List(listOpts) } diff --git a/pkg/controller/controller_clusterserviceplan_test.go b/pkg/controller/controller_clusterserviceplan_test.go index 5c6fd5d7f11..8c9b58e2910 100644 --- a/pkg/controller/controller_clusterserviceplan_test.go +++ b/pkg/controller/controller_clusterserviceplan_test.go @@ -59,8 +59,10 @@ func TestReconcileClusterServicePlanRemovedFromCatalog(t *testing.T) { shouldError: false, catalogActionsCheckFunc: func(t *testing.T, actions []clientgotesting.Action) { listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServicePlanRef.name", "cspguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServicePlanRefName: "cspguid", + }), + Fields: fields.Everything(), } assertNumberOfActions(t, actions, 1) @@ -74,8 +76,10 @@ func TestReconcileClusterServicePlanRemovedFromCatalog(t *testing.T) { shouldError: false, catalogActionsCheckFunc: func(t *testing.T, actions []clientgotesting.Action) { listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServicePlanRef.name", "cspguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServicePlanRefName: "cspguid", + }), + Fields: fields.Everything(), } assertNumberOfActions(t, actions, 2) @@ -96,8 +100,10 @@ func TestReconcileClusterServicePlanRemovedFromCatalog(t *testing.T) { errText: strPtr("oops"), catalogActionsCheckFunc: func(t *testing.T, actions []clientgotesting.Action) { listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.clusterServicePlanRef.name", "cspguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServicePlanRefName: "cspguid", + }), + Fields: fields.Everything(), } assertNumberOfActions(t, actions, 2) diff --git a/pkg/controller/controller_instance.go b/pkg/controller/controller_instance.go index e850fde27f9..e86f1fd2b59 100644 --- a/pkg/controller/controller_instance.go +++ b/pkg/controller/controller_instance.go @@ -32,7 +32,6 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/sets" @@ -1358,21 +1357,27 @@ func (c *controller) resolveClusterServiceClassRef(instance *v1beta1.ServiceInst ) } } else { - filterField := instance.Spec.GetClusterServiceClassFilterFieldName() + filterLabel := instance.Spec.GetClusterServiceClassFilterLabelName() filterValue := instance.Spec.GetSpecifiedClusterServiceClass() + klog.V(4).Info(pcb.Messagef("looking up a ClusterServiceClass from %s: %q", filterLabel, filterValue)) + labelSelector := labels.SelectorFromSet(labels.Set{ + filterLabel: filterValue, + }).String() - klog.V(4).Info(pcb.Messagef("looking up a ClusterServiceClass from %s: %q", filterField, filterValue)) listOpts := metav1.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(filterField, filterValue).String(), + LabelSelector: labelSelector, } + serviceClasses, err := c.serviceCatalogClient.ClusterServiceClasses().List(listOpts) + klog.Info(pcb.Messagef("Found %d ClusterServiceClasses", len(serviceClasses.Items))) + if err == nil && len(serviceClasses.Items) == 1 { sc = &serviceClasses.Items[0] instance.Spec.ClusterServiceClassRef = &v1beta1.ClusterObjectReference{ Name: sc.Name, } klog.V(4).Info(pcb.Messagef( - "resolved %c to K8S ClusterServiceClass %q", + "resolved %c to ClusterServiceClass %q", instance.Spec.PlanReference, sc.Name, )) } else { @@ -1381,6 +1386,11 @@ func (c *controller) resolveClusterServiceClassRef(instance *v1beta1.ServiceInst instance.Spec.PlanReference, len(serviceClasses.Items), ) } + + klog.V(4).Info(pcb.Messagef( + "resolved %c to ClusterServiceClass %q", + instance.Spec.PlanReference, sc.Name, + )) } return sc, nil @@ -1419,14 +1429,22 @@ func (c *controller) resolveServiceClassRef(instance *v1beta1.ServiceInstance) ( ) } } else { - filterField := instance.Spec.GetServiceClassFilterFieldName() + filterLabel := instance.Spec.GetServiceClassFilterLabelName() filterValue := instance.Spec.GetSpecifiedServiceClass() - klog.V(4).Info(pcb.Messagef("looking up a ServiceClass from %s: %q", filterField, filterValue)) + klog.V(4).Info(pcb.Messagef("looking up a ServiceClass from %s: %q", filterLabel, filterValue)) + + labelSelector := labels.SelectorFromSet(labels.Set{ + filterLabel: filterValue, + }).String() + listOpts := metav1.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(filterField, filterValue).String(), + LabelSelector: labelSelector, } + serviceClasses, err := c.serviceCatalogClient.ServiceClasses(instance.Namespace).List(listOpts) + klog.Info(pcb.Messagef("Found %d ServiceClasses", len(serviceClasses.Items))) + if err == nil && len(serviceClasses.Items) == 1 { sc = &serviceClasses.Items[0] instance.Spec.ServiceClassRef = &v1beta1.LocalObjectReference{ @@ -1476,14 +1494,18 @@ func (c *controller) resolveClusterServicePlanRef(instance *v1beta1.ServiceInsta ) } } else { - fieldSet := fields.Set{ - instance.Spec.GetClusterServicePlanFilterFieldName(): instance.Spec.GetSpecifiedClusterServicePlan(), - "spec.clusterServiceClassRef.name": instance.Spec.ClusterServiceClassRef.Name, - "spec.clusterServiceBrokerName": brokerName, + labelSelector := labels.SelectorFromSet(labels.Set{ + instance.Spec.GetClusterServicePlanFilterLabelName(): instance.Spec.GetSpecifiedClusterServicePlan(), + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: instance.Spec.ClusterServiceClassRef.Name, + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: brokerName, + }).String() + + listOpts := metav1.ListOptions{ + LabelSelector: labelSelector, } - fieldSelector := fields.SelectorFromSet(fieldSet).String() - listOpts := metav1.ListOptions{FieldSelector: fieldSelector} servicePlans, err := c.serviceCatalogClient.ClusterServicePlans().List(listOpts) + klog.Info(pcb.Messagef("Found %d ClusterServicePlans", len(servicePlans.Items))) + if err == nil && len(servicePlans.Items) == 1 { sp := &servicePlans.Items[0] instance.Spec.ClusterServicePlanRef = &v1beta1.ClusterObjectReference{ @@ -1532,14 +1554,18 @@ func (c *controller) resolveServicePlanRef(instance *v1beta1.ServiceInstance, br ) } } else { - fieldSet := fields.Set{ - instance.Spec.GetServicePlanFilterFieldName(): instance.Spec.GetSpecifiedServicePlan(), - "spec.serviceClassRef.name": instance.Spec.ServiceClassRef.Name, - "spec.serviceBrokerName": brokerName, + labelSelector := labels.SelectorFromSet(labels.Set{ + instance.Spec.GetServicePlanFilterLabelName(): instance.Spec.GetSpecifiedServicePlan(), + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceClassRefName: instance.Spec.ServiceClassRef.Name, + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceBrokerName: brokerName, + }).String() + + listOpts := metav1.ListOptions{ + LabelSelector: labelSelector, } - fieldSelector := fields.SelectorFromSet(fieldSet).String() - listOpts := metav1.ListOptions{FieldSelector: fieldSelector} servicePlans, err := c.serviceCatalogClient.ServicePlans(instance.Namespace).List(listOpts) + klog.Info(pcb.Messagef("Found %d ServicePlans", len(servicePlans.Items))) + if err == nil && len(servicePlans.Items) == 1 { sp := &servicePlans.Items[0] instance.Spec.ServicePlanRef = &v1beta1.LocalObjectReference{ @@ -1554,6 +1580,7 @@ func (c *controller) resolveServicePlanRef(instance *v1beta1.ServiceInstance, br instance.Spec.PlanReference, instance.Spec.ServiceClassRef.Name, instance.Spec.PlanReference, len(servicePlans.Items), ) } + } return nil diff --git a/pkg/controller/controller_instance_ns_test.go b/pkg/controller/controller_instance_ns_test.go index 2f6d3d073a2..2a6d9115507 100644 --- a/pkg/controller/controller_instance_ns_test.go +++ b/pkg/controller/controller_instance_ns_test.go @@ -822,14 +822,20 @@ func TestResolveNamespacedReferencesWorks(t *testing.T) { assertNumberOfActions(t, actions, 3) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.externalName", instance.Spec.ServiceClassExternalName), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: instance.Spec.ServiceClassExternalName, + }), + Fields: fields.Everything(), } assertList(t, actions[0], &v1beta1.ServiceClass{}, listRestrictions) listRestrictions = clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.ParseSelectorOrDie("spec.externalName=test-serviceplan,spec.serviceBrokerName=test-servicebroker,spec.serviceClassRef.name=scguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "test-serviceplan", + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceBrokerName: "test-servicebroker", + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceClassRefName: "scguid", + }), + Fields: fields.Everything(), } assertList(t, actions[1], &v1beta1.ServicePlan{}, listRestrictions) diff --git a/pkg/controller/controller_instance_test.go b/pkg/controller/controller_instance_test.go index 2558ba53ffd..ecec2e705bd 100644 --- a/pkg/controller/controller_instance_test.go +++ b/pkg/controller/controller_instance_test.go @@ -115,7 +115,7 @@ func TestReconcileServiceInstanceNonExistentClusterServiceClass(t *testing.T) { ExternalID: testServiceInstanceGUID, }, Status: v1beta1.ServiceInstanceStatus{ - Conditions: []v1beta1.ServiceInstanceCondition{}, + Conditions: []v1beta1.ServiceInstanceCondition{}, DeprovisionStatus: v1beta1.ServiceInstanceDeprovisionStatusNotRequired, }, } @@ -131,8 +131,10 @@ func TestReconcileServiceInstanceNonExistentClusterServiceClass(t *testing.T) { assertNumberOfActions(t, actions, 2) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.externalName", instance.Spec.ClusterServiceClassExternalName), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: instance.Spec.ClusterServiceClassExternalName, + }), + Fields: fields.Everything(), } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) @@ -169,7 +171,7 @@ func TestReconcileServiceInstanceNonExistentClusterServiceClassWithK8SName(t *te ExternalID: testServiceInstanceGUID, }, Status: v1beta1.ServiceInstanceStatus{ - Conditions: []v1beta1.ServiceInstanceCondition{}, + Conditions: []v1beta1.ServiceInstanceCondition{}, DeprovisionStatus: v1beta1.ServiceInstanceDeprovisionStatusNotRequired, }, } @@ -302,7 +304,7 @@ func TestReconcileServiceInstanceNonExistentClusterServicePlan(t *testing.T) { ExternalID: testServiceInstanceGUID, }, Status: v1beta1.ServiceInstanceStatus{ - Conditions: []v1beta1.ServiceInstanceCondition{}, + Conditions: []v1beta1.ServiceInstanceCondition{}, DeprovisionStatus: v1beta1.ServiceInstanceDeprovisionStatusNotRequired, }, } @@ -321,8 +323,12 @@ func TestReconcileServiceInstanceNonExistentClusterServicePlan(t *testing.T) { assertNumberOfActions(t, actions, 2) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.ParseSelectorOrDie("spec.externalName=nothere,spec.clusterServiceBrokerName=test-clusterservicebroker,spec.clusterServiceClassRef.name=cscguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "nothere", + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: "cscguid", + }), + Fields: fields.Everything(), } assertList(t, actions[0], &v1beta1.ClusterServicePlan{}, listRestrictions) @@ -368,7 +374,7 @@ func TestReconcileServiceInstanceNonExistentClusterServicePlanK8SName(t *testing ExternalID: testServiceInstanceGUID, }, Status: v1beta1.ServiceInstanceStatus{ - Conditions: []v1beta1.ServiceInstanceCondition{}, + Conditions: []v1beta1.ServiceInstanceCondition{}, DeprovisionStatus: v1beta1.ServiceInstanceDeprovisionStatusNotRequired, }, } @@ -800,14 +806,20 @@ func TestReconcileServiceInstanceResolvesReferences(t *testing.T) { assertNumberOfActions(t, actions, 3) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.externalName", instance.Spec.ClusterServiceClassExternalName), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: instance.Spec.ClusterServiceClassExternalName, + }), + Fields: fields.Everything(), } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) listRestrictions = clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.ParseSelectorOrDie("spec.externalName=test-clusterserviceplan,spec.clusterServiceBrokerName=test-clusterservicebroker,spec.clusterServiceClassRef.name=cscguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "test-clusterserviceplan", + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: "cscguid", + }), + Fields: fields.Everything(), } assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions) @@ -1004,8 +1016,12 @@ func TestReconcileServiceInstanceResolvesReferencesClusterServiceClassRefAlready assertNumberOfActions(t, actions, 2) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.ParseSelectorOrDie("spec.externalName=test-clusterserviceplan,spec.clusterServiceBrokerName=test-clusterservicebroker,spec.clusterServiceClassRef.name=cscguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "test-clusterserviceplan", + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: "cscguid", + }), + Fields: fields.Everything(), } assertList(t, actions[0], &v1beta1.ClusterServicePlan{}, listRestrictions) @@ -4616,8 +4632,10 @@ func TestResolveReferencesNoClusterServiceClass(t *testing.T) { assertNumberOfActions(t, actions, 2) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.externalName", instance.Spec.ClusterServiceClassExternalName), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: instance.Spec.ClusterServiceClassExternalName, + }), + Fields: fields.Everything(), } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) @@ -4903,14 +4921,20 @@ func TestResolveReferencesNoClusterServicePlan(t *testing.T) { assertNumberOfActions(t, actions, 3) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.externalName", instance.Spec.ClusterServiceClassExternalName), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: instance.Spec.ClusterServiceClassExternalName, + }), + Fields: fields.Everything(), } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) listRestrictions = clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.ParseSelectorOrDie("spec.externalName=test-clusterserviceplan,spec.clusterServiceBrokerName=test-clusterservicebroker,spec.clusterServiceClassRef.name=cscguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "test-clusterserviceplan", + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: "cscguid", + }), + Fields: fields.Everything(), } assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions) @@ -5400,14 +5424,20 @@ func TestResolveReferencesWorks(t *testing.T) { assertNumberOfActions(t, actions, 3) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.externalName", instance.Spec.ClusterServiceClassExternalName), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: instance.Spec.ClusterServiceClassExternalName, + }), + Fields: fields.Everything(), } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) listRestrictions = clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.ParseSelectorOrDie("spec.externalName=test-clusterserviceplan,spec.clusterServiceBrokerName=test-clusterservicebroker,spec.clusterServiceClassRef.name=cscguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "test-clusterserviceplan", + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: "cscguid", + }), + Fields: fields.Everything(), } assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions) @@ -5445,7 +5475,14 @@ func TestResolveReferencesForPlanChange(t *testing.T) { newPlanName := "new-plan-name" sp := &v1beta1.ClusterServicePlan{ - ObjectMeta: metav1.ObjectMeta{Name: newPlanID}, + ObjectMeta: metav1.ObjectMeta{ + Name: newPlanID, + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: newPlanName, + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: testClusterServiceBrokerName, + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: testClusterServiceClassGUID, + }, + }, Spec: v1beta1.ClusterServicePlanSpec{ CommonServicePlanSpec: v1beta1.CommonServicePlanSpec{ ExternalID: newPlanID, @@ -5479,8 +5516,12 @@ func TestResolveReferencesForPlanChange(t *testing.T) { assertNumberOfActions(t, actions, 2) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.ParseSelectorOrDie("spec.externalName=new-plan-name,spec.clusterServiceBrokerName=test-clusterservicebroker,spec.clusterServiceClassRef.name=cscguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "new-plan-name", + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: "test-clusterservicebroker", + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: "cscguid", + }), + Fields: fields.Everything(), } assertList(t, actions[0], &v1beta1.ClusterServicePlan{}, listRestrictions) diff --git a/pkg/controller/controller_ns_test.go b/pkg/controller/controller_ns_test.go index 938f28878d0..15e122d381e 100644 --- a/pkg/controller/controller_ns_test.go +++ b/pkg/controller/controller_ns_test.go @@ -106,6 +106,11 @@ func getTestServiceClass() *v1beta1.ServiceClass { ObjectMeta: metav1.ObjectMeta{ Name: testServiceClassGUID, Namespace: testNamespace, + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceClassRefName: testServiceClassGUID, + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: testServiceClassName, + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceBrokerName: testServiceBrokerName, + }, }, Spec: v1beta1.ServiceClassSpec{ ServiceBrokerName: testServiceBrokerName, @@ -127,6 +132,12 @@ func getTestServicePlan() *v1beta1.ServicePlan { ObjectMeta: metav1.ObjectMeta{ Name: testServicePlanGUID, Namespace: testNamespace, + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecServicePlanRefName: testServicePlanGUID, + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: testServicePlanName, + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceBrokerName: testServiceBrokerName, + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceClassRefName: testServiceClassGUID, + }, }, Spec: v1beta1.ServicePlanSpec{ ServiceBrokerName: testServiceBrokerName, diff --git a/pkg/controller/controller_servicebroker.go b/pkg/controller/controller_servicebroker.go index b622532f620..35f55ac09a2 100644 --- a/pkg/controller/controller_servicebroker.go +++ b/pkg/controller/controller_servicebroker.go @@ -20,14 +20,13 @@ import ( "fmt" "time" - "k8s.io/klog" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/tools/cache" + "k8s.io/klog" "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" "github.com/kubernetes-incubator/service-catalog/pkg/metrics" @@ -179,10 +178,12 @@ func (c *controller) reconcileServiceBroker(broker *v1beta1.ServiceBroker) error if broker.Status.OperationStartTime == nil { toUpdate := broker.DeepCopy() toUpdate.Status.OperationStartTime = &now - if _, err := c.serviceCatalogClient.ServiceBrokers(broker.Namespace).UpdateStatus(toUpdate); err != nil { + updated, err := c.serviceCatalogClient.ServiceBrokers(broker.Namespace).UpdateStatus(toUpdate) + if err != nil { klog.Error(pcb.Messagef("Error updating operation start time: %v", err)) return err } + broker = updated } else if !time.Now().Before(broker.Status.OperationStartTime.Time.Add(c.reconciliationRetryDuration)) { s := "Stopping reconciliation retries because too much time has elapsed" klog.Info(pcb.Message(s)) @@ -697,12 +698,14 @@ func (c *controller) updateServiceBrokerFinalizers( } func (c *controller) getCurrentServiceClassesAndPlansForNamespacedBroker(broker *v1beta1.ServiceBroker) ([]v1beta1.ServiceClass, []v1beta1.ServicePlan, error) { - fieldSet := fields.Set{ - v1beta1.FilterSpecServiceBrokerName: broker.Name, - } - fieldSelector := fields.SelectorFromSet(fieldSet).String() - listOpts := metav1.ListOptions{FieldSelector: fieldSelector} + pcb := pretty.NewServiceBrokerContextBuilder(broker) + labelSelector := labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceBrokerName: broker.Name, + }).String() + listOpts := metav1.ListOptions{ + LabelSelector: labelSelector, + } existingServiceClasses, err := c.serviceCatalogClient.ServiceClasses(broker.Namespace).List(listOpts) if err != nil { c.recorder.Eventf(broker, corev1.EventTypeWarning, errorListingServiceClassesReason, "%v %v", errorListingServiceClassesMessage, err) @@ -718,6 +721,7 @@ func (c *controller) getCurrentServiceClassesAndPlansForNamespacedBroker(broker return nil, nil, err } + klog.Info(pcb.Messagef("Found %d ServiceClasses", len(existingServiceClasses.Items))) existingServicePlans, err := c.serviceCatalogClient.ServicePlans(broker.Namespace).List(listOpts) if err != nil { @@ -734,6 +738,7 @@ func (c *controller) getCurrentServiceClassesAndPlansForNamespacedBroker(broker return nil, nil, err } + klog.Info(pcb.Messagef("Found %d ServicePlans", len(existingServicePlans.Items))) return existingServiceClasses.Items, existingServicePlans.Items, nil } diff --git a/pkg/controller/controller_servicebroker_test.go b/pkg/controller/controller_servicebroker_test.go index 754e23ea6a3..532333cc77e 100644 --- a/pkg/controller/controller_servicebroker_test.go +++ b/pkg/controller/controller_servicebroker_test.go @@ -168,8 +168,10 @@ func TestReconcileServiceBrokerDelete(t *testing.T) { assertNumberOfActions(t, catalogActions, 7) listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.serviceBrokerName", broker.Name), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceBrokerName: broker.Name, + }), + Fields: fields.Everything(), } assertList(t, catalogActions[0], &v1beta1.ServiceClass{}, listRestrictions) assertList(t, catalogActions[1], &v1beta1.ServicePlan{}, listRestrictions) diff --git a/pkg/controller/controller_serviceclass.go b/pkg/controller/controller_serviceclass.go index 7dce15daf88..7350ff06bb3 100644 --- a/pkg/controller/controller_serviceclass.go +++ b/pkg/controller/controller_serviceclass.go @@ -18,12 +18,12 @@ package controller import ( "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/klog" "github.com/kubernetes-incubator/service-catalog/pkg/pretty" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" "k8s.io/client-go/tools/cache" ) @@ -86,6 +86,7 @@ func (c *controller) reconcileServiceClass(serviceClass *v1beta1.ServiceClass) e if err != nil { return err } + klog.Info(pcb.Messagef("Found %d ServiceInstances", len(serviceInstances.Items))) if len(serviceInstances.Items) != 0 { return nil @@ -96,11 +97,13 @@ func (c *controller) reconcileServiceClass(serviceClass *v1beta1.ServiceClass) e } func (c *controller) findServiceInstancesOnServiceClass(serviceClass *v1beta1.ServiceClass) (*v1beta1.ServiceInstanceList, error) { - fieldSet := fields.Set{ - "spec.serviceClassRef.name": serviceClass.Name, + labelSelector := labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceClassRefName: serviceClass.Name, + }).String() + + listOpts := metav1.ListOptions{ + LabelSelector: labelSelector, } - fieldSelector := fields.SelectorFromSet(fieldSet).String() - listOpts := metav1.ListOptions{FieldSelector: fieldSelector} return c.serviceCatalogClient.ServiceInstances(serviceClass.Namespace).List(listOpts) } diff --git a/pkg/controller/controller_serviceclass_test.go b/pkg/controller/controller_serviceclass_test.go index 65e0c3e7f65..88ac647dfef 100644 --- a/pkg/controller/controller_serviceclass_test.go +++ b/pkg/controller/controller_serviceclass_test.go @@ -59,10 +59,11 @@ func TestReconcileServiceClassRemovedFromCatalog(t *testing.T) { shouldError: false, catalogActionsCheckFunc: func(t *testing.T, actions []clientgotesting.Action) { listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.serviceClassRef.name", "scguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceClassRefName: "scguid", + }), + Fields: fields.Everything(), } - assertNumberOfActions(t, actions, 1) assertList(t, actions[0], &v1beta1.ServiceInstance{}, listRestrictions) }, @@ -74,8 +75,10 @@ func TestReconcileServiceClassRemovedFromCatalog(t *testing.T) { shouldError: false, catalogActionsCheckFunc: func(t *testing.T, actions []clientgotesting.Action) { listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.serviceClassRef.name", "scguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceClassRefName: "scguid", + }), + Fields: fields.Everything(), } assertNumberOfActions(t, actions, 2) @@ -96,8 +99,10 @@ func TestReconcileServiceClassRemovedFromCatalog(t *testing.T) { errText: strPtr("oops"), catalogActionsCheckFunc: func(t *testing.T, actions []clientgotesting.Action) { listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.serviceClassRef.name", "scguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceClassRefName: "scguid", + }), + Fields: fields.Everything(), } assertNumberOfActions(t, actions, 2) diff --git a/pkg/controller/controller_serviceplan.go b/pkg/controller/controller_serviceplan.go index 601736914c1..44c04ecd3a4 100644 --- a/pkg/controller/controller_serviceplan.go +++ b/pkg/controller/controller_serviceplan.go @@ -18,12 +18,12 @@ package controller import ( "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/klog" "github.com/kubernetes-incubator/service-catalog/pkg/pretty" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" "k8s.io/client-go/tools/cache" ) @@ -88,6 +88,7 @@ func (c *controller) reconcileServicePlan(servicePlan *v1beta1.ServicePlan) erro if err != nil { return err } + klog.Info(pcb.Messagef("Found %d ServiceInstances", len(serviceInstances.Items))) if len(serviceInstances.Items) != 0 { return nil @@ -98,11 +99,13 @@ func (c *controller) reconcileServicePlan(servicePlan *v1beta1.ServicePlan) erro } func (c *controller) findServiceInstancesOnServicePlan(servicePlan *v1beta1.ServicePlan) (*v1beta1.ServiceInstanceList, error) { - fieldSet := fields.Set{ - "spec.servicePlanRef.name": servicePlan.Name, + labelSelector := labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecServicePlanRefName: servicePlan.Name, + }).String() + + listOpts := metav1.ListOptions{ + LabelSelector: labelSelector, } - fieldSelector := fields.SelectorFromSet(fieldSet).String() - listOpts := metav1.ListOptions{FieldSelector: fieldSelector} return c.serviceCatalogClient.ServiceInstances(metav1.NamespaceAll).List(listOpts) } diff --git a/pkg/controller/controller_serviceplan_test.go b/pkg/controller/controller_serviceplan_test.go index ae620b626e0..6875937c93d 100644 --- a/pkg/controller/controller_serviceplan_test.go +++ b/pkg/controller/controller_serviceplan_test.go @@ -59,8 +59,10 @@ func TestReconcileServicePlanRemovedFromCatalog(t *testing.T) { shouldError: false, catalogActionsCheckFunc: func(t *testing.T, actions []clientgotesting.Action) { listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.servicePlanRef.name", "spguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecServicePlanRefName: "spguid", + }), + Fields: fields.Everything(), } assertNumberOfActions(t, actions, 1) @@ -74,8 +76,10 @@ func TestReconcileServicePlanRemovedFromCatalog(t *testing.T) { shouldError: false, catalogActionsCheckFunc: func(t *testing.T, actions []clientgotesting.Action) { listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.servicePlanRef.name", "spguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecServicePlanRefName: "spguid", + }), + Fields: fields.Everything(), } assertNumberOfActions(t, actions, 2) @@ -96,8 +100,10 @@ func TestReconcileServicePlanRemovedFromCatalog(t *testing.T) { errText: strPtr("oops"), catalogActionsCheckFunc: func(t *testing.T, actions []clientgotesting.Action) { listRestrictions := clientgotesting.ListRestrictions{ - Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.servicePlanRef.name", "spguid"), + Labels: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecServicePlanRefName: "spguid", + }), + Fields: fields.Everything(), } assertNumberOfActions(t, actions, 2) diff --git a/pkg/controller/controller_test.go b/pkg/controller/controller_test.go index e1e47ccb744..6cda80e0fe8 100644 --- a/pkg/controller/controller_test.go +++ b/pkg/controller/controller_test.go @@ -554,7 +554,34 @@ func getTestBearerAuthSecret() *corev1.Secret { func getTestClusterServiceClass() *v1beta1.ClusterServiceClass { broker := getTestClusterServiceBroker() class := &v1beta1.ClusterServiceClass{ - ObjectMeta: metav1.ObjectMeta{Name: testClusterServiceClassGUID}, + ObjectMeta: metav1.ObjectMeta{ + Name: testClusterServiceClassGUID, + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: testClusterServiceClassGUID, + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: testClusterServiceClassName, + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: testClusterServiceBrokerName, + }, + }, + Spec: v1beta1.ClusterServiceClassSpec{ + ClusterServiceBrokerName: testClusterServiceBrokerName, + CommonServiceClassSpec: v1beta1.CommonServiceClassSpec{ + Description: "a test service", + ExternalName: testClusterServiceClassName, + ExternalID: testClusterServiceClassGUID, + Bindable: true, + }, + }, + } + markAsServiceCatalogManagedResource(class, broker) + return class +} + +func getTestClusterServiceClassWithoutLabels() *v1beta1.ClusterServiceClass { + broker := getTestClusterServiceBroker() + class := &v1beta1.ClusterServiceClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: testClusterServiceClassGUID, + }, Spec: v1beta1.ClusterServiceClassSpec{ ClusterServiceBrokerName: testClusterServiceBrokerName, CommonServiceClassSpec: v1beta1.CommonServiceClassSpec{ @@ -572,7 +599,14 @@ func getTestClusterServiceClass() *v1beta1.ClusterServiceClass { func getTestMarkedAsRemovedClusterServiceClass() *v1beta1.ClusterServiceClass { broker := getTestClusterServiceBroker() class := &v1beta1.ClusterServiceClass{ - ObjectMeta: metav1.ObjectMeta{Name: testRemovedClusterServiceClassGUID}, + ObjectMeta: metav1.ObjectMeta{ + Name: testRemovedClusterServiceClassGUID, + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: testClusterServiceClassGUID, + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: testClusterServiceClassName, + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: testClusterServiceBrokerName, + }, + }, Spec: v1beta1.ClusterServiceClassSpec{ ClusterServiceBrokerName: testClusterServiceBrokerName, CommonServiceClassSpec: v1beta1.CommonServiceClassSpec{ @@ -595,7 +629,12 @@ func getTestMarkedAsRemovedClusterServiceClass() *v1beta1.ClusterServiceClass { func getTestRemovedClusterServiceClass() *v1beta1.ClusterServiceClass { broker := getTestClusterServiceBroker() class := &v1beta1.ClusterServiceClass{ - ObjectMeta: metav1.ObjectMeta{Name: testRemovedClusterServiceClassGUID}, + ObjectMeta: metav1.ObjectMeta{ + Name: testRemovedClusterServiceClassGUID, + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: testClusterServiceBrokerName, + }, + }, Spec: v1beta1.ClusterServiceClassSpec{ ClusterServiceBrokerName: testClusterServiceBrokerName, CommonServiceClassSpec: v1beta1.CommonServiceClassSpec{ @@ -648,7 +687,15 @@ func getTestBindingRetrievableServiceClass() *v1beta1.ServiceClass { func getTestClusterServicePlan() *v1beta1.ClusterServicePlan { broker := getTestClusterServiceBroker() plan := &v1beta1.ClusterServicePlan{ - ObjectMeta: metav1.ObjectMeta{Name: testClusterServicePlanGUID}, + ObjectMeta: metav1.ObjectMeta{ + Name: testClusterServicePlanGUID, + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServicePlanRefName: testClusterServicePlanGUID, + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: testClusterServicePlanName, + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: testClusterServiceBrokerName, + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: testClusterServiceClassGUID, + }, + }, Spec: v1beta1.ClusterServicePlanSpec{ ClusterServiceBrokerName: testClusterServiceBrokerName, CommonServicePlanSpec: v1beta1.CommonServicePlanSpec{ @@ -694,7 +741,14 @@ func getTestMarkedAsRemovedClusterServicePlan() *v1beta1.ClusterServicePlan { func getTestRemovedClusterServicePlan() *v1beta1.ClusterServicePlan { broker := getTestClusterServiceBroker() plan := &v1beta1.ClusterServicePlan{ - ObjectMeta: metav1.ObjectMeta{Name: testRemovedClusterServicePlanGUID}, + ObjectMeta: metav1.ObjectMeta{ + Name: testRemovedClusterServicePlanGUID, + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: testRemovedClusterServicePlanGUID, + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: testClusterServiceClassGUID, + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: testClusterServiceBrokerName, + }, + }, Spec: v1beta1.ClusterServicePlanSpec{ ClusterServiceBrokerName: testClusterServiceBrokerName, CommonServicePlanSpec: v1beta1.CommonServicePlanSpec{ @@ -714,7 +768,14 @@ func getTestRemovedClusterServicePlan() *v1beta1.ClusterServicePlan { func getTestClusterServicePlanNonbindable() *v1beta1.ClusterServicePlan { broker := getTestClusterServiceBroker() plan := &v1beta1.ClusterServicePlan{ - ObjectMeta: metav1.ObjectMeta{Name: testNonbindableClusterServicePlanGUID}, + ObjectMeta: metav1.ObjectMeta{ + Name: testNonbindableClusterServicePlanGUID, + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServicePlanRefName: testNonbindableClusterServicePlanGUID, + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: testClusterServicePlanName, + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceBrokerName: testClusterServiceBrokerName, + }, + }, Spec: v1beta1.ClusterServicePlanSpec{ CommonServicePlanSpec: v1beta1.CommonServicePlanSpec{ ExternalName: testNonbindableClusterServicePlanName, @@ -817,6 +878,12 @@ func getTestServiceInstance() *v1beta1.ServiceInstance { Name: testServiceInstanceName, Namespace: testNamespace, Generation: 1, + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceClassRefName: testServiceClassGUID, + v1beta1.GroupName + "/" + v1beta1.FilterSpecServicePlanRefName: testServicePlanGUID, + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: testClusterServiceClassGUID, + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServicePlanRefName: testClusterServicePlanGUID, + }, }, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ @@ -876,7 +943,7 @@ func getTestServiceInstanceK8SNames() *v1beta1.ServiceInstance { ExternalID: testServiceInstanceGUID, }, Status: v1beta1.ServiceInstanceStatus{ - Conditions: []v1beta1.ServiceInstanceCondition{}, + Conditions: []v1beta1.ServiceInstanceCondition{}, DeprovisionStatus: v1beta1.ServiceInstanceDeprovisionStatusNotRequired, }, } diff --git a/pkg/svcat/service-catalog/instance.go b/pkg/svcat/service-catalog/instance.go index 46ab834cb25..706e90f2681 100644 --- a/pkg/svcat/service-catalog/instance.go +++ b/pkg/svcat/service-catalog/instance.go @@ -25,15 +25,10 @@ import ( "github.com/pkg/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/wait" ) -const ( - // FieldServicePlanRef is the jsonpath to an instance's plan name (Kubernetes name). - FieldServicePlanRef = "spec.clusterServicePlanRef.name" -) - // RetrieveInstances lists all instances in a namespace. func (sdk *SDK) RetrieveInstances(ns, classFilter, planFilter string) (*v1beta1.ServiceInstanceList, error) { instances, err := sdk.ServiceCatalog().ServiceInstances(ns).List(v1.ListOptions{}) @@ -88,7 +83,9 @@ func (sdk *SDK) RetrieveInstanceByBinding(b *v1beta1.ServiceBinding, // RetrieveInstancesByPlan retrieves all instances of a plan. func (sdk *SDK) RetrieveInstancesByPlan(plan Plan) ([]v1beta1.ServiceInstance, error) { planOpts := v1.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(FieldServicePlanRef, plan.GetName()).String(), + LabelSelector: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServicePlanRefName: plan.GetName(), + }).String(), } instances, err := sdk.ServiceCatalog().ServiceInstances("").List(planOpts) if err != nil { diff --git a/pkg/svcat/service-catalog/plan.go b/pkg/svcat/service-catalog/plan.go index 2a227f70c6d..be018218712 100644 --- a/pkg/svcat/service-catalog/plan.go +++ b/pkg/svcat/service-catalog/plan.go @@ -22,23 +22,13 @@ import ( "strings" "github.com/hashicorp/go-multierror" + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" ) -const ( - // FieldExternalPlanName is the jsonpath to a plan's external name. - FieldExternalPlanName = "spec.externalName" - - // FieldClusterServiceClassRef is the jsonpath to a plan's associated class name. - FieldClusterServiceClassRef = "spec.clusterServiceClassRef.name" - - // FieldServiceClassRef is the jsonpath to a plan's associated class name. - FieldServiceClassRef = "spec.serviceClassRef.name" -) - // Plan provides a unifying layer of cluster and namespace scoped plan resources. type Plan interface { @@ -138,7 +128,9 @@ func (sdk *SDK) RetrievePlanByName(name string, opts ScopeOptions) (Plan, error) } listOpts := metav1.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(FieldExternalPlanName, name).String(), + LabelSelector: labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: name, + }).String(), } return sdk.retrieveSinglePlanByListOptions(name, opts, listOpts) @@ -155,18 +147,21 @@ func (sdk *SDK) RetrievePlanByClassAndName(className, planName string, opts Scop return nil, err } - var classRefSelector fields.Selector + var classRefSet labels.Set if opts.Scope.Matches(ClusterScope) { - classRefSelector = fields.OneTermEqualSelector(FieldClusterServiceClassRef, class.GetName()) + classRefSet = labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: class.GetName(), + } } else { - classRefSelector = fields.OneTermEqualSelector(FieldServiceClassRef, class.GetName()) + classRefSet = labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceClassRefName: class.GetName(), + } } - listOpts := metav1.ListOptions{ - FieldSelector: fields.AndSelectors( - classRefSelector, - fields.OneTermEqualSelector(FieldExternalPlanName, planName), - ).String(), + LabelSelector: labels.Merge(classRefSet, + labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: planName, + }).String(), } ss := []string{class.GetName(), planName} @@ -175,7 +170,7 @@ func (sdk *SDK) RetrievePlanByClassAndName(className, planName string, opts Scop // RetrievePlanByClassIDAndName gets a plan by its external name and class kube name combination. func (sdk *SDK) RetrievePlanByClassIDAndName(classKubeName, planName string, scopeOpts ScopeOptions) (Plan, error) { - var classRefSelector fields.Selector + var classRefSet labels.Set findError := &multierror.Error{ ErrorFormat: func(errors []error) string { return joinErrors("error:", errors, "\n ") @@ -184,12 +179,14 @@ func (sdk *SDK) RetrievePlanByClassIDAndName(classKubeName, planName string, sco //we run through both of these to support AllScope (i.e. we don't know if its a cluster or namespaced plan) if scopeOpts.Scope.Matches(ClusterScope) { - classRefSelector = fields.OneTermEqualSelector(FieldClusterServiceClassRef, classKubeName) + classRefSet = labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: classKubeName, + } listOpts := metav1.ListOptions{ - FieldSelector: fields.AndSelectors( - classRefSelector, - fields.OneTermEqualSelector(FieldExternalPlanName, planName), - ).String(), + LabelSelector: labels.Merge(classRefSet, + labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: planName, + }).String(), } ss := []string{classKubeName, planName} @@ -201,12 +198,14 @@ func (sdk *SDK) RetrievePlanByClassIDAndName(classKubeName, planName string, sco } } if scopeOpts.Scope.Matches(NamespaceScope) { - classRefSelector = fields.OneTermEqualSelector(FieldServiceClassRef, classKubeName) + classRefSet = labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceClassRefName: classKubeName, + } listOpts := metav1.ListOptions{ - FieldSelector: fields.AndSelectors( - classRefSelector, - fields.OneTermEqualSelector(FieldExternalPlanName, planName), - ).String(), + LabelSelector: labels.Merge(classRefSet, + labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: planName, + }).String(), } ss := []string{classKubeName, planName} diff --git a/pkg/svcat/service-catalog/plan_test.go b/pkg/svcat/service-catalog/plan_test.go index 102a5344a2a..a34ad4d909b 100644 --- a/pkg/svcat/service-catalog/plan_test.go +++ b/pkg/svcat/service-catalog/plan_test.go @@ -18,11 +18,11 @@ package servicecatalog_test import ( "fmt" - "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" "github.com/kubernetes-incubator/service-catalog/pkg/client/clientset_generated/clientset/fake" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/testing" @@ -45,17 +45,52 @@ var _ = Describe("Plan", func() { ) BeforeEach(func() { - csc = &v1beta1.ClusterServiceClass{ObjectMeta: metav1.ObjectMeta{Name: "someclass"}} - csp = &v1beta1.ClusterServicePlan{ObjectMeta: metav1.ObjectMeta{Name: "foobar"}} + csc = &v1beta1.ClusterServiceClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "someclass", + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "foobar", + }, + }, + } + csp = &v1beta1.ClusterServicePlan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foobar", + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "foobar", + }, + }, + } csp2 = &v1beta1.ClusterServicePlan{ - ObjectMeta: metav1.ObjectMeta{Name: "clusterscopedplan"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "clusterscopedplan", + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "clusterscopedplan", + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: csc.Name, + }, + }, Spec: v1beta1.ClusterServicePlanSpec{ ClusterServiceClassRef: v1beta1.ClusterObjectReference{Name: csc.Name}, }, } - sc = &v1beta1.ServiceClass{ObjectMeta: metav1.ObjectMeta{Name: "somenamespacedclass", Namespace: "default"}} + sc = &v1beta1.ServiceClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "somenamespacedclass", + Namespace: "default", + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "somenamespacedclass", + }, + }, + } sp = &v1beta1.ServicePlan{ - ObjectMeta: metav1.ObjectMeta{Name: "foobar", Namespace: sc.Namespace}, + ObjectMeta: metav1.ObjectMeta{ + Name: "foobar", + Namespace: sc.Namespace, + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "foobar", + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceClassRefName: sc.Name, + }, + }, Spec: v1beta1.ServicePlanSpec{ ServiceClassRef: v1beta1.LocalObjectReference{Name: sc.Name}, }, @@ -240,11 +275,12 @@ var _ = Describe("Plan", func() { Expect(plan.GetNamespace()).To(Equal("")) actions := singleClient.Actions() Expect(len(actions)).To(Equal(2)) - fieldSelector := fields.OneTermEqualSelector(FieldClusterServiceClassRef, classKubeName) + labelRequirement, err := labels.NewRequirement(v1beta1.GroupName+"/"+v1beta1.FilterSpecClusterServiceClassRefName, "=", []string{classKubeName}) + Expect(err).NotTo(HaveOccurred()) Expect(actions[0].Matches("list", "clusterserviceplans")).To(BeTrue()) - Expect(actions[0].(testing.ListAction).GetListRestrictions().Fields).To(ContainElement(fieldSelector)) + Expect(actions[0].(testing.ListAction).GetListRestrictions().Labels).To(ContainElement(*labelRequirement)) Expect(actions[1].Matches("list", "serviceplans")).To(BeTrue()) - Expect(actions[1].(testing.ListAction).GetListRestrictions().Fields).To(ContainElement(fieldSelector)) + Expect(actions[1].(testing.ListAction).GetListRestrictions().Labels).To(ContainElement(*labelRequirement)) }) It("Calls the generated v1beta1 List method with the passed in class kube name and plan external name for namespace-scoped plans", func() { classKubeName := sc.Name @@ -271,16 +307,18 @@ var _ = Describe("Plan", func() { Expect(plan.GetNamespace()).To(Equal(sp.Namespace)) actions := singleClient.Actions() Expect(len(actions)).To(Equal(4)) - fieldSelector := fields.OneTermEqualSelector(FieldClusterServiceClassRef, classKubeName) + labelRequirement, err := labels.NewRequirement(v1beta1.GroupName+"/"+v1beta1.FilterSpecClusterServiceClassRefName, "=", []string{classKubeName}) + Expect(err).NotTo(HaveOccurred()) Expect(actions[0].Matches("list", "clusterserviceplans")).To(BeTrue()) - Expect(actions[0].(testing.ListAction).GetListRestrictions().Fields).To(ContainElement(fieldSelector)) + Expect(actions[0].(testing.ListAction).GetListRestrictions().Labels).To(ContainElement(*labelRequirement)) Expect(actions[1].Matches("list", "serviceplans")).To(BeTrue()) - Expect(actions[1].(testing.ListAction).GetListRestrictions().Fields).To(ContainElement(fieldSelector)) - namespacedFieldSelector := fields.OneTermEqualSelector(FieldServiceClassRef, classKubeName) + Expect(actions[1].(testing.ListAction).GetListRestrictions().Labels).To(ContainElement(*labelRequirement)) + namespacedLabelRequirement, err := labels.NewRequirement(v1beta1.GroupName+"/"+v1beta1.FilterSpecServiceClassRefName, "=", []string{classKubeName}) + Expect(err).NotTo(HaveOccurred()) Expect(actions[2].Matches("list", "clusterserviceplans")).To(BeTrue()) - Expect(actions[2].(testing.ListAction).GetListRestrictions().Fields).To(ContainElement(namespacedFieldSelector)) + Expect(actions[2].(testing.ListAction).GetListRestrictions().Labels).To(ContainElement(*namespacedLabelRequirement)) Expect(actions[3].Matches("list", "serviceplans")).To(BeTrue()) - Expect(actions[3].(testing.ListAction).GetListRestrictions().Fields).To(ContainElement(namespacedFieldSelector)) + Expect(actions[3].(testing.ListAction).GetListRestrictions().Labels).To(ContainElement(*namespacedLabelRequirement)) }) It("Bubbles up errors", func() { classKubeName := csc.Name @@ -314,14 +352,16 @@ var _ = Describe("Plan", func() { Expect(err.Error()).Should(ContainSubstring(namespacedErrorMessage)) actions := badClient.Actions() Expect(len(actions)).To(Equal(3)) - fieldSelector := fields.OneTermEqualSelector(FieldClusterServiceClassRef, classKubeName) + labelRequirement, err := labels.NewRequirement(v1beta1.GroupName+"/"+v1beta1.FilterSpecClusterServiceClassRefName, "=", []string{classKubeName}) + Expect(err).NotTo(HaveOccurred()) Expect(actions[0].Matches("list", "clusterserviceplans")).To(BeTrue()) - Expect(actions[0].(testing.ListAction).GetListRestrictions().Fields).To(ContainElement(fieldSelector)) - namespacedFieldSelector := fields.OneTermEqualSelector(FieldServiceClassRef, classKubeName) + Expect(actions[0].(testing.ListAction).GetListRestrictions().Labels).To(ContainElement(*labelRequirement)) + labelNamespacedRequirement, err := labels.NewRequirement(v1beta1.GroupName+"/"+v1beta1.FilterSpecServiceClassRefName, "=", []string{classKubeName}) + Expect(err).NotTo(HaveOccurred()) Expect(actions[1].Matches("list", "clusterserviceplans")).To(BeTrue()) - Expect(actions[1].(testing.ListAction).GetListRestrictions().Fields).To(ContainElement(namespacedFieldSelector)) + Expect(actions[1].(testing.ListAction).GetListRestrictions().Labels).To(ContainElement(*labelNamespacedRequirement)) Expect(actions[2].Matches("list", "serviceplans")).To(BeTrue()) - Expect(actions[2].(testing.ListAction).GetListRestrictions().Fields).To(ContainElement(namespacedFieldSelector)) + Expect(actions[2].(testing.ListAction).GetListRestrictions().Labels).To(ContainElement(*labelNamespacedRequirement)) }) }) Describe("RetrievePlanByID", func() { diff --git a/pkg/webhook/servicecatalog/clusterserviceclass/mutation/handler.go b/pkg/webhook/servicecatalog/clusterserviceclass/mutation/handler.go new file mode 100644 index 00000000000..a9e55debbad --- /dev/null +++ b/pkg/webhook/servicecatalog/clusterserviceclass/mutation/handler.go @@ -0,0 +1,99 @@ +/* +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 mutation + +import ( + "context" + "encoding/json" + "net/http" + + sc "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + webhookutil "github.com/kubernetes-incubator/service-catalog/pkg/webhookutil" + + admissionTypes "k8s.io/api/admission/v1beta1" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// CreateUpdateHandler handles ClusterServiceClass +type CreateUpdateHandler struct { + decoder *admission.Decoder +} + +var _ admission.Handler = &CreateUpdateHandler{} + +// Handle handles admission requests. +func (h *CreateUpdateHandler) Handle(ctx context.Context, req admission.Request) admission.Response { + traced := webhookutil.NewTracedLogger(req.UID) + traced.Infof("Start handling operation: %s for %s: %q", req.Operation, req.Kind.Kind, req.Name) + + cb := &sc.ClusterServiceClass{} + if err := webhookutil.MatchKinds(cb, req.Kind); err != nil { + traced.Errorf("Error matching kinds: %v", err) + return admission.Errored(http.StatusBadRequest, err) + } + + if err := h.decoder.Decode(req, cb); err != nil { + traced.Errorf("Could not decode request object: %v", err) + return admission.Errored(http.StatusBadRequest, err) + } + + mutated := cb.DeepCopy() + switch req.Operation { + case admissionTypes.Create: + h.mutateOnCreate(ctx, mutated) + case admissionTypes.Update: + h.mutateOnUpdate(ctx, mutated) + default: + traced.Infof("ClusterServiceClass mutation wehbook does not support action %q", req.Operation) + return admission.Allowed("action not taken") + } + h.syncLabels(mutated) + rawMutated, err := json.Marshal(mutated) + if err != nil { + traced.Errorf("Error marshaling mutated object: %v", err) + return admission.Errored(http.StatusInternalServerError, err) + } + + traced.Infof("Completed successfully operation: %s for %s: %q", req.Operation, req.Kind.Kind, req.Name) + return admission.PatchResponseFromRaw(req.Object.Raw, rawMutated) +} + +var _ admission.DecoderInjector = &CreateUpdateHandler{} + +// InjectDecoder injects the decoder +func (h *CreateUpdateHandler) InjectDecoder(d *admission.Decoder) error { + h.decoder = d + return nil +} + +func (h *CreateUpdateHandler) mutateOnCreate(ctx context.Context, binding *sc.ClusterServiceClass) { + binding.Finalizers = []string{sc.FinalizerServiceCatalog} +} + +func (h *CreateUpdateHandler) mutateOnUpdate(ctx context.Context, obj *sc.ClusterServiceClass) { + // TODO: implement logic from pkg/registry/servicecatalog/binding/strategy.go +} + +func (h *CreateUpdateHandler) syncLabels(obj *sc.ClusterServiceClass) { + if obj.Labels == nil { + obj.Labels = make(map[string]string) + } + + obj.Labels[sc.GroupName+"/"+sc.FilterSpecExternalID] = obj.Spec.ExternalID + obj.Labels[sc.GroupName+"/"+sc.FilterSpecExternalName] = obj.Spec.ExternalName + obj.Labels[sc.GroupName+"/"+sc.FilterSpecClusterServiceBrokerName] = obj.Spec.ClusterServiceBrokerName +} diff --git a/pkg/webhook/servicecatalog/clusterserviceplan/mutation/handler.go b/pkg/webhook/servicecatalog/clusterserviceplan/mutation/handler.go new file mode 100644 index 00000000000..06f9bb2f4e9 --- /dev/null +++ b/pkg/webhook/servicecatalog/clusterserviceplan/mutation/handler.go @@ -0,0 +1,100 @@ +/* +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 mutation + +import ( + "context" + "encoding/json" + "net/http" + + sc "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + webhookutil "github.com/kubernetes-incubator/service-catalog/pkg/webhookutil" + + admissionTypes "k8s.io/api/admission/v1beta1" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// CreateUpdateHandler handles ClusterServicePlann +type CreateUpdateHandler struct { + decoder *admission.Decoder +} + +var _ admission.Handler = &CreateUpdateHandler{} + +// Handle handles admission requests. +func (h *CreateUpdateHandler) Handle(ctx context.Context, req admission.Request) admission.Response { + traced := webhookutil.NewTracedLogger(req.UID) + traced.Infof("Start handling operation: %s for %s: %q", req.Operation, req.Kind.Kind, req.Name) + + cb := &sc.ClusterServicePlan{} + if err := webhookutil.MatchKinds(cb, req.Kind); err != nil { + traced.Errorf("Error matching kinds: %v", err) + return admission.Errored(http.StatusBadRequest, err) + } + + if err := h.decoder.Decode(req, cb); err != nil { + traced.Errorf("Could not decode request object: %v", err) + return admission.Errored(http.StatusBadRequest, err) + } + + mutated := cb.DeepCopy() + switch req.Operation { + case admissionTypes.Create: + h.mutateOnCreate(ctx, mutated) + case admissionTypes.Update: + h.mutateOnUpdate(ctx, mutated) + default: + traced.Infof("ClusterServicePlan mutation wehbook does not support action %q", req.Operation) + return admission.Allowed("action not taken") + } + h.syncLabels(mutated) + rawMutated, err := json.Marshal(mutated) + if err != nil { + traced.Errorf("Error marshaling mutated object: %v", err) + return admission.Errored(http.StatusInternalServerError, err) + } + + traced.Infof("Completed successfully operation: %s for %s: %q", req.Operation, req.Kind.Kind, req.Name) + return admission.PatchResponseFromRaw(req.Object.Raw, rawMutated) +} + +var _ admission.DecoderInjector = &CreateUpdateHandler{} + +// InjectDecoder injects the decoder +func (h *CreateUpdateHandler) InjectDecoder(d *admission.Decoder) error { + h.decoder = d + return nil +} + +func (h *CreateUpdateHandler) mutateOnCreate(ctx context.Context, binding *sc.ClusterServicePlan) { + +} + +func (h *CreateUpdateHandler) mutateOnUpdate(ctx context.Context, obj *sc.ClusterServicePlan) { + // TODO: implement logic from pkg/registry/servicecatalog/binding/strategy.go +} + +func (h *CreateUpdateHandler) syncLabels(obj *sc.ClusterServicePlan) { + if obj.Labels == nil { + obj.Labels = make(map[string]string) + } + + obj.Labels[sc.GroupName+"/"+sc.FilterSpecExternalID] = obj.Spec.ExternalID + obj.Labels[sc.GroupName+"/"+sc.FilterSpecExternalName] = obj.Spec.ExternalName + obj.Labels[sc.GroupName+"/"+sc.FilterSpecClusterServiceClassRefName] = obj.Spec.ClusterServiceClassRef.Name + obj.Labels[sc.GroupName+"/"+sc.FilterSpecClusterServiceBrokerName] = obj.Spec.ClusterServiceBrokerName +} diff --git a/pkg/webhook/servicecatalog/serviceclass/mutation/handler.go b/pkg/webhook/servicecatalog/serviceclass/mutation/handler.go new file mode 100644 index 00000000000..1cd1b07a152 --- /dev/null +++ b/pkg/webhook/servicecatalog/serviceclass/mutation/handler.go @@ -0,0 +1,99 @@ +/* +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 mutation + +import ( + "context" + "encoding/json" + "net/http" + + sc "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + webhookutil "github.com/kubernetes-incubator/service-catalog/pkg/webhookutil" + + admissionTypes "k8s.io/api/admission/v1beta1" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// CreateUpdateHandler handles ServiceClass +type CreateUpdateHandler struct { + decoder *admission.Decoder +} + +var _ admission.Handler = &CreateUpdateHandler{} + +// Handle handles admission requests. +func (h *CreateUpdateHandler) Handle(ctx context.Context, req admission.Request) admission.Response { + traced := webhookutil.NewTracedLogger(req.UID) + traced.Infof("Start handling operation: %s for %s: %q", req.Operation, req.Kind.Kind, req.Name) + + cb := &sc.ServiceClass{} + if err := webhookutil.MatchKinds(cb, req.Kind); err != nil { + traced.Errorf("Error matching kinds: %v", err) + return admission.Errored(http.StatusBadRequest, err) + } + + if err := h.decoder.Decode(req, cb); err != nil { + traced.Errorf("Could not decode request object: %v", err) + return admission.Errored(http.StatusBadRequest, err) + } + + mutated := cb.DeepCopy() + switch req.Operation { + case admissionTypes.Create: + h.mutateOnCreate(ctx, mutated) + case admissionTypes.Update: + h.mutateOnUpdate(ctx, mutated) + default: + traced.Infof("ServiceClass mutation wehbook does not support action %q", req.Operation) + return admission.Allowed("action not taken") + } + h.syncLabels(mutated) + rawMutated, err := json.Marshal(mutated) + if err != nil { + traced.Errorf("Error marshaling mutated object: %v", err) + return admission.Errored(http.StatusInternalServerError, err) + } + + traced.Infof("Completed successfully operation: %s for %s: %q", req.Operation, req.Kind.Kind, req.Name) + return admission.PatchResponseFromRaw(req.Object.Raw, rawMutated) +} + +var _ admission.DecoderInjector = &CreateUpdateHandler{} + +// InjectDecoder injects the decoder +func (h *CreateUpdateHandler) InjectDecoder(d *admission.Decoder) error { + h.decoder = d + return nil +} + +func (h *CreateUpdateHandler) mutateOnCreate(ctx context.Context, binding *sc.ServiceClass) { + +} + +func (h *CreateUpdateHandler) mutateOnUpdate(ctx context.Context, obj *sc.ServiceClass) { + // TODO: implement logic from pkg/registry/servicecatalog/binding/strategy.go +} + +func (h *CreateUpdateHandler) syncLabels(obj *sc.ServiceClass) { + if obj.Labels == nil { + obj.Labels = make(map[string]string) + } + + obj.Labels[sc.GroupName+"/"+sc.FilterSpecExternalID] = obj.Spec.ExternalID + obj.Labels[sc.GroupName+"/"+sc.FilterSpecExternalName] = obj.Spec.ExternalName + obj.Labels[sc.GroupName+"/"+sc.FilterSpecServiceBrokerName] = obj.Spec.ServiceBrokerName +} diff --git a/pkg/webhook/servicecatalog/serviceplan/mutation/handler.go b/pkg/webhook/servicecatalog/serviceplan/mutation/handler.go new file mode 100644 index 00000000000..ab6afdb9ebf --- /dev/null +++ b/pkg/webhook/servicecatalog/serviceplan/mutation/handler.go @@ -0,0 +1,100 @@ +/* +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 mutation + +import ( + "context" + "encoding/json" + "net/http" + + sc "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + webhookutil "github.com/kubernetes-incubator/service-catalog/pkg/webhookutil" + + admissionTypes "k8s.io/api/admission/v1beta1" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// CreateUpdateHandler handles ServicePlan +type CreateUpdateHandler struct { + decoder *admission.Decoder +} + +var _ admission.Handler = &CreateUpdateHandler{} + +// Handle handles admission requests. +func (h *CreateUpdateHandler) Handle(ctx context.Context, req admission.Request) admission.Response { + traced := webhookutil.NewTracedLogger(req.UID) + traced.Infof("Start handling operation: %s for %s: %q", req.Operation, req.Kind.Kind, req.Name) + + cb := &sc.ServicePlan{} + if err := webhookutil.MatchKinds(cb, req.Kind); err != nil { + traced.Errorf("Error matching kinds: %v", err) + return admission.Errored(http.StatusBadRequest, err) + } + + if err := h.decoder.Decode(req, cb); err != nil { + traced.Errorf("Could not decode request object: %v", err) + return admission.Errored(http.StatusBadRequest, err) + } + + mutated := cb.DeepCopy() + switch req.Operation { + case admissionTypes.Create: + h.mutateOnCreate(ctx, mutated) + case admissionTypes.Update: + h.mutateOnUpdate(ctx, mutated) + default: + traced.Infof("ServicePlan mutation wehbook does not support action %q", req.Operation) + return admission.Allowed("action not taken") + } + h.syncLabels(mutated) + rawMutated, err := json.Marshal(mutated) + if err != nil { + traced.Errorf("Error marshaling mutated object: %v", err) + return admission.Errored(http.StatusInternalServerError, err) + } + + traced.Infof("Completed successfully operation: %s for %s: %q", req.Operation, req.Kind.Kind, req.Name) + return admission.PatchResponseFromRaw(req.Object.Raw, rawMutated) +} + +var _ admission.DecoderInjector = &CreateUpdateHandler{} + +// InjectDecoder injects the decoder +func (h *CreateUpdateHandler) InjectDecoder(d *admission.Decoder) error { + h.decoder = d + return nil +} + +func (h *CreateUpdateHandler) mutateOnCreate(ctx context.Context, binding *sc.ServicePlan) { + +} + +func (h *CreateUpdateHandler) mutateOnUpdate(ctx context.Context, obj *sc.ServicePlan) { + // TODO: implement logic from pkg/registry/servicecatalog/binding/strategy.go +} + +func (h *CreateUpdateHandler) syncLabels(obj *sc.ServicePlan) { + if obj.Labels == nil { + obj.Labels = make(map[string]string) + } + + obj.Labels[sc.GroupName+"/"+sc.FilterSpecExternalID] = obj.Spec.ExternalID + obj.Labels[sc.GroupName+"/"+sc.FilterSpecExternalName] = obj.Spec.ExternalName + obj.Labels[sc.GroupName+"/"+sc.FilterSpecServiceClassRefName] = obj.Spec.ServiceClassRef.Name + obj.Labels[sc.GroupName+"/"+sc.FilterSpecServiceBrokerName] = obj.Spec.ServiceBrokerName +} diff --git a/plugin/pkg/admission/serviceplan/defaultserviceplan/admission.go b/plugin/pkg/admission/serviceplan/defaultserviceplan/admission.go index fbf60577ec7..b445e159395 100644 --- a/plugin/pkg/admission/serviceplan/defaultserviceplan/admission.go +++ b/plugin/pkg/admission/serviceplan/defaultserviceplan/admission.go @@ -25,13 +25,14 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" apimachineryv1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apiserver/pkg/admission" "github.com/kubernetes-incubator/service-catalog/pkg/client/clientset_generated/internalclientset" servicecataloginternalversion "github.com/kubernetes-incubator/service-catalog/pkg/client/clientset_generated/internalclientset/typed/servicecatalog/internalversion" "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog" + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" scadmission "github.com/kubernetes-incubator/service-catalog/pkg/apiserver/admission" ) @@ -260,15 +261,17 @@ func (d *defaultServicePlan) getServiceClassByK8SName(a admission.Attributes, sc } func (d *defaultServicePlan) getClusterServiceClassByField(a admission.Attributes, ref *servicecatalog.PlanReference) (*servicecatalog.ClusterServiceClass, error) { - filterField := ref.GetClusterServiceClassFilterFieldName() + filterLabel := ref.GetClusterServiceClassFilterLabelName() filterValue := ref.GetSpecifiedClusterServiceClass() - klog.V(4).Infof("Fetching ClusterServiceClass filtered by %q = %q", filterField, filterValue) - fieldSet := fields.Set{ - filterField: filterValue, + klog.V(4).Infof("Fetching ClusterServiceClass filtered by %q = %q", filterLabel, filterValue) + labelSelector := labels.SelectorFromSet(labels.Set{ + filterLabel: filterValue, + }).String() + + listOpts := apimachineryv1.ListOptions{ + LabelSelector: labelSelector, } - fieldSelector := fields.SelectorFromSet(fieldSet).String() - listOpts := apimachineryv1.ListOptions{FieldSelector: fieldSelector} serviceClasses, err := d.cscClient.List(listOpts) if err != nil { klog.V(4).Infof("Listing ClusterServiceClasses failed: %q", err) @@ -278,21 +281,23 @@ func (d *defaultServicePlan) getClusterServiceClassByField(a admission.Attribute klog.V(4).Infof("Found single ClusterServiceClass as %+v", serviceClasses.Items[0]) return &serviceClasses.Items[0], nil } - msg := fmt.Sprintf("Could not find a single ClusterServiceClass with %q = %q, found %v", filterField, filterValue, len(serviceClasses.Items)) + msg := fmt.Sprintf("Could not find a single ClusterServiceClass with %q = %q, found %v", filterLabel, filterValue, len(serviceClasses.Items)) klog.V(4).Info(msg) return nil, admission.NewNotFound(a) } func (d *defaultServicePlan) getServiceClassByField(a admission.Attributes, ref *servicecatalog.PlanReference) (*servicecatalog.ServiceClass, error) { - filterField := ref.GetServiceClassFilterFieldName() + filterLabel := ref.GetServiceClassFilterLabelName() filterValue := ref.GetSpecifiedServiceClass() - klog.V(4).Infof("Fetching ServiceClass filtered by %q = %q", filterField, filterValue) - fieldSet := fields.Set{ - filterField: filterValue, + klog.V(4).Infof("Fetching ServiceClass filtered by %q = %q", filterLabel, filterValue) + labelSelector := labels.SelectorFromSet(labels.Set{ + filterLabel: filterValue, + }).String() + + listOpts := apimachineryv1.ListOptions{ + LabelSelector: labelSelector, } - fieldSelector := fields.SelectorFromSet(fieldSet).String() - listOpts := apimachineryv1.ListOptions{FieldSelector: fieldSelector} serviceClasses, err := d.scClient.List(listOpts) if err != nil { klog.V(4).Infof("Listing ServiceClasses failed: %q", err) @@ -302,7 +307,7 @@ func (d *defaultServicePlan) getServiceClassByField(a admission.Attributes, ref klog.V(4).Infof("Found single ServiceClass as %+v", serviceClasses.Items[0]) return &serviceClasses.Items[0], nil } - msg := fmt.Sprintf("Could not find a single ServiceClass with %q = %q, found %v", filterField, filterValue, len(serviceClasses.Items)) + msg := fmt.Sprintf("Could not find a single ServiceClass with %q = %q, found %v", filterLabel, filterValue, len(serviceClasses.Items)) klog.V(4).Info(msg) return nil, admission.NewNotFound(a) } @@ -311,11 +316,13 @@ func (d *defaultServicePlan) getServiceClassByField(a admission.Attributes, ref // ServicePlans for the specified service class name func (d *defaultServicePlan) getClusterServicePlansByClusterServiceClassName(scName string) ([]servicecatalog.ClusterServicePlan, error) { klog.V(4).Infof("Fetching ClusterServicePlans by class name %q", scName) - fieldSet := fields.Set{ - "spec.clusterServiceClassRef.name": scName, + labelSelector := labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: scName, + }).String() + + listOpts := apimachineryv1.ListOptions{ + LabelSelector: labelSelector, } - fieldSelector := fields.SelectorFromSet(fieldSet).String() - listOpts := apimachineryv1.ListOptions{FieldSelector: fieldSelector} servicePlans, err := d.cspClient.List(listOpts) if err != nil { klog.Infof("Listing ClusterServicePlans failed: %q", err) @@ -330,11 +337,13 @@ func (d *defaultServicePlan) getClusterServicePlansByClusterServiceClassName(scN // ServicePlans for the specified service class name func (d *defaultServicePlan) getServicePlansByServiceClassName(scName string) ([]servicecatalog.ServicePlan, error) { klog.V(4).Infof("Fetching ServicePlans by class name %q", scName) - fieldSet := fields.Set{ - "spec.serviceClassRef.name": scName, + labelSelector := labels.SelectorFromSet(labels.Set{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceClassRefName: scName, + }).String() + + listOpts := apimachineryv1.ListOptions{ + LabelSelector: labelSelector, } - fieldSelector := fields.SelectorFromSet(fieldSet).String() - listOpts := apimachineryv1.ListOptions{FieldSelector: fieldSelector} servicePlans, err := d.spClient.List(listOpts) if err != nil { klog.Infof("Listing ServicePlans failed: %q", err) diff --git a/plugin/pkg/admission/serviceplan/defaultserviceplan/admission_test.go b/plugin/pkg/admission/serviceplan/defaultserviceplan/admission_test.go index 1fc4120cece..ad5f523a91e 100644 --- a/plugin/pkg/admission/serviceplan/defaultserviceplan/admission_test.go +++ b/plugin/pkg/admission/serviceplan/defaultserviceplan/admission_test.go @@ -33,6 +33,7 @@ import ( core "k8s.io/client-go/testing" "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog" + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" scadmission "github.com/kubernetes-incubator/service-catalog/pkg/apiserver/admission" "github.com/kubernetes-incubator/service-catalog/pkg/client/clientset_generated/internalclientset" "github.com/kubernetes-incubator/service-catalog/pkg/client/clientset_generated/internalclientset/fake" @@ -147,6 +148,10 @@ func newClusterServiceClass(id string, name string) *servicecatalog.ClusterServi sc := &servicecatalog.ClusterServiceClass{ ObjectMeta: metav1.ObjectMeta{ Name: id, + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalID: id, + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: name, + }, }, Spec: servicecatalog.ClusterServiceClassSpec{ CommonServiceClassSpec: servicecatalog.CommonServiceClassSpec{ @@ -163,6 +168,10 @@ func newServiceClass(id string, name string) *servicecatalog.ServiceClass { sc := &servicecatalog.ServiceClass{ ObjectMeta: metav1.ObjectMeta{ Name: id, + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalID: id, + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: name, + }, }, Spec: servicecatalog.ServiceClassSpec{ CommonServiceClassSpec: servicecatalog.CommonServiceClassSpec{ @@ -175,10 +184,16 @@ func newServiceClass(id string, name string) *servicecatalog.ServiceClass { } // newClusterServicePlans returns new serviceplans. -func newClusterServicePlans(count uint, useDifferentClasses bool) []*servicecatalog.ClusterServicePlan { - classname := "test-serviceclass" +func newClusterServicePlans(classname string, count uint, useDifferentClasses bool) []*servicecatalog.ClusterServicePlan { sp1 := &servicecatalog.ClusterServicePlan{ - ObjectMeta: metav1.ObjectMeta{Name: "bar-id"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "bar-id", + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalID: "12345", + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "bar", + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: classname, + }, + }, Spec: servicecatalog.ClusterServicePlanSpec{ CommonServicePlanSpec: servicecatalog.CommonServicePlanSpec{ ExternalName: "bar", @@ -193,7 +208,14 @@ func newClusterServicePlans(count uint, useDifferentClasses bool) []*servicecata classname = "different-serviceclass" } sp2 := &servicecatalog.ClusterServicePlan{ - ObjectMeta: metav1.ObjectMeta{Name: "baz-id"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "baz-id", + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalID: "23456", + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "baz", + v1beta1.GroupName + "/" + v1beta1.FilterSpecClusterServiceClassRefName: classname, + }, + }, Spec: servicecatalog.ClusterServicePlanSpec{ CommonServicePlanSpec: servicecatalog.CommonServicePlanSpec{ ExternalName: "baz", @@ -218,10 +240,16 @@ func newClusterServicePlans(count uint, useDifferentClasses bool) []*servicecata } // newServicePlans returns new serviceplans. -func newServicePlans(count uint, useDifferentClasses bool) []*servicecatalog.ServicePlan { - classname := "test-serviceclass" +func newServicePlans(classname string, count uint, useDifferentClasses bool) []*servicecatalog.ServicePlan { sp1 := &servicecatalog.ServicePlan{ - ObjectMeta: metav1.ObjectMeta{Name: "bar-id"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "bar-id", + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalID: "12345", + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "bar", + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceClassRefName: classname, + }, + }, Spec: servicecatalog.ServicePlanSpec{ CommonServicePlanSpec: servicecatalog.CommonServicePlanSpec{ ExternalName: "bar", @@ -236,7 +264,14 @@ func newServicePlans(count uint, useDifferentClasses bool) []*servicecatalog.Ser classname = "different-serviceclass" } sp2 := &servicecatalog.ServicePlan{ - ObjectMeta: metav1.ObjectMeta{Name: "baz-id"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "baz-id", + Labels: map[string]string{ + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalID: "23456", + v1beta1.GroupName + "/" + v1beta1.FilterSpecExternalName: "bar", + v1beta1.GroupName + "/" + v1beta1.FilterSpecServiceClassRefName: classname, + }, + }, Spec: servicecatalog.ServicePlanSpec{ CommonServicePlanSpec: servicecatalog.CommonServicePlanSpec{ ExternalName: "baz", @@ -309,9 +344,9 @@ func TestWithPlanWorks(t *testing.T) { t.Run(tc.name, func(t *testing.T) { var fakeClient *fake.Clientset if tc.namespaced { - fakeClient = newFakeServiceCatalogClientForNamespacedTest(nil, newServicePlans(1, false), "" /* do not use get */) + fakeClient = newFakeServiceCatalogClientForNamespacedTest(nil, newServicePlans("", 1, false), "" /* do not use get */) } else { - fakeClient = newFakeServiceCatalogClientForTest(nil, newClusterServicePlans(1, false), "" /* do not use get */) + fakeClient = newFakeServiceCatalogClientForTest(nil, newClusterServicePlans("", 1, false), "" /* do not use get */) } handler, informerFactory, err := newHandlerForTest(fakeClient) if err != nil { @@ -353,9 +388,9 @@ func TestWithNoPlanFailsWithNoClass(t *testing.T) { t.Run(tc.name, func(t *testing.T) { var fakeClient *fake.Clientset if tc.namespaced { - fakeClient = newFakeServiceCatalogClientForNamespacedTest(nil, newServicePlans(1, false), "" /* do not use get */) + fakeClient = newFakeServiceCatalogClientForNamespacedTest(nil, newServicePlans("", 1, false), "" /* do not use get */) } else { - fakeClient = newFakeServiceCatalogClientForTest(nil, newClusterServicePlans(1, false), "" /* do not use get */) + fakeClient = newFakeServiceCatalogClientForTest(nil, newClusterServicePlans("", 1, false), "" /* do not use get */) } handler, informerFactory, err := newHandlerForTest(fakeClient) if err != nil { @@ -388,16 +423,16 @@ func TestWithNoPlanWorksWithSinglePlan(t *testing.T) { servicecatalog.PlanReference{ClusterServiceClassExternalName: "foo"}, servicecatalog.PlanReference{ClusterServiceClassExternalName: "foo", ClusterServicePlanExternalName: "bar"}, false}, {"cluster external id", - servicecatalog.PlanReference{ClusterServiceClassExternalID: "foo"}, - servicecatalog.PlanReference{ClusterServiceClassExternalID: "foo", ClusterServicePlanExternalID: "12345"}, false}, + servicecatalog.PlanReference{ClusterServiceClassExternalID: "foo-id"}, + servicecatalog.PlanReference{ClusterServiceClassExternalID: "foo-id", ClusterServicePlanExternalID: "12345"}, false}, {"cluster k8s", servicecatalog.PlanReference{ClusterServiceClassName: "foo-id"}, servicecatalog.PlanReference{ClusterServiceClassName: "foo-id", ClusterServicePlanName: "bar-id"}, false}, {"ns external name", servicecatalog.PlanReference{ServiceClassExternalName: "foo"}, servicecatalog.PlanReference{ServiceClassExternalName: "foo", ServicePlanExternalName: "bar"}, true}, {"ns external id", - servicecatalog.PlanReference{ServiceClassExternalID: "foo"}, - servicecatalog.PlanReference{ServiceClassExternalID: "foo", ServicePlanExternalID: "12345"}, true}, + servicecatalog.PlanReference{ServiceClassExternalID: "foo-id"}, + servicecatalog.PlanReference{ServiceClassExternalID: "foo-id", ServicePlanExternalID: "12345"}, true}, {"ns k8s", servicecatalog.PlanReference{ServiceClassName: "foo-id"}, servicecatalog.PlanReference{ServiceClassName: "foo-id", ServicePlanName: "bar-id"}, true}, } @@ -407,12 +442,12 @@ func TestWithNoPlanWorksWithSinglePlan(t *testing.T) { var fakeClient *fake.Clientset if tc.namespaced { sc := newServiceClass("foo-id", "foo") - sps := newServicePlans(1, false) + sps := newServicePlans("foo-id", 1, false) klog.V(4).Infof("Created Service as %+v", sc) fakeClient = newFakeServiceCatalogClientForNamespacedTest(sc, sps, "" /* do not use get */) } else { csc := newClusterServiceClass("foo-id", "foo") - csps := newClusterServicePlans(1, false) + csps := newClusterServicePlans("foo-id", 1, false) klog.V(4).Infof("Created Service as %+v", csc) fakeClient = newFakeServiceCatalogClientForTest(csc, csps, "" /* do not use get */) } @@ -449,10 +484,10 @@ func TestWithNoPlanFailsWithMultiplePlans(t *testing.T) { namespaced bool }{ {"cluster external name", servicecatalog.PlanReference{ClusterServiceClassExternalName: "foo"}, false}, - {"cluster external id", servicecatalog.PlanReference{ClusterServiceClassExternalID: "foo"}, false}, + {"cluster external id", servicecatalog.PlanReference{ClusterServiceClassExternalID: "foo-id"}, false}, {"cluster k8s", servicecatalog.PlanReference{ClusterServiceClassName: "foo-id"}, false}, {"ns external name", servicecatalog.PlanReference{ServiceClassExternalName: "foo"}, true}, - {"ns external id", servicecatalog.PlanReference{ServiceClassExternalID: "foo"}, true}, + {"ns external id", servicecatalog.PlanReference{ServiceClassExternalID: "foo-id"}, true}, {"ns k8s", servicecatalog.PlanReference{ServiceClassName: "foo-id"}, true}, } @@ -461,12 +496,12 @@ func TestWithNoPlanFailsWithMultiplePlans(t *testing.T) { var fakeClient *fake.Clientset if tc.namespaced { sc := newServiceClass("foo-id", "foo") - sps := newServicePlans(2, false) + sps := newServicePlans("foo-id", 2, false) klog.V(4).Infof("Created Service as %+v", sc) fakeClient = newFakeServiceCatalogClientForNamespacedTest(sc, sps, "" /* do not use get */) } else { csc := newClusterServiceClass("foo-id", "foo") - csps := newClusterServicePlans(2, false) + csps := newClusterServicePlans("foo-id", 2, false) klog.V(4).Infof("Created Service as %+v", csc) fakeClient = newFakeServiceCatalogClientForTest(csc, csps, "" /* do not use get */) } @@ -503,16 +538,16 @@ func TestWithNoPlanSucceedsWithMultiplePlansFromDifferentClasses(t *testing.T) { servicecatalog.PlanReference{ClusterServiceClassExternalName: "foo"}, servicecatalog.PlanReference{ClusterServiceClassExternalName: "foo", ClusterServicePlanExternalName: "bar"}, false}, {"cluster external id", - servicecatalog.PlanReference{ClusterServiceClassExternalID: "foo"}, - servicecatalog.PlanReference{ClusterServiceClassExternalID: "foo", ClusterServicePlanExternalID: "12345"}, false}, + servicecatalog.PlanReference{ClusterServiceClassExternalID: "foo-id"}, + servicecatalog.PlanReference{ClusterServiceClassExternalID: "foo-id", ClusterServicePlanExternalID: "12345"}, false}, {"cluster k8s", servicecatalog.PlanReference{ClusterServiceClassName: "foo-id"}, servicecatalog.PlanReference{ClusterServiceClassName: "foo-id", ClusterServicePlanName: "bar-id"}, false}, {"ns external name", servicecatalog.PlanReference{ServiceClassExternalName: "foo"}, servicecatalog.PlanReference{ServiceClassExternalName: "foo", ServicePlanExternalName: "bar"}, true}, {"ns external id", - servicecatalog.PlanReference{ServiceClassExternalID: "foo"}, - servicecatalog.PlanReference{ServiceClassExternalID: "foo", ServicePlanExternalID: "12345"}, true}, + servicecatalog.PlanReference{ServiceClassExternalID: "foo-id"}, + servicecatalog.PlanReference{ServiceClassExternalID: "foo-id", ServicePlanExternalID: "12345"}, true}, {"ns k8s", servicecatalog.PlanReference{ServiceClassName: "foo-id"}, servicecatalog.PlanReference{ServiceClassName: "foo-id", ServicePlanName: "bar-id"}, true}, } @@ -520,16 +555,16 @@ func TestWithNoPlanSucceedsWithMultiplePlansFromDifferentClasses(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { var fakeClient *fake.Clientset - classFilter := "test-serviceclass" + classFilter := "foo-id" if tc.namespaced { sc := newServiceClass("foo-id", "foo") - sps := newServicePlans(2, true) + sps := newServicePlans("foo-id", 2, true) klog.V(4).Infof("Created Service as %+v", sc) fakeClient = newFakeServiceCatalogClientForNamespacedTest(sc, sps, classFilter /* do not use get */) } else { csc := newClusterServiceClass("foo-id", "foo") - csps := newClusterServicePlans(2, true) + csps := newClusterServicePlans("foo-id", 2, true) klog.V(4).Infof("Created Service as %+v", csc) fakeClient = newFakeServiceCatalogClientForTest(csc, csps, classFilter /* do not use get */) } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go index adf46495b6d..646d86a87e6 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go @@ -104,21 +104,21 @@ type client struct { } // Create implements client.Client -func (c *client) Create(ctx context.Context, obj runtime.Object) error { +func (c *client) Create(ctx context.Context, obj runtime.Object, opts ...CreateOptionFunc) error { _, ok := obj.(*unstructured.Unstructured) if ok { - return c.unstructuredClient.Create(ctx, obj) + return c.unstructuredClient.Create(ctx, obj, opts...) } - return c.typedClient.Create(ctx, obj) + return c.typedClient.Create(ctx, obj, opts...) } // Update implements client.Client -func (c *client) Update(ctx context.Context, obj runtime.Object) error { +func (c *client) Update(ctx context.Context, obj runtime.Object, opts ...UpdateOptionFunc) error { _, ok := obj.(*unstructured.Unstructured) if ok { - return c.unstructuredClient.Update(ctx, obj) + return c.unstructuredClient.Update(ctx, obj, opts...) } - return c.typedClient.Update(ctx, obj) + return c.typedClient.Update(ctx, obj, opts...) } // Delete implements client.Client @@ -130,6 +130,15 @@ func (c *client) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteO return c.typedClient.Delete(ctx, obj, opts...) } +// Patch implements client.Client +func (c *client) Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOptionFunc) error { + _, ok := obj.(*unstructured.Unstructured) + if ok { + return c.unstructuredClient.Patch(ctx, obj, patch, opts...) + } + return c.typedClient.Patch(ctx, obj, patch, opts...) +} + // Get implements client.Client func (c *client) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error { _, ok := obj.(*unstructured.Unstructured) diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go index 53fd4ad212b..1cf9be68e62 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go @@ -39,6 +39,14 @@ func ObjectKeyFromObject(obj runtime.Object) (ObjectKey, error) { return ObjectKey{Namespace: accessor.GetNamespace(), Name: accessor.GetName()}, nil } +// Patch is a patch that can be applied to a Kubernetes object. +type Patch interface { + // Type is the PatchType of the patch. + Type() types.PatchType + // Data is the raw data representing the patch. + Data(obj runtime.Object) ([]byte, error) +} + // TODO(directxman12): is there a sane way to deal with get/delete options? // Reader knows how to read and list Kubernetes objects. @@ -57,14 +65,18 @@ type Reader interface { // Writer knows how to create, delete, and update Kubernetes objects. type Writer interface { // Create saves the object obj in the Kubernetes cluster. - Create(ctx context.Context, obj runtime.Object) error + Create(ctx context.Context, obj runtime.Object, opts ...CreateOptionFunc) error // Delete deletes the given obj from Kubernetes cluster. Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error // Update updates the given obj in the Kubernetes cluster. obj must be a // struct pointer so that obj can be updated with the content returned by the Server. - Update(ctx context.Context, obj runtime.Object) error + Update(ctx context.Context, obj runtime.Object, opts ...UpdateOptionFunc) error + + // Patch patches the given obj in the Kubernetes cluster. obj must be a + // struct pointer so that obj can be updated with the content returned by the Server. + Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOptionFunc) error } // StatusClient knows how to create a client which can update status subresource @@ -106,6 +118,57 @@ type FieldIndexer interface { IndexField(obj runtime.Object, field string, extractValue IndexerFunc) error } +// CreateOptions contains options for create requests. It's generally a subset +// of metav1.CreateOptions. +type CreateOptions struct { + // When present, indicates that modifications should not be + // persisted. An invalid or unrecognized dryRun directive will + // result in an error response and no further processing of the + // request. Valid values are: + // - All: all dry run stages will be processed + DryRun []string + + // Raw represents raw CreateOptions, as passed to the API server. + Raw *metav1.CreateOptions +} + +// AsCreateOptions returns these options as a metav1.CreateOptions. +// This may mutate the Raw field. +func (o *CreateOptions) AsCreateOptions() *metav1.CreateOptions { + + if o == nil { + return &metav1.CreateOptions{} + } + if o.Raw == nil { + o.Raw = &metav1.CreateOptions{} + } + + o.Raw.DryRun = o.DryRun + return o.Raw +} + +// ApplyOptions executes the given CreateOptionFuncs and returns the mutated +// CreateOptions. +func (o *CreateOptions) ApplyOptions(optFuncs []CreateOptionFunc) *CreateOptions { + for _, optFunc := range optFuncs { + optFunc(o) + } + return o +} + +// CreateOptionFunc is a function that mutates a CreateOptions struct. It implements +// the functional options pattern. See +// https://github.com/tmrts/go-patterns/blob/master/idiom/functional-options.md. +type CreateOptionFunc func(*CreateOptions) + +// CreateDryRunAll is a functional option that sets the DryRun +// field of a CreateOptions struct to metav1.DryRunAll. +func CreateDryRunAll() CreateOptionFunc { + return func(opts *CreateOptions) { + opts.DryRun = []string{metav1.DryRunAll} + } +} + // DeleteOptions contains options for delete requests. It's generally a subset // of metav1.DeleteOptions. type DeleteOptions struct { @@ -326,3 +389,85 @@ func UseListOptions(newOpts *ListOptions) ListOptionFunc { *opts = *newOpts } } + +// UpdateOptions contains options for create requests. It's generally a subset +// of metav1.UpdateOptions. +type UpdateOptions struct { + // When present, indicates that modifications should not be + // persisted. An invalid or unrecognized dryRun directive will + // result in an error response and no further processing of the + // request. Valid values are: + // - All: all dry run stages will be processed + DryRun []string + + // Raw represents raw UpdateOptions, as passed to the API server. + Raw *metav1.UpdateOptions +} + +// AsUpdateOptions returns these options as a metav1.UpdateOptions. +// This may mutate the Raw field. +func (o *UpdateOptions) AsUpdateOptions() *metav1.UpdateOptions { + + if o == nil { + return &metav1.UpdateOptions{} + } + if o.Raw == nil { + o.Raw = &metav1.UpdateOptions{} + } + + o.Raw.DryRun = o.DryRun + return o.Raw +} + +// ApplyOptions executes the given UpdateOptionFuncs and returns the mutated +// UpdateOptions. +func (o *UpdateOptions) ApplyOptions(optFuncs []UpdateOptionFunc) *UpdateOptions { + for _, optFunc := range optFuncs { + optFunc(o) + } + return o +} + +// UpdateOptionFunc is a function that mutates a UpdateOptions struct. It implements +// the functional options pattern. See +// https://github.com/tmrts/go-patterns/blob/master/idiom/functional-options.md. +type UpdateOptionFunc func(*UpdateOptions) + +// UpdateDryRunAll is a functional option that sets the DryRun +// field of a UpdateOptions struct to metav1.DryRunAll. +func UpdateDryRunAll() UpdateOptionFunc { + return func(opts *UpdateOptions) { + opts.DryRun = []string{metav1.DryRunAll} + } +} + +// PatchOptions contains options for patch requests. +type PatchOptions struct { + UpdateOptions +} + +// ApplyOptions executes the given PatchOptionFuncs, mutating these PatchOptions. +// It returns the mutated PatchOptions for convenience. +func (o *PatchOptions) ApplyOptions(optFuncs []PatchOptionFunc) *PatchOptions { + for _, optFunc := range optFuncs { + optFunc(o) + } + return o +} + +// PatchOptionFunc is a function that mutates a PatchOptions struct. It implements +// the functional options pattern. See +// https://github.com/tmrts/go-patterns/blob/master/idiom/functional-options.md. +type PatchOptionFunc func(*PatchOptions) + +// Sadly, we need a separate function to "adapt" PatchOptions to the constituent +// update options, since there's no way to write a function that works for both. + +// UpdatePatchWith adapts the given UpdateOptionFuncs to be a PatchOptionFunc. +func UpdatePatchWith(optFuncs ...UpdateOptionFunc) PatchOptionFunc { + return func(opts *PatchOptions) { + for _, optFunc := range optFuncs { + optFunc(&opts.UpdateOptions) + } + } +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go new file mode 100644 index 00000000000..8951f2e5fee --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go @@ -0,0 +1,73 @@ +/* +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 client + +import ( + jsonpatch "github.com/evanphx/json-patch" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/json" +) + +type patch struct { + patchType types.PatchType + data []byte +} + +// Type implements Patch. +func (s *patch) Type() types.PatchType { + return s.patchType +} + +// Data implements Patch. +func (s *patch) Data(obj runtime.Object) ([]byte, error) { + return s.data, nil +} + +// ConstantPatch constructs a new Patch with the given PatchType and data. +func ConstantPatch(patchType types.PatchType, data []byte) Patch { + return &patch{patchType, data} +} + +type mergeFromPatch struct { + from runtime.Object +} + +// Type implements patch. +func (s *mergeFromPatch) Type() types.PatchType { + return types.MergePatchType +} + +// Data implements Patch. +func (s *mergeFromPatch) Data(obj runtime.Object) ([]byte, error) { + originalJSON, err := json.Marshal(s.from) + if err != nil { + return nil, err + } + + modifiedJSON, err := json.Marshal(obj) + if err != nil { + return nil, err + } + + return jsonpatch.CreateMergePatch(originalJSON, modifiedJSON) +} + +// MergeFrom creates a Patch that patches using the merge-patch strategy with the given object as base. +func MergeFrom(obj runtime.Object) Patch { + return &mergeFromPatch{obj} +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go index 47a90e37d92..011e1421cff 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go @@ -30,31 +30,39 @@ type typedClient struct { } // Create implements client.Client -func (c *typedClient) Create(ctx context.Context, obj runtime.Object) error { +func (c *typedClient) Create(ctx context.Context, obj runtime.Object, opts ...CreateOptionFunc) error { o, err := c.cache.getObjMeta(obj) if err != nil { return err } + + createOpts := &CreateOptions{} + createOpts.ApplyOptions(opts) return o.Post(). NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()). Resource(o.resource()). Body(obj). + VersionedParams(createOpts.AsCreateOptions(), c.paramCodec). Context(ctx). Do(). Into(obj) } // Update implements client.Client -func (c *typedClient) Update(ctx context.Context, obj runtime.Object) error { +func (c *typedClient) Update(ctx context.Context, obj runtime.Object, opts ...UpdateOptionFunc) error { o, err := c.cache.getObjMeta(obj) if err != nil { return err } + + updateOpts := &UpdateOptions{} + updateOpts.ApplyOptions(opts) return o.Put(). NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()). Resource(o.resource()). Name(o.GetName()). Body(obj). + VersionedParams(updateOpts.AsUpdateOptions(), c.paramCodec). Context(ctx). Do(). Into(obj) @@ -78,6 +86,30 @@ func (c *typedClient) Delete(ctx context.Context, obj runtime.Object, opts ...De Error() } +// Patch implements client.Client +func (c *typedClient) Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOptionFunc) error { + o, err := c.cache.getObjMeta(obj) + if err != nil { + return err + } + + data, err := patch.Data(obj) + if err != nil { + return err + } + + patchOpts := &PatchOptions{} + return o.Patch(patch.Type()). + NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()). + Resource(o.resource()). + Name(o.GetName()). + VersionedParams(patchOpts.ApplyOptions(opts).AsUpdateOptions(), c.paramCodec). + Body(data). + Context(ctx). + Do(). + Into(obj) +} + // Get implements client.Client func (c *typedClient) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error { r, err := c.cache.getResource(obj) diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go index 4e616b89465..49700c14bc2 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go @@ -37,16 +37,18 @@ type unstructuredClient struct { } // Create implements client.Client -func (uc *unstructuredClient) Create(_ context.Context, obj runtime.Object) error { +func (uc *unstructuredClient) Create(_ context.Context, obj runtime.Object, opts ...CreateOptionFunc) error { u, ok := obj.(*unstructured.Unstructured) if !ok { return fmt.Errorf("unstructured client did not understand object: %T", obj) } + createOpts := CreateOptions{} + createOpts.ApplyOptions(opts) r, err := uc.getResourceInterface(u.GroupVersionKind(), u.GetNamespace()) if err != nil { return err } - i, err := r.Create(u, metav1.CreateOptions{}) + i, err := r.Create(u, *createOpts.AsCreateOptions()) if err != nil { return err } @@ -55,16 +57,18 @@ func (uc *unstructuredClient) Create(_ context.Context, obj runtime.Object) erro } // Update implements client.Client -func (uc *unstructuredClient) Update(_ context.Context, obj runtime.Object) error { +func (uc *unstructuredClient) Update(_ context.Context, obj runtime.Object, opts ...UpdateOptionFunc) error { u, ok := obj.(*unstructured.Unstructured) if !ok { return fmt.Errorf("unstructured client did not understand object: %T", obj) } + updateOpts := UpdateOptions{} + updateOpts.ApplyOptions(opts) r, err := uc.getResourceInterface(u.GroupVersionKind(), u.GetNamespace()) if err != nil { return err } - i, err := r.Update(u, metav1.UpdateOptions{}) + i, err := r.Update(u, *updateOpts.AsUpdateOptions()) if err != nil { return err } @@ -87,6 +91,31 @@ func (uc *unstructuredClient) Delete(_ context.Context, obj runtime.Object, opts return err } +// Patch implements client.Client +func (uc *unstructuredClient) Patch(_ context.Context, obj runtime.Object, patch Patch, opts ...PatchOptionFunc) error { + u, ok := obj.(*unstructured.Unstructured) + if !ok { + return fmt.Errorf("unstructured client did not understand object: %T", obj) + } + r, err := uc.getResourceInterface(u.GroupVersionKind(), u.GetNamespace()) + if err != nil { + return err + } + + data, err := patch.Data(obj) + if err != nil { + return err + } + + patchOpts := &PatchOptions{} + i, err := r.Patch(u.GetName(), patch.Type(), data, *patchOpts.ApplyOptions(opts).AsUpdateOptions()) + if err != nil { + return err + } + u.Object = i.Object + return nil +} + // Get implements client.Client func (uc *unstructuredClient) Get(_ context.Context, key ObjectKey, obj runtime.Object) error { u, ok := obj.(*unstructured.Unstructured) diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go index 230b78f89cd..60138b904cc 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go @@ -37,6 +37,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/metrics" "sigs.k8s.io/controller-runtime/pkg/recorder" "sigs.k8s.io/controller-runtime/pkg/runtime/inject" + "sigs.k8s.io/controller-runtime/pkg/webhook" ) var log = logf.RuntimeLog.WithName("manager") @@ -58,6 +59,9 @@ type controllerManager struct { // client is the client injected into Controllers (and EventHandlers, Sources and Predicates). client client.Client + // apiReader is the reader that will make requests to the api server and not the cache. + apiReader client.Reader + // fieldIndexes knows how to add field indexes over the Cache used by this controller, // which can later be consumed via field selectors from the injected client. fieldIndexes client.FieldIndexer @@ -90,6 +94,13 @@ type controllerManager struct { internalStopper chan<- struct{} startCache func(stop <-chan struct{}) error + + // port is the port that the webhook server serves at. + port int + // host is the hostname that the webhook server binds to. + host string + + webhookServer *webhook.Server } // Add sets dependencies on i, and adds it to the list of runnables to start. @@ -121,6 +132,9 @@ func (cm *controllerManager) SetFields(i interface{}) error { if _, err := inject.ClientInto(cm.client, i); err != nil { return err } + if _, err := inject.APIReaderInto(cm.apiReader, i); err != nil { + return err + } if _, err := inject.SchemeInto(cm.scheme, i); err != nil { return err } @@ -167,6 +181,23 @@ func (cm *controllerManager) GetRESTMapper() meta.RESTMapper { return cm.mapper } +func (cm *controllerManager) GetAPIReader() client.Reader { + return cm.apiReader +} + +func (cm *controllerManager) GetWebhookServer() *webhook.Server { + if cm.webhookServer == nil { + cm.webhookServer = &webhook.Server{ + Port: cm.port, + Host: cm.host, + } + if err := cm.Add(cm.webhookServer); err != nil { + panic("unable to add webhookServer to the controller manager") + } + } + return cm.webhookServer +} + func (cm *controllerManager) serveMetrics(stop <-chan struct{}) { handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{ ErrorHandling: promhttp.HTTPErrorOnError, diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go index 0fb73d14f36..06e08c8a5a4 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go @@ -36,6 +36,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/leaderelection" "sigs.k8s.io/controller-runtime/pkg/metrics" "sigs.k8s.io/controller-runtime/pkg/recorder" + "sigs.k8s.io/controller-runtime/pkg/webhook" ) // Manager initializes shared dependencies such as Caches and Clients, and provides them to Runnables. @@ -74,6 +75,14 @@ type Manager interface { // GetRESTMapper returns a RESTMapper GetRESTMapper() meta.RESTMapper + + // GetAPIReader returns a reader that will be configured to use the API server. + // This should be used sparingly and only when the client does not fit your + // use case. + GetAPIReader() client.Reader + + // GetWebhookServer returns a webhook.Server + GetWebhookServer() *webhook.Server } // Options are the arguments for creating a new Manager @@ -116,6 +125,13 @@ type Options struct { // for serving prometheus metrics MetricsBindAddress string + // Port is the port that the webhook server serves at. + // It is used to set webhook.Server.Port. + Port int + // Host is the hostname that the webhook server binds to. + // It is used to set webhook.Server.Host. + Host string + // Functions to all for a user to customize the values that will be injected. // NewCache is the function that will create the cache to be used @@ -179,6 +195,11 @@ func New(config *rest.Config, options Options) (Manager, error) { return nil, err } + apiReader, err := client.New(config, client.Options{Scheme: options.Scheme, Mapper: mapper}) + if err != nil { + return nil, err + } + writeObj, err := options.NewClient(cache, config, client.Options{Scheme: options.Scheme, Mapper: mapper}) if err != nil { return nil, err @@ -217,12 +238,15 @@ func New(config *rest.Config, options Options) (Manager, error) { cache: cache, fieldIndexes: cache, client: writeObj, + apiReader: apiReader, recorderProvider: recorderProvider, resourceLock: resourceLock, mapper: mapper, metricsListener: metricsListener, internalStop: stop, internalStopper: stop, + port: options.Port, + host: options.Host, }, nil } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/inject.go b/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/inject.go index c006fdb53d1..b47a91d0428 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/inject.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/inject/inject.go @@ -41,6 +41,20 @@ func CacheInto(c cache.Cache, i interface{}) (bool, error) { return false, nil } +// APIReader is used by the Manager to inject the APIReader into necessary types. +type APIReader interface { + InjectAPIReader(client.Reader) error +} + +// APIReaderInto will set APIReader on i and return the result if it implements APIReaderInto. +// Returns false if i does not implement APIReader +func APIReaderInto(reader client.Reader, i interface{}) (bool, error) { + if s, ok := i.(APIReader); ok { + return true, s.InjectAPIReader(reader) + } + return false, nil +} + // Config is used by the ControllerManager to inject Config into Sources, EventHandlers, Predicates, and // Reconciles type Config interface { diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go index ced04ed060b..e3257a10786 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go @@ -35,7 +35,13 @@ func NewDecoder(scheme *runtime.Scheme) (*Decoder, error) { } // Decode decodes the inlined object in the AdmissionRequest into the passed-in runtime.Object. +// If you want decode the OldObject in the AdmissionRequest, use DecodeRaw. func (d *Decoder) Decode(req Request, into runtime.Object) error { + return d.DecodeRaw(req.Object, into) +} + +// DecodeRaw decodes a RawExtension object into the passed-in runtime.Object. +func (d *Decoder) DecodeRaw(rawObj runtime.RawExtension, into runtime.Object) error { // NB(directxman12): there's a bug/weird interaction between decoders and // the API server where the API server doesn't send a GVK on the embedded // objects, which means the unstructured decoder refuses to decode. It @@ -45,7 +51,7 @@ func (d *Decoder) Decode(req Request, into runtime.Object) error { // See kubernetes/kubernetes#74373. if unstructuredInto, isUnstructured := into.(*unstructured.Unstructured); isUnstructured { // unmarshal into unstructured's underlying object to avoid calling the decoder - if err := json.Unmarshal(req.Object.Raw, &unstructuredInto.Object); err != nil { + if err := json.Unmarshal(rawObj.Raw, &unstructuredInto.Object); err != nil { return err } @@ -53,5 +59,5 @@ func (d *Decoder) Decode(req Request, into runtime.Object) error { } deserializer := d.codecs.UniversalDeserializer() - return runtime.DecodeInto(deserializer, req.AdmissionRequest.Object.Raw, into) + return runtime.DecodeInto(deserializer, rawObj.Raw, into) } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter.go new file mode 100644 index 00000000000..8b255894ba8 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter.go @@ -0,0 +1,75 @@ +/* +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 admission + +import ( + "context" + "encoding/json" + "net/http" + + "k8s.io/apimachinery/pkg/runtime" +) + +// Defaulter defines functions for setting defaults on resources +type Defaulter interface { + runtime.Object + Default() +} + +// DefaultingWebhookFor creates a new Webhook for Defaulting the provided type. +func DefaultingWebhookFor(defaulter Defaulter) *Webhook { + return &Webhook{ + Handler: &mutatingHandler{defaulter: defaulter}, + } +} + +type mutatingHandler struct { + defaulter Defaulter + decoder *Decoder +} + +var _ DecoderInjector = &mutatingHandler{} + +// InjectDecoder injects the decoder into a mutatingHandler. +func (h *mutatingHandler) InjectDecoder(d *Decoder) error { + h.decoder = d + return nil +} + +// Handle handles admission requests. +func (h *mutatingHandler) Handle(ctx context.Context, req Request) Response { + if h.defaulter == nil { + panic("defaulter should never be nil") + } + + // Get the object in the request + obj := h.defaulter.DeepCopyObject().(Defaulter) + err := h.decoder.Decode(req, obj) + if err != nil { + return Errored(http.StatusBadRequest, err) + } + + // Default the object + obj.Default() + marshalled, err := json.Marshal(obj) + if err != nil { + return Errored(http.StatusInternalServerError, err) + } + + // Create the patch + return PatchResponseFromRaw(req.Object.Raw, marshalled) +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator.go new file mode 100644 index 00000000000..282bb9b47cc --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator.go @@ -0,0 +1,93 @@ +/* +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 admission + +import ( + "context" + "net/http" + + "k8s.io/api/admission/v1beta1" + "k8s.io/apimachinery/pkg/runtime" +) + +// Validator defines functions for validating an operation +type Validator interface { + runtime.Object + ValidateCreate() error + ValidateUpdate(old runtime.Object) error +} + +// ValidatingWebhookFor creates a new Webhook for validating the provided type. +func ValidatingWebhookFor(validator Validator) *Webhook { + return &Webhook{ + Handler: &validatingHandler{validator: validator}, + } +} + +type validatingHandler struct { + validator Validator + decoder *Decoder +} + +var _ DecoderInjector = &validatingHandler{} + +// InjectDecoder injects the decoder into a validatingHandler. +func (h *validatingHandler) InjectDecoder(d *Decoder) error { + h.decoder = d + return nil +} + +// Handle handles admission requests. +func (h *validatingHandler) Handle(ctx context.Context, req Request) Response { + if h.validator == nil { + panic("validator should never be nil") + } + + // Get the object in the request + obj := h.validator.DeepCopyObject().(Validator) + if req.Operation == v1beta1.Create { + err := h.decoder.Decode(req, obj) + if err != nil { + return Errored(http.StatusBadRequest, err) + } + + err = obj.ValidateCreate() + if err != nil { + return Denied(err.Error()) + } + } + + if req.Operation == v1beta1.Update { + oldObj := obj.DeepCopyObject() + + err := h.decoder.DecodeRaw(req.Object, obj) + if err != nil { + return Errored(http.StatusBadRequest, err) + } + err = h.decoder.DecodeRaw(req.OldObject, oldObj) + if err != nil { + return Errored(http.StatusBadRequest, err) + } + + err = obj.ValidateUpdate(oldObj) + if err != nil { + return Denied(err.Error()) + } + } + + return Allowed("") +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/alias.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/alias.go index d721878b7fa..27ed60ad183 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/alias.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/alias.go @@ -23,6 +23,12 @@ import ( // define some aliases for common bits of the webhook functionality +// Defaulter defines functions for setting defaults on resources +type Defaulter = admission.Defaulter + +// Validator defines functions for validating an operation +type Validator = admission.Validator + // AdmissionRequest defines the input for an admission handler. // It contains information to identify the object in // question (group, version, kind, resource, subresource, diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go index 25b2eb43e15..19a9888391c 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go @@ -36,6 +36,9 @@ const ( keyName = "tls.key" ) +// DefaultPort is the default port that the webhook server serves. +var DefaultPort = 443 + // Server is an admission webhook server that can serve traffic and // generates related k8s resources for deploying. type Server struct { @@ -45,7 +48,7 @@ type Server struct { // Port is the port number that the server will serve. // It will be defaulted to 443 if unspecified. - Port int32 + Port int // CertDir is the directory that contains the server key and certificate. // If using FSCertWriter in Provisioner, the server itself will provision the certificate and @@ -54,10 +57,9 @@ type Server struct { // the user is responsible to mount the secret to the this location for the server to consume. CertDir string - // TODO(directxman12): should we make the mux configurable? + // WebhookMux is the multiplexer that handles different webhooks. + WebhookMux *http.ServeMux - // webhookMux is the multiplexer that handles different webhooks. - webhookMux *http.ServeMux // webhooks keep track of all registered webhooks for dependency injection, // and to provide better panic messages on duplicate webhook registration. webhooks map[string]http.Handler @@ -72,11 +74,14 @@ type Server struct { // setDefaults does defaulting for the Server. func (s *Server) setDefaults() { s.webhooks = map[string]http.Handler{} - s.webhookMux = http.NewServeMux() + if s.WebhookMux == nil { + s.WebhookMux = http.NewServeMux() + } - if s.Port <= 0 { - s.Port = 443 + if s.Port < 0 { + s.Port = DefaultPort } + if len(s.CertDir) == 0 { s.CertDir = path.Join("/tmp", "k8s-webhook-server", "serving-certs") } @@ -92,7 +97,7 @@ func (s *Server) Register(path string, hook http.Handler) { } // TODO(directxman12): call setfields if we've already started the server s.webhooks[path] = hook - s.webhookMux.Handle(path, instrumentedHook(path, hook)) + s.WebhookMux.Handle(path, instrumentedHook(path, hook)) } // instrumentedHook adds some instrumentation on top of the given webhook. @@ -143,7 +148,7 @@ func (s *Server) Start(stop <-chan struct{}) error { } srv := &http.Server{ - Handler: s.webhookMux, + Handler: s.WebhookMux, } idleConnsClosed := make(chan struct{})