From ee4ff3f3c4cd6c1a3f50feb7e1693d1a03b14d14 Mon Sep 17 00:00:00 2001 From: Jeff Peeler Date: Tue, 24 Oct 2017 14:23:23 -0400 Subject: [PATCH] Squashed 'cmd/service-catalog/go/src/github.com/kubernetes-incubator/service-catalog/' changes from aa27078754..510060232e 510060232e origin build: add origin tooling de45e94bdf v0.1.0 chart changes (#1468) 0bb9982d4b Modify Makefile to only specify ldflags once (#1471) 5d6afac597 Fixes #735: Add repo-sync script for charts (#1453) 630f13f0d7 fix lingering unversioned client API (#1466) 6f49128ac9 Fix several logging errors (#1464) 2aece61fc2 Delete removed serviceClasses when they have no instances left (#1450) 179d3025d3 Uncommenting UID field after updating to k8s 1.8 (#1457) b70c076176 Reorder class and plan creation; test plan conflict handling (#1459) 4bea01279e Use versioned client APIs (#1458) ff4af30d95 clean up logic for 410 gone deprovision poll (#1452) 3fddf27d70 clean up logic and fix message for failed poll (#1451) 40926cdc20 Fix typo from #1354 (#1456) ff86ef28ac Delete removed serviceplans when they have no instances left (#1444) 8411a16281 tweak binding setAndUpdateOrphanMitigation function (#1448) ce28252dac Combine apiserver and controller-manager into a single service-catalog image (#1343) 7bbc8eee5e Check service class / plan before allowing provisioning or plan changes. (#1439) baf28deb79 Create listers before adding event handlers in controller (#1446) 294157dc58 remove setServiceBindingCondition dependency on controller (#1441) 118a0f7b89 Fix typo in validation (#1447) 117bfbddda clean up error logging (#1443) dff470fa19 Move "External" around in some resource names/properties (#1354) 0885edb831 Adding expectedGot function and using it. (#1440) a7d582e9e1 Pretty controller broker (#1442) c5edfaf52e Set apimachinery build variables with semver info (#1429) 0e90d82115 Add a pretty formatter for ClusterService[Class|Plan] (#1408) fb874df919 Remove deprecated basic auth config support (#1431) f4cd181fbf Migrate to metav1 methods for manipulating controllerRefs (#1433) 96b286e966 Make service/plan reference fields on instance spec selectable (#1422) 33f2b04fd6 First example using the pretty context builder. (#1403) 7852917875 Stop using corev1.ObjectReference and corev1.LocalObjectReference (#1417) fcf94803b4 Add tests for plan updates (#1412) 819332e32f Add root CAs (#1419) b49a76af86 Clean Makefile a little (#1399) d681da0068 Use a separate etcd prefix for each integration test to keep tests isolated (#1415) 314a622af3 Wire etcd prefix to storage and call complete with options (#1394) REVERT: aa27078754 origin build: add origin tooling git-subtree-dir: cmd/service-catalog/go/src/github.com/kubernetes-incubator/service-catalog git-subtree-split: 510060232e54eb64b294213bb5d7847e169a2fac --- Makefile | 70 ++-- ORIGIN-SYNC-README.txt | 214 ---------- build/apiserver/Dockerfile | 20 - .../Dockerfile | 9 +- build/version.sh | 174 ++++++++ charts/catalog/README.md | 6 +- .../templates/apiserver-deployment.yaml | 5 +- .../controller-manager-deployment.yaml | 5 +- charts/catalog/values.yaml | 15 +- charts/ups-broker/README.md | 2 +- charts/ups-broker/values.yaml | 2 +- cmd/apiserver/apiserver.go | 54 --- cmd/apiserver/app/plugins.go | 29 -- cmd/apiserver/app/server/etcd_options.go | 11 +- cmd/apiserver/app/server/options.go | 20 +- cmd/apiserver/app/server/plugins.go | 40 ++ cmd/apiserver/app/server/run_server.go | 14 +- cmd/apiserver/app/server/server.go | 102 +---- cmd/service-catalog/apiserver.go | 41 ++ .../controller-manager.go | 45 +-- cmd/service-catalog/main.go | 38 ++ contrib/examples/apiserver/apiserver.yaml | 3 +- contrib/examples/apiserver/instance.yaml | 4 +- .../walkthrough/ups-instance-default-sp.yaml | 2 +- .../examples/walkthrough/ups-instance.yaml | 4 +- contrib/hack/start-server.sh | 2 +- contrib/jenkins/install_catalog.sh | 9 +- docs/devguide.md | 18 +- docs/v1/api-server-walkthrough.md | 6 +- docs/walkthrough-1.6.md | 6 +- docs/walkthrough-1.7.md | 6 +- hack/common.sh | 51 ++- hack/lib/build/constants.sh | 3 +- pkg/apis/servicecatalog/types.go | 57 ++- pkg/apis/servicecatalog/v1beta1/conversion.go | 12 + .../servicecatalog/v1beta1/conversion_test.go | 32 ++ pkg/apis/servicecatalog/v1beta1/register.go | 2 + pkg/apis/servicecatalog/v1beta1/types.go | 59 ++- .../v1beta1/zz_generated.conversion.go | 133 +++++-- .../v1beta1/zz_generated.deepcopy.go | 88 ++++- .../servicecatalog/validation/binding_test.go | 3 +- pkg/apis/servicecatalog/validation/broker.go | 12 +- .../servicecatalog/validation/broker_test.go | 13 +- .../servicecatalog/validation/instance.go | 34 +- .../validation/instance_test.go | 113 +++--- .../validation/serviceplan_test.go | 3 +- .../servicecatalog/zz_generated.deepcopy.go | 88 ++++- pkg/apiserver/etcd_config.go | 2 + pkg/apiserver/etcd_rest_options_factory.go | 2 +- pkg/controller/controller.go | 124 ++---- pkg/controller/controller_binding.go | 96 ++--- pkg/controller/controller_binding_test.go | 148 +++---- pkg/controller/controller_broker.go | 372 ++++++------------ pkg/controller/controller_broker_test.go | 215 ++++++++-- pkg/controller/controller_events_test.go | 35 +- pkg/controller/controller_instance.go | 279 +++++++------ pkg/controller/controller_instance_test.go | 250 ++++++++++-- pkg/controller/controller_serviceclass.go | 65 ++- .../controller_serviceclass_test.go | 137 +++++++ pkg/controller/controller_serviceplan.go | 64 ++- pkg/controller/controller_serviceplan_test.go | 137 +++++++ pkg/controller/controller_test.go | 153 +++++-- pkg/controller/parameters_test.go | 8 +- pkg/hyperkube/README.md | 15 + pkg/hyperkube/hyperkube.go | 256 ++++++++++++ pkg/hyperkube/hyperkube_test.go | 322 +++++++++++++++ pkg/hyperkube/server.go | 77 ++++ pkg/kubernetes/README.md | 2 +- pkg/kubernetes/pkg/util/template/template.go | 49 +++ pkg/openapi/openapi_generated.go | 116 ++++-- pkg/pretty/context_builder.go | 93 +++++ pkg/pretty/context_builder_test.go | 157 ++++++++ pkg/pretty/pretty_kind.go | 44 +++ pkg/pretty/pretty_names.go | 53 +++ pkg/pretty/pretty_names_test.go | 61 +++ .../servicecatalog/binding/storage.go | 5 + .../servicecatalog/binding/strategy_test.go | 5 +- pkg/registry/servicecatalog/broker/storage.go | 5 + .../servicecatalog/instance/storage.go | 19 +- .../servicecatalog/instance/strategy.go | 2 +- .../servicecatalog/instance/strategy_test.go | 11 +- .../rest/storage_servicecatalog_test.go | 3 +- .../servicecatalog/serviceclass/storage.go | 5 + pkg/version.go | 24 +- pkg/version/base.go | 59 +++ pkg/version/version.go | 16 +- .../broker/authsarcheck/admission.go | 11 +- .../broker/authsarcheck/admission_test.go | 29 +- .../namespace/lifecycle/admission.go | 2 +- .../lifecycle/admission_test.go | 3 +- .../serviceplan/changevalidator/admission.go | 10 +- .../changevalidator/admission_test.go | 4 +- .../defaultserviceplan/admission.go | 18 +- .../defaultserviceplan/admission_test.go | 31 +- test/e2e/framework/util.go | 6 +- test/e2e/instance.go | 4 +- test/e2e/walkthrough.go | 9 +- test/integration.sh | 4 +- test/integration/clientset_test.go | 51 ++- test/integration/controller_test.go | 149 ++++--- .../deleted_services_and_plans_test.go | 193 +++++++++ test/integration/framework.go | 4 +- test/integration/util.go | 5 + test/repo-sync.sh | 45 +++ test/util/assertions.go | 159 ++++++++ test/util/util.go | 36 ++ .../plugin/namespace/lifecycle/admission.go | 2 +- 107 files changed, 4196 insertions(+), 1744 deletions(-) delete mode 100644 ORIGIN-SYNC-README.txt delete mode 100644 build/apiserver/Dockerfile rename build/{controller-manager => service-catalog}/Dockerfile (72%) create mode 100755 build/version.sh delete mode 100644 cmd/apiserver/apiserver.go delete mode 100644 cmd/apiserver/app/plugins.go create mode 100644 cmd/apiserver/app/server/plugins.go create mode 100644 cmd/service-catalog/apiserver.go rename cmd/{controller-manager => service-catalog}/controller-manager.go (52%) create mode 100644 cmd/service-catalog/main.go create mode 100644 pkg/controller/controller_serviceclass_test.go create mode 100644 pkg/controller/controller_serviceplan_test.go create mode 100644 pkg/hyperkube/README.md create mode 100644 pkg/hyperkube/hyperkube.go create mode 100644 pkg/hyperkube/hyperkube_test.go create mode 100644 pkg/hyperkube/server.go create mode 100644 pkg/kubernetes/pkg/util/template/template.go create mode 100644 pkg/pretty/context_builder.go create mode 100644 pkg/pretty/context_builder_test.go create mode 100644 pkg/pretty/pretty_kind.go create mode 100644 pkg/pretty/pretty_names.go create mode 100644 pkg/pretty/pretty_names_test.go create mode 100644 pkg/version/base.go create mode 100644 test/integration/deleted_services_and_plans_test.go create mode 100644 test/repo-sync.sh create mode 100644 test/util/assertions.go diff --git a/Makefile b/Makefile index 23ff6d59616e..d6f4dae9e3bb 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,7 @@ SRC_DIRS = $(shell sh -c "find $(TOP_SRC_DIRS) -name \\*.go \ TEST_DIRS ?= $(shell sh -c "find $(TOP_SRC_DIRS) -name \\*_test.go \ -exec dirname {} \\; | sort | uniq") VERSION ?= $(shell git describe --always --abbrev=7 --dirty) +BUILD_LDFLAGS = $(shell build/version.sh $(ROOT) $(SC_PKG)) # Run stat against /dev/null and check if it has any stdout output. # If stdout is blank, we are detecting bsd-stat because stat it has @@ -73,15 +74,13 @@ $(error Unsupported platform to compile for) endif GO_BUILD = env GOOS=$(PLATFORM) GOARCH=$(ARCH) go build -i $(GOFLAGS) \ - -ldflags "-X $(SC_PKG)/pkg.VERSION=$(VERSION)" + -ldflags "-X $(SC_PKG)/pkg.VERSION=$(VERSION) $(BUILD_LDFLAGS)" BASE_PATH = $(ROOT:/src/github.com/kubernetes-incubator/service-catalog/=) export GOPATH = $(BASE_PATH):$(ROOT)/vendor MUTABLE_TAG ?= canary -APISERVER_IMAGE = $(REGISTRY)apiserver-$(ARCH):$(VERSION) -APISERVER_MUTABLE_IMAGE = $(REGISTRY)apiserver-$(ARCH):$(MUTABLE_TAG) -CONTROLLER_MANAGER_IMAGE = $(REGISTRY)controller-manager-$(ARCH):$(VERSION) -CONTROLLER_MANAGER_MUTABLE_IMAGE = $(REGISTRY)controller-manager-$(ARCH):$(MUTABLE_TAG) +SERVICE_CATALOG_IMAGE = $(REGISTRY)service-catalog-$(ARCH):$(VERSION) +SERVICE_CATALOG_MUTABLE_IMAGE = $(REGISTRY)service-catalog-$(ARCH):$(MUTABLE_TAG) USER_BROKER_IMAGE = $(REGISTRY)user-broker-$(ARCH):$(VERSION) USER_BROKER_MUTABLE_IMAGE = $(REGISTRY)user-broker-$(ARCH):$(MUTABLE_TAG) @@ -109,11 +108,10 @@ NON_VENDOR_DIRS = $(shell $(DOCKER_CMD) glide nv) # This section builds the output binaries. # Some will have dedicated targets to make it easier to type, for example -# "apiserver" instead of "bin/apiserver". +# "service-catalog" instead of "bin/service-catalog". ######################################################################### build: .init .generate_files \ - $(BINDIR)/apiserver \ - $(BINDIR)/controller-manager \ + $(BINDIR)/service-catalog \ $(BINDIR)/user-broker user-broker: $(BINDIR)/user-broker @@ -122,14 +120,10 @@ $(BINDIR)/user-broker: .init contrib/cmd/user-broker \ $(shell find contrib/pkg/broker -type f) $(DOCKER_CMD) $(GO_BUILD) -o $@ $(SC_PKG)/contrib/cmd/user-broker -# We'll rebuild apiserver if any go file has changed (ie. NEWEST_GO_FILE) -apiserver: $(BINDIR)/apiserver -$(BINDIR)/apiserver: .init .generate_files cmd/apiserver $(NEWEST_GO_FILE) - $(DOCKER_CMD) $(GO_BUILD) -o $@ $(SC_PKG)/cmd/apiserver - -controller-manager: $(BINDIR)/controller-manager -$(BINDIR)/controller-manager: .init .generate_files cmd/controller-manager $(NEWEST_GO_FILE) - $(DOCKER_CMD) $(GO_BUILD) -o $@ $(SC_PKG)/cmd/controller-manager +# We'll rebuild service-catalog if any go file has changed (ie. NEWEST_GO_FILE) +service-catalog: $(BINDIR)/service-catalog +$(BINDIR)/service-catalog: .init .generate_files cmd/service-catalog $(NEWEST_GO_FILE) + $(DOCKER_CMD) $(GO_BUILD) -o $@ $(SC_PKG)/cmd/service-catalog # This section contains the code generation stuff ################################################# @@ -220,8 +214,9 @@ $(BINDIR)/e2e.test: .init $(NEWEST_E2ETEST_SOURCE) $(NEWEST_GO_FILE) verify: .init .generate_files verify-client-gen @echo Running gofmt: @$(DOCKER_CMD) gofmt -l -s $(TOP_TEST_DIRS) $(TOP_SRC_DIRS)>.out 2>&1||true - @bash -c '[ "`cat .out`" == "" ] || \ - (echo -e "\n*** Please 'gofmt' the following:" ; cat .out ; echo ; false)' + @[ ! -s .out ] || \ + (echo && echo "*** Please 'gofmt' the following:" && \ + cat .out && echo && rm .out && false) @rm .out @# @echo Running golint and go vet: @@ -240,7 +235,7 @@ verify: .init .generate_files verify-client-gen $(DOCKER_CMD) go vet $(NON_VENDOR_DIRS) @echo Running repo-infra verify scripts @$(DOCKER_CMD) vendor/github.com/kubernetes/repo-infra/verify/verify-boilerplate.sh --rootdir=. | grep -v generated > .out 2>&1 || true - @bash -c '[ "`cat .out`" == "" ] || (cat .out ; false)' + @[ ! -s .out ] || (cat .out && rm .out && false) @rm .out @# @echo Running href checker: @@ -328,7 +323,7 @@ clean-coverage: # Building Docker Images for our executables ############################################ -images: user-broker-image controller-manager-image apiserver-image +images: user-broker-image service-catalog-image images-all: $(addprefix arch-image-,$(ALL_ARCH)) arch-image-%: @@ -357,24 +352,17 @@ ifeq ($(ARCH),amd64) docker tag $(USER_BROKER_MUTABLE_IMAGE) $(REGISTRY)user-broker:$(MUTABLE_TAG) endif -apiserver-image: build/apiserver/Dockerfile $(BINDIR)/apiserver - $(call build-and-tag,"apiserver",$(APISERVER_IMAGE),$(APISERVER_MUTABLE_IMAGE)) -ifeq ($(ARCH),amd64) - docker tag $(APISERVER_IMAGE) $(REGISTRY)apiserver:$(VERSION) - docker tag $(APISERVER_MUTABLE_IMAGE) $(REGISTRY)apiserver:$(MUTABLE_TAG) -endif - -controller-manager-image: build/controller-manager/Dockerfile $(BINDIR)/controller-manager - $(call build-and-tag,"controller-manager",$(CONTROLLER_MANAGER_IMAGE),$(CONTROLLER_MANAGER_MUTABLE_IMAGE)) +service-catalog-image: build/service-catalog/Dockerfile $(BINDIR)/service-catalog + $(call build-and-tag,"service-catalog",$(SERVICE_CATALOG_IMAGE),$(SERVICE_CATALOG_MUTABLE_IMAGE)) ifeq ($(ARCH),amd64) - docker tag $(CONTROLLER_MANAGER_IMAGE) $(REGISTRY)controller-manager:$(VERSION) - docker tag $(CONTROLLER_MANAGER_MUTABLE_IMAGE) $(REGISTRY)controller-manager:$(MUTABLE_TAG) + docker tag $(SERVICE_CATALOG_IMAGE) $(REGISTRY)service-catalog:$(VERSION) + docker tag $(SERVICE_CATALOG_MUTABLE_IMAGE) $(REGISTRY)service-catalog:$(MUTABLE_TAG) endif # Push our Docker Images to a registry ###################################### -push: user-broker-push controller-manager-push apiserver-push +push: user-broker-push service-catalog-push user-broker-push: user-broker-image docker push $(USER_BROKER_IMAGE) @@ -384,20 +372,12 @@ ifeq ($(ARCH),amd64) docker push $(REGISTRY)user-broker:$(MUTABLE_TAG) endif -controller-manager-push: controller-manager-image - docker push $(CONTROLLER_MANAGER_IMAGE) - docker push $(CONTROLLER_MANAGER_MUTABLE_IMAGE) -ifeq ($(ARCH),amd64) - docker push $(REGISTRY)controller-manager:$(VERSION) - docker push $(REGISTRY)controller-manager:$(MUTABLE_TAG) -endif - -apiserver-push: apiserver-image - docker push $(APISERVER_IMAGE) - docker push $(APISERVER_MUTABLE_IMAGE) +service-catalog-push: service-catalog-image + docker push $(SERVICE_CATALOG_IMAGE) + docker push $(SERVICE_CATALOG_MUTABLE_IMAGE) ifeq ($(ARCH),amd64) - docker push $(REGISTRY)apiserver:$(VERSION) - docker push $(REGISTRY)apiserver:$(MUTABLE_TAG) + docker push $(REGISTRY)service-catalog:$(VERSION) + docker push $(REGISTRY)service-catalog:$(MUTABLE_TAG) endif diff --git a/ORIGIN-SYNC-README.txt b/ORIGIN-SYNC-README.txt deleted file mode 100644 index b981984dce74..000000000000 --- a/ORIGIN-SYNC-README.txt +++ /dev/null @@ -1,214 +0,0 @@ -JAY&JEFF: review git push - previous discussion was to add explicit remotes to every push - -This is a how-to for syncing a given tag from service-catalog and merging -it into the openshift/origin repository. At the bottom are build instructions -and notes on testing - - -Prerequisite setup: -- git clone of service-catalog repo from https://github.com/kubernetes-incubator/service-catalog.git -- $ git remote add openshift git@github.com:openshift/service-catalog.git -- ensure there aren't any patches in the openshift/origin repo that need to be - put in the openshift/service-catalog:origin-patches branch. From origin repository: - $ git log cmd/service-catalog - if there are commits since the last sync these will need to be dealt with. - -Because git allows you to set up your remotes as you wish, we'll define the -remotes by name and URL here: -In service-catalog repo -$ git remote -v -openshift git@github.com:openshift/service-catalog.git (fetch) -openshift git@github.com:openshift/service-catalog.git (push) -origin https://github.com/kubernetes-incubator/service-catalog.git (fetch) -origin https://github.com/kubernetes-incubator/service-catalog.git (push) - -In Origin repo: -$ git remote -v -origin git@github.com:openshift/origin.git (fetch) -origin git@github.com:openshift/origin.git (push) -sc https://github.com/openshift/service-catalog (fetch) -sc https://github.com/openshift/service-catalog (push) - -the "sc" remote is required for a later step where we rebase the SC git subtree. - -1. Begin work in the Service Catalog repo -# download updates -$ git fetch origin -$ git fetch openshift - -# syncs the openshift/service-catalog repo with the upstream tag -# (in service-catalog repo) -$ TAG=v0.0.10 -$ git pull origin master -$ git push openshift $TAG - -# updates master branch in service-catalog repo (unused, but the master branch -# is the first thing people see.) -$ git checkout openshift/master -b openshift-master - -$ git merge --ff-only $TAG <** used to be git rebase openshift/master $TAG **> - -$ git push openshift openshift-master:master -$ git checkout origin-patches -$ git rebase $TAG -$ git push --force-with-lease -$ git branch -D $TAG+origin - -# rebases origin-patches branch -$ git rebase openshift/origin-patches $TAG -$ git push openshift openshift/origin-patches --force-with-lease - -[At this point, the service catalog origin remote is no longer needed.] - -2. Bring over patches from cmd/service-catalog if necessary -# If git log cmd/service-catalog in the origin repo is showing commits not in -# origin-patches, they must be brought over to the service-catalog -# origin-patches branch. Otherwise, skip this section, proceed to step 75. -(origin repo) -$ git pull -$ git log cmd/service-catalog -# If there are commits since the last rebase, not the earliest commit. -# Next descend in the catalog directories to the point that matches upstream, -# which is: -$ cd go/src/github.com/kubernetes-incubator/service-catalog -# Now generate the patches for all the commits (caret is important): -$ git format-patch 0d55d15c8a1b6dc16db77d176da9651e13382a48^ --relative - -# EXAMPLE: -$ git log --oneline cmd/service-catalog/ -d09188f636 (HEAD -> catalog-ldflags) a test -0d55d15c8a catalog: add build time version info -dc92bcfd23 Merge version v0.0.17 of Service Catalog from https://github.com/openshift/service-catalog:v0.0.17+origin -4cf8f8e04e Rename pkg/deploy -> pkg/apps -2617823364 Merge pull request #15202 from adelton/drop-oadm - -# move generated patches to directory of service-catalog repo checkout -$ mv *.patch to /path/to/catalog/repo -# in origin-patches branch of service catalog -$ git checkout origin-patches -$ git am *.patch -$ git push openshift origin-patches - -[At this point, the origin-patches branch requires no further changes.] - -3. Squash origin patches -# In the Origin Repo: -# Create a new branch based off the rebased origin-patches branch -$ git checkout -b $TAG+origin -# Squash all origin patches into one commit (search for earliest origin tooling -# commit, "origin build: add origin tooling") and record the SHA from the PREVIOUS -# commit - -$ git rebase -i -# toggle all the origin commits to be squashed -$ git push openshift --force-with-lease - -[Now the openshift/service-catalog repo is fully up to date.] - - -# The following is done within Origin repo -# Pulls in code from openshift/service-catalog repo into OpenShift. -# Do not change the wording of the merge commit! The wording is used to set -# version information during the build. -$ git pull -$ git fetch sc -$ git subtree pull --prefix cmd/service-catalog/go/src/github.com/kubernetes-incubator/service-catalog https://github.com/openshift/service-catalog $TAG+origin --squash -m "Merge version $TAG of Service Catalog from https://github.com/openshift/service-catalog:$TAG+origin" - -# Check for new Admission Controllers or image argument changes -# One easy way is to check if charts/catalog/templates/apiserver-deployment.yaml has been changed -# If so, update examples/service-catalog/service-catalog.yaml (origin). Afer update, -# you must regenerate bindata by running hack/update-generated-bindata.sh and adding the updated files -# (pkg/oc/bootstrap/bindata.go) to a new commit. - -# Check for RBAC changes that would need to be reflected in service-catalog.yaml as well. - -# Push to GitHub & open a PR -$ git push - -=== -The following shows the general flow for rebasing, along with how the -image is set within "oc cluster up". The info may be useful for testing -a rebase. -=== - - Start here when there's a new upstream release. - + - | -+--------------------v--------------------+ -| GIT:kubernetes-incubator/service-catalog| -| 1 | -+--------------------+--------------------+ - | - | rebase and push new tag - | - | - +---------------v---------------+ +--------------------+ - |GIT:openshift/service-catalog <---------+GIT:openshift/origin| commits made in cmd/service-catalog - |2 | |3 | are put in origin-patches branch in (2) - +---------------+---------------+ +--------------------+ - | - | vendor into origin via subtree merge - | - | - +-----------v---------+ - | GIT:openshift/origin| - | 4 | - +---------------------+ - - -Upon code landing in the openshift/origin repo, hack/build-release.sh is -called. This script calls hack/build-images.sh, which contains a list of -images and the directories containing the required Dockerfiles. Eventually, -hack/push-release.sh is called which pushes the origin-service-catalog image -to dockerhub. - -When "oc cluster up --service-catalog" is executed, the template in -examples/service-catalog/service-catalog.yaml is used. (Technically it's not -used directly, and is updated via hack/update-generated-bindata.sh) The -template has a variable for the service catalog image to use named -SERVICE_CATALOG_IMAGE. The variable is currently set to -openshift/origin-service-catalog:latest. - -In order to best test the rebase, rebuilding everything is best to ensure that -openshift is in sync with service catalog. -# build origin -$ make - - -# create openshift/origin image with latest code & push to docker -$ hack/build-local-images.py - -# Double check for any necessary Origin SC deployment changes that should be made to -# the yaml deployment file (examples/service-catalog/service-catalog.yaml). Role changes? -# New Admission Controllers? Change in arguments? - -# update bindata -$ hack/update-generated-bindata.sh - -# build Service Catalog binary -$ cmd/service-catalog/go/src/github.com/kubernetes-incubator/service-catalog/hack/build-go.sh - -# create openshift/origin-service-catalog image and push to docker -$ hack/build-local-images.py service-catalog - -# run oc cluster up with latest images -$ oc cluster up --version=latest --service-catalog - -# set the security context to admin for running e2e -$ oc login -u system:admin - -Build the Service Catalog E2E test -# build e2e test -cd $HOME/go/src/github.com/openshift/origin/cmd/service-catalog/go/src/github.com/kubernetes-incubator/service-catalog -make bin/e2e.test - -# Setup environment and run E2E -$ export KUBECONFIG=$HOME/go/src/github.com/openshift/origin/.kubeconfig -$ export SERVICECATALOGCONFIG=$HOME/go/src/github.com/openshift/origin/.kubeconfig -$ make test-e2e - -# You can also manually exeute the e2e and add additional options: -# * enable verbose output: -v 10 -alsologtostderr -# * specify specific tests: -ginkgo.focus= where is either walkthrough, ServiceBroker or ServiceInstance -$ bin/e2e.test -v 10 -alsologtostderr -ginkgo.focus=ServiceBroker - diff --git a/build/apiserver/Dockerfile b/build/apiserver/Dockerfile deleted file mode 100644 index 64ea8adfa851..000000000000 --- a/build/apiserver/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2016 The Kubernetes Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM BASEIMAGE - -ADD apiserver /opt/services/ - -ENTRYPOINT ["/opt/services/apiserver"] -CMD ["--etcd-servers" , "localhost"] diff --git a/build/controller-manager/Dockerfile b/build/service-catalog/Dockerfile similarity index 72% rename from build/controller-manager/Dockerfile rename to build/service-catalog/Dockerfile index 3d28245a84fc..96a654d0f707 100644 --- a/build/controller-manager/Dockerfile +++ b/build/service-catalog/Dockerfile @@ -14,6 +14,11 @@ FROM BASEIMAGE -ADD controller-manager /opt/services/ +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install ca-certificates -y && \ + rm -rf /var/lib/apt/lists/* -ENTRYPOINT ["/opt/services/controller-manager" ] +ADD service-catalog opt/services/ + +ENTRYPOINT ["/opt/services/service-catalog"] diff --git a/build/version.sh b/build/version.sh new file mode 100755 index 000000000000..9d492454d104 --- /dev/null +++ b/build/version.sh @@ -0,0 +1,174 @@ +#!/bin/bash + +# Copyright 2017 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 + +# Note: this was copied from hack/lib/version.sh and then adapted +# Changes from original: +# variables renamed: +# KUBE_* -> CATALOG_* +# kube -> catalog +# KUBE_ROOT -> ROOT +# KUBE_GO_PACKAGE -> SC_GO_PACKAGE +# added get_ldflags for use in Makefile + +# ----------------------------------------------------------------------------- +# Version management helpers. These functions help to set, save and load the +# following variables: +# +# CATALOG_GIT_COMMIT - The git commit id corresponding to this +# source code. +# CATALOG_GIT_TREE_STATE - "clean" indicates no changes since the git commit id +# "dirty" indicates source code changes after the git commit id +# CATALOG_GIT_VERSION - "vX.Y" used to indicate the last release version. +# CATALOG_GIT_MAJOR - The major part of the version +# CATALOG_GIT_MINOR - The minor component of the version + +# Grovels through git to set a set of env variables. +# +# If CATALOG_GIT_VERSION_FILE, this function will load from that file instead of +# querying git. +catalog::version::get_version_vars() { + if [[ -n ${CATALOG_GIT_VERSION_FILE-} ]]; then + catalog::version::load_version_vars "${CATALOG_GIT_VERSION_FILE}" + return + fi + + local git=(git --work-tree "${ROOT}") + + if [[ -n ${CATALOG_GIT_COMMIT-} ]] || CATALOG_GIT_COMMIT=$("${git[@]}" rev-parse "HEAD^{commit}" 2>/dev/null); then + if [[ -z ${CATALOG_GIT_TREE_STATE-} ]]; then + # Check if the tree is dirty. default to dirty + if git_status=$("${git[@]}" status --porcelain 2>/dev/null) && [[ -z ${git_status} ]]; then + CATALOG_GIT_TREE_STATE="clean" + else + CATALOG_GIT_TREE_STATE="dirty" + fi + fi + + # Use git describe to find the version based on annotated tags. + if [[ -n ${CATALOG_GIT_VERSION-} ]] || CATALOG_GIT_VERSION=$("${git[@]}" describe --tags --abbrev=14 "${CATALOG_GIT_COMMIT}^{commit}" 2>/dev/null); then + # This translates the "git describe" to an actual semver.org + # compatible semantic version that looks something like this: + # v1.1.0-alpha.0.6+84c76d1142ea4d + # + # TODO: We continue calling this "git version" because so many + # downstream consumers are expecting it there. + DASHES_IN_VERSION=$(echo "${CATALOG_GIT_VERSION}" | sed "s/[^-]//g") + if [[ "${DASHES_IN_VERSION}" == "---" ]] ; then + # We have distance to subversion (v1.1.0-subversion-1-gCommitHash) + CATALOG_GIT_VERSION=$(echo "${CATALOG_GIT_VERSION}" | sed "s/-\([0-9]\{1,\}\)-g\([0-9a-f]\{14\}\)$/.\1\+\2/") + elif [[ "${DASHES_IN_VERSION}" == "--" ]] ; then + # We have distance to base tag (v1.1.0-1-gCommitHash) + CATALOG_GIT_VERSION=$(echo "${CATALOG_GIT_VERSION}" | sed "s/-g\([0-9a-f]\{14\}\)$/+\1/") + fi + if [[ "${CATALOG_GIT_TREE_STATE}" == "dirty" ]]; then + # git describe --dirty only considers changes to existing files, but + # that is problematic since new untracked .go files affect the build, + # so use our idea of "dirty" from git status instead. + CATALOG_GIT_VERSION+="-dirty" + fi + + + # Try to match the "git describe" output to a regex to try to extract + # the "major" and "minor" versions and whether this is the exact tagged + # version or whether the tree is between two tagged versions. + if [[ "${CATALOG_GIT_VERSION}" =~ ^v([0-9]+)\.([0-9]+)(\.[0-9]+)?([-].*)?$ ]]; then + CATALOG_GIT_MAJOR=${BASH_REMATCH[1]} + CATALOG_GIT_MINOR=${BASH_REMATCH[2]} + if [[ -n "${BASH_REMATCH[4]}" ]]; then + CATALOG_GIT_MINOR+="+" + fi + fi + fi + fi +} + +# Saves the environment flags to $1 +catalog::version::save_version_vars() { + local version_file=${1-} + [[ -n ${version_file} ]] || { + echo "!!! Internal error. No file specified in catalog::version::save_version_vars" + return 1 + } + + cat <"${version_file}" +CATALOG_GIT_COMMIT='${CATALOG_GIT_COMMIT-}' +CATALOG_GIT_TREE_STATE='${CATALOG_GIT_TREE_STATE-}' +CATALOG_GIT_VERSION='${CATALOG_GIT_VERSION-}' +CATALOG_GIT_MAJOR='${CATALOG_GIT_MAJOR-}' +CATALOG_GIT_MINOR='${CATALOG_GIT_MINOR-}' +EOF +} + +# Loads up the version variables from file $1 +catalog::version::load_version_vars() { + local version_file=${1-} + [[ -n ${version_file} ]] || { + echo "!!! Internal error. No file specified in catalog::version::load_version_vars" + return 1 + } + + source "${version_file}" +} + +catalog::version::ldflag() { + local key=${1} + local val=${2} + + # If you update these, also update the list pkg/version/def.bzl. + echo "-X ${SC_GO_PACKAGE}/pkg/version.${key}=${val}" + echo "-X ${SC_GO_PACKAGE}/vendor/k8s.io/client-go/pkg/version.${key}=${val}" +} + +# Prints the value that needs to be passed to the -ldflags parameter of go build +# in order to set the Kubernetes based on the git tree status. +# IMPORTANT: if you update any of these, also update the lists in +# pkg/version/def.bzl and hack/print-workspace-status.sh. +catalog::version::ldflags() { + catalog::version::get_version_vars + + local buildDate= + [[ -z ${SOURCE_DATE_EPOCH-} ]] || buildDate="--date=@${SOURCE_DATE_EPOCH}" + local -a ldflags=($(catalog::version::ldflag "buildDate" "$(date ${buildDate} -u +'%Y-%m-%dT%H:%M:%SZ')")) + if [[ -n ${CATALOG_GIT_COMMIT-} ]]; then + ldflags+=($(catalog::version::ldflag "gitCommit" "${CATALOG_GIT_COMMIT}")) + ldflags+=($(catalog::version::ldflag "gitTreeState" "${CATALOG_GIT_TREE_STATE}")) + fi + + if [[ -n ${CATALOG_GIT_VERSION-} ]]; then + ldflags+=($(catalog::version::ldflag "gitVersion" "${CATALOG_GIT_VERSION}")) + fi + + if [[ -n ${CATALOG_GIT_MAJOR-} && -n ${CATALOG_GIT_MINOR-} ]]; then + ldflags+=( + $(catalog::version::ldflag "gitMajor" "${CATALOG_GIT_MAJOR}") + $(catalog::version::ldflag "gitMinor" "${CATALOG_GIT_MINOR}") + ) + fi + + # The -ldflags parameter takes a single string, so join the output. + echo "${ldflags[*]-}" +} + +# called from Makefile +catalog::version::get_ldflags() { + export ROOT=$1 + export SC_GO_PACKAGE=$2 + catalog::version::ldflags +} + +catalog::version::get_ldflags $1 $2 diff --git a/charts/catalog/README.md b/charts/catalog/README.md index 15fcb6dcdb50..b6749680e474 100644 --- a/charts/catalog/README.md +++ b/charts/catalog/README.md @@ -40,8 +40,8 @@ chart and their default values. | Parameter | Description | Default | |-----------|-------------|---------| -| `apiserver.image` | apiserver image to use | `quay.io/kubernetes-service-catalog/apiserver:v0.1.0-rc2` | -| `apiserver.imagePullPolicy` | `imagePullPolicy` for the apiserver | `Always` | +| `image` | apiserver image to use | `quay.io/kubernetes-service-catalog/service-catalog:v0.1.0` | +| `imagePullPolicy` | `imagePullPolicy` for the service catalog | `Always` | | `apiserver.tls.cert` | Base64-encoded x509 certificate | A self-signed certificate | | `apiserver.tls.key` | Base64-encoded private key | The private key for the certificate above | | `apiserver.tls.ca` | Base64-encoded CA certificate used to sign the above certificate | | @@ -53,8 +53,6 @@ chart and their default values. | `apiserver.storage.etcd.servers` | If storage type is `etcd`: etcd URL(s); override this if NOT using embedded etcd | `http://localhost:2379` | | `apiserver.verbosity` | Log level; valid values are in the range 0 - 10 | `10` | | `apiserver.auth.enabled` | Enable authentication and authorization | `false` | -| `controllerManager.image` | controller-manager image to use | `quay.io/kubernetes-service-catalog/controller-manager:v0.1.0-rc2` | -| `controllerManager.imagePullPolicy` | `imagePullPolicy` for the controller-manager | `Always` | | `controllerManager.verbosity` | Log level; valid values are in the range 0 - 10 | `10` | | `controllerManager.resyncInterval` | How often the controller should resync informers; duration format (`20m`, `1h`, etc) | `5m` | | `controllerManager.brokerRelistInterval` | How often the controller should relist the catalogs of ready brokers; duration format (`20m`, `1h`, etc) | `24h` | diff --git a/charts/catalog/templates/apiserver-deployment.yaml b/charts/catalog/templates/apiserver-deployment.yaml index a38fd09e33c0..b626ae2e7ccc 100644 --- a/charts/catalog/templates/apiserver-deployment.yaml +++ b/charts/catalog/templates/apiserver-deployment.yaml @@ -23,8 +23,8 @@ spec: serviceAccountName: "{{ .Values.apiserver.serviceAccount }}" containers: - name: apiserver - image: {{ .Values.apiserver.image }} - imagePullPolicy: {{ .Values.apiserver.imagePullPolicy }} + image: {{ .Values.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} resources: requests: cpu: 100m @@ -33,6 +33,7 @@ spec: cpu: 100m memory: 30Mi args: + - apiserver {{ if .Values.apiserver.audit.activated -}} - --audit-log-path - {{ .Values.apiserver.audit.logPath }} diff --git a/charts/catalog/templates/controller-manager-deployment.yaml b/charts/catalog/templates/controller-manager-deployment.yaml index ff86258c977a..9d75bdd4a4ec 100644 --- a/charts/catalog/templates/controller-manager-deployment.yaml +++ b/charts/catalog/templates/controller-manager-deployment.yaml @@ -23,8 +23,8 @@ spec: serviceAccountName: "{{ .Values.controllerManager.serviceAccount }}" containers: - name: controller-manager - image: {{ .Values.controllerManager.image }} - imagePullPolicy: {{ .Values.controllerManager.imagePullPolicy }} + image: {{ .Values.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} resources: requests: cpu: 100m @@ -38,6 +38,7 @@ spec: fieldRef: fieldPath: metadata.namespace args: + - controller-manager - --port - "8080" {{ if .Values.controllerManager.leaderElection.activated -}} diff --git a/charts/catalog/values.yaml b/charts/catalog/values.yaml index 55ce73904bba..d1de7efb8b7a 100644 --- a/charts/catalog/values.yaml +++ b/charts/catalog/values.yaml @@ -1,15 +1,15 @@ # Default values for Service Catalog +# service-catalog image to use +image: quay.io/kubernetes-service-catalog/service-catalog:v0.1.0 +# imagePullPolicy for the service-catalog; valid values are "IfNotPresent", +# "Never", and "Always" +imagePullPolicy: Always # determines whether the API server should be registered with the kube-aggregator useAggregator: false ## If true, create & use RBAC resources ## rbacEnable: true apiserver: - # apiserver image to use - image: quay.io/kubernetes-service-catalog/apiserver:v0.1.0-rc2 - # imagePullPolicy for the apiserver; valid values are "IfNotPresent", - # "Never", and "Always" - imagePullPolicy: Always aggregator: # priority is the priority of the APIService. Please see # https://github.com/kubernetes/kubernetes/blob/v1.7.0/staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1/types.go#L56-L61 @@ -70,11 +70,6 @@ apiserver: logPath: "/tmp/service-catalog-apiserver-audit.log" serviceAccount: service-catalog-apiserver controllerManager: - # controller-manager image to use - image: quay.io/kubernetes-service-catalog/controller-manager:v0.1.0-rc2 - # imagePullPolicy for the controller-manager; valid values are - # "IfNotPresent", "Never", and "Always" - imagePullPolicy: Always # Log level; valid values are in the range 0 - 10 verbosity: 10 # Resync interval; format is a duration (`20m`, `1h`, etc) diff --git a/charts/ups-broker/README.md b/charts/ups-broker/README.md index 849b150bb708..32bb66c04d9b 100644 --- a/charts/ups-broker/README.md +++ b/charts/ups-broker/README.md @@ -34,7 +34,7 @@ Service Broker | Parameter | Description | Default | |-----------|-------------|---------| -| `image` | Image to use | `quay.io/kubernetes-service-catalog/user-broker:v0.1.0-rc2` | +| `image` | Image to use | `quay.io/kubernetes-service-catalog/user-broker:v0.1.0` | | `imagePullPolicy` | `imagePullPolicy` for the ups-broker | `Always` | Specify each parameter using the `--set key=value[,key=value]` argument to diff --git a/charts/ups-broker/values.yaml b/charts/ups-broker/values.yaml index b4720209b59f..22b7a08bc9ff 100644 --- a/charts/ups-broker/values.yaml +++ b/charts/ups-broker/values.yaml @@ -1,6 +1,6 @@ # Default values for User-Provided Service Broker # Image to use -image: quay.io/kubernetes-service-catalog/user-broker:v0.1.0-rc2 +image: quay.io/kubernetes-service-catalog/user-broker:v0.1.0 # ImagePullPolicy; valid values are "IfNotPresent", "Never", and "Always" imagePullPolicy: Always # Certificate details to use for TLS. Leave blank to not use TLS diff --git a/cmd/apiserver/apiserver.go b/cmd/apiserver/apiserver.go deleted file mode 100644 index 40c74f1a5ef2..000000000000 --- a/cmd/apiserver/apiserver.go +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// The apiserver serves the REST APIs for the service catalog API. -package main - -import ( - "os" - - "github.com/golang/glog" - // set up logging the k8s way - "k8s.io/apiserver/pkg/util/logs" - - // The API groups for our API must be installed before we can use the - // client to work with them. This needs to be done once per process; this - // is the point at which we handle this for the API server process. - // Please do not remove. - _ "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/install" - - _ "github.com/kubernetes-incubator/service-catalog/cmd/apiserver/app" - "github.com/kubernetes-incubator/service-catalog/cmd/apiserver/app/server" -) - -func main() { - logs.InitLogs() - // make sure we print all the logs while shutting down. - defer logs.FlushLogs() - - cmd, err := server.NewCommandServer(os.Stdout) - if err != nil { - glog.Errorf("Error creating server: %v", err) - logs.FlushLogs() - os.Exit(1) - } - - if err := cmd.Execute(); err != nil { - glog.Errorf("server exited unexpectedly (%s)", err) - logs.FlushLogs() - os.Exit(1) - } -} diff --git a/cmd/apiserver/app/plugins.go b/cmd/apiserver/app/plugins.go deleted file mode 100644 index 2f59d5051f7d..000000000000 --- a/cmd/apiserver/app/plugins.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2017 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 app - -// This file exists to force the desired plugin implementations to be linked. -// This should probably be part of some configuration fed into the build for a -// given binary target. -import ( - // Admission controllers - _ "github.com/kubernetes-incubator/service-catalog/plugin/pkg/admission/broker/authsarcheck" - _ "github.com/kubernetes-incubator/service-catalog/plugin/pkg/admission/namespace/lifecycle" - _ "github.com/kubernetes-incubator/service-catalog/plugin/pkg/admission/servicebindings/lifecycle" - _ "github.com/kubernetes-incubator/service-catalog/plugin/pkg/admission/serviceplan/changevalidator" - _ "github.com/kubernetes-incubator/service-catalog/plugin/pkg/admission/serviceplan/defaultserviceplan" -) diff --git a/cmd/apiserver/app/server/etcd_options.go b/cmd/apiserver/app/server/etcd_options.go index 6d2e517000a5..02168a396a9a 100644 --- a/cmd/apiserver/app/server/etcd_options.go +++ b/cmd/apiserver/app/server/etcd_options.go @@ -18,7 +18,6 @@ package server import ( "github.com/kubernetes-incubator/service-catalog/pkg/api" - "github.com/pborman/uuid" "github.com/spf13/pflag" genericserveroptions "k8s.io/apiserver/pkg/server/options" "k8s.io/apiserver/pkg/storage/storagebackend" @@ -32,10 +31,18 @@ type EtcdOptions struct { *genericserveroptions.EtcdOptions } +const ( + // DefaultEtcdPathPrefix is the default prefix that is prepended to all + // resource paths in etcd. It is intended to allow an operator to + // differentiate the storage of different API servers from one another in + // a single etcd. + DefaultEtcdPathPrefix = "/registry" +) + // NewEtcdOptions creates a new, empty, EtcdOptions instance func NewEtcdOptions() *EtcdOptions { return &EtcdOptions{ - EtcdOptions: genericserveroptions.NewEtcdOptions(storagebackend.NewDefaultConfig(uuid.New(), api.Scheme, nil)), + EtcdOptions: genericserveroptions.NewEtcdOptions(storagebackend.NewDefaultConfig(DefaultEtcdPathPrefix, api.Scheme, nil)), } } diff --git a/cmd/apiserver/app/server/options.go b/cmd/apiserver/app/server/options.go index 322812be25e1..5222aae7a11b 100644 --- a/cmd/apiserver/app/server/options.go +++ b/cmd/apiserver/app/server/options.go @@ -26,6 +26,14 @@ import ( genericserveroptions "k8s.io/apiserver/pkg/server/options" ) +const ( + // Store generated SSL certificates in a place that won't collide with the + // k8s core API server. + certDirectory = "/var/run/kubernetes-service-catalog" + + storageTypeFlagName = "storageType" +) + // ServiceCatalogServerOptions contains the aggregation of configuration structs for // the service-catalog server. It contains everything needed to configure a basic API server. // It is public so that integration tests can access it. @@ -47,7 +55,6 @@ type ServiceCatalogServerOptions struct { EtcdOptions *EtcdOptions // DisableAuth disables delegating authentication and authorization for testing scenarios DisableAuth bool - StopCh <-chan struct{} // StandaloneMode if true asserts that we will not depend on a kube-apiserver StandaloneMode bool } @@ -55,7 +62,7 @@ type ServiceCatalogServerOptions struct { // NewServiceCatalogServerOptions creates a new instances of // ServiceCatalogServerOptions with all sub-options filled in. func NewServiceCatalogServerOptions() *ServiceCatalogServerOptions { - return &ServiceCatalogServerOptions{ + opts := &ServiceCatalogServerOptions{ GenericServerRunOptions: genericserveroptions.NewServerRunOptions(), AdmissionOptions: genericserveroptions.NewAdmissionOptions(), SecureServingOptions: genericserveroptions.NewSecureServingOptions(), @@ -63,10 +70,17 @@ func NewServiceCatalogServerOptions() *ServiceCatalogServerOptions { AuthorizationOptions: genericserveroptions.NewDelegatingAuthorizationOptions(), AuditOptions: genericserveroptions.NewAuditOptions(), EtcdOptions: NewEtcdOptions(), + StandaloneMode: standaloneMode(), } + // register all admission plugins + registerAllAdmissionPlugins(opts.AdmissionOptions.Plugins) + // Set generated SSL cert path correctly + opts.SecureServingOptions.ServerCert.CertDirectory = certDirectory + return opts } -func (s *ServiceCatalogServerOptions) addFlags(flags *pflag.FlagSet) { +// AddFlags adds to the flag set the flags to configure the API Server. +func (s *ServiceCatalogServerOptions) AddFlags(flags *pflag.FlagSet) { flags.StringVar( &s.StorageTypeString, "storage-type", diff --git a/cmd/apiserver/app/server/plugins.go b/cmd/apiserver/app/server/plugins.go new file mode 100644 index 000000000000..a772d7830581 --- /dev/null +++ b/cmd/apiserver/app/server/plugins.go @@ -0,0 +1,40 @@ +/* +Copyright 2017 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 server + +// This file exists to force the desired plugin implementations to be linked. +// This should probably be part of some configuration fed into the build for a +// given binary target. +import ( + "k8s.io/apiserver/pkg/admission" + + // Admission controllers + "github.com/kubernetes-incubator/service-catalog/plugin/pkg/admission/broker/authsarcheck" + "github.com/kubernetes-incubator/service-catalog/plugin/pkg/admission/namespace/lifecycle" + siclifecycle "github.com/kubernetes-incubator/service-catalog/plugin/pkg/admission/servicebindings/lifecycle" + "github.com/kubernetes-incubator/service-catalog/plugin/pkg/admission/serviceplan/changevalidator" + "github.com/kubernetes-incubator/service-catalog/plugin/pkg/admission/serviceplan/defaultserviceplan" +) + +// registerAllAdmissionPlugins registers all admission plugins +func registerAllAdmissionPlugins(plugins *admission.Plugins) { + lifecycle.Register(plugins) + defaultserviceplan.Register(plugins) + siclifecycle.Register(plugins) + changevalidator.Register(plugins) + authsarcheck.Register(plugins) +} diff --git a/cmd/apiserver/app/server/run_server.go b/cmd/apiserver/app/server/run_server.go index 3fb9490ef85e..6665e08af4b8 100644 --- a/cmd/apiserver/app/server/run_server.go +++ b/cmd/apiserver/app/server/run_server.go @@ -31,15 +31,15 @@ import ( ) // RunServer runs an API server with configuration according to opts -func RunServer(opts *ServiceCatalogServerOptions) error { +func RunServer(opts *ServiceCatalogServerOptions, stopCh <-chan struct{}) error { storageType, err := opts.StorageType() if err != nil { return err } - if opts.StopCh == nil { + if stopCh == nil { /* the caller of RunServer should generate the stop channel if there is a need to stop the API server */ - opts.StopCh = make(chan struct{}) + stopCh = make(chan struct{}) } err = opts.Validate() @@ -48,13 +48,13 @@ func RunServer(opts *ServiceCatalogServerOptions) error { } if storageType == server.StorageTypeEtcd { - return runEtcdServer(opts) + return runEtcdServer(opts, stopCh) } // This should never happen, catch for potential bugs panic("Unexpected storage type: " + storageType) } -func runEtcdServer(opts *ServiceCatalogServerOptions) error { +func runEtcdServer(opts *ServiceCatalogServerOptions, stopCh <-chan struct{}) error { etcdOpts := opts.EtcdOptions glog.V(4).Infoln("Preparing to run API server") genericConfig, scConfig, err := buildGenericConfig(opts) @@ -105,7 +105,7 @@ func runEtcdServer(opts *ServiceCatalogServerOptions) error { if err != nil { return fmt.Errorf("error completing API server configuration: %v", err) } - addPostStartHooks(server.GenericAPIServer, scConfig, opts.StopCh) + addPostStartHooks(server.GenericAPIServer, scConfig, stopCh) // Install healthz checks before calling PrepareRun. etcdChecker := checkEtcdConnectable{ @@ -117,7 +117,7 @@ func runEtcdServer(opts *ServiceCatalogServerOptions) error { // do we need to do any post api installation setup? We should have set up the api already? glog.Infoln("Running the API server") - server.PrepareRun().Run(opts.StopCh) + server.PrepareRun().Run(stopCh) return nil } diff --git a/cmd/apiserver/app/server/server.go b/cmd/apiserver/app/server/server.go index 6593593cb879..d00ec69d4b05 100644 --- a/cmd/apiserver/app/server/server.go +++ b/cmd/apiserver/app/server/server.go @@ -17,110 +17,16 @@ limitations under the License. package server import ( - "flag" - "io" - "os" - "github.com/golang/glog" - "github.com/kubernetes-incubator/service-catalog/pkg" - "github.com/kubernetes-incubator/service-catalog/pkg/kubernetes/pkg/util/interrupt" - "github.com/kubernetes-incubator/service-catalog/pkg/registry/servicecatalog/server" - "github.com/kubernetes-incubator/service-catalog/plugin/pkg/admission/broker/authsarcheck" - "github.com/kubernetes-incubator/service-catalog/plugin/pkg/admission/namespace/lifecycle" - siclifecycle "github.com/kubernetes-incubator/service-catalog/plugin/pkg/admission/servicebindings/lifecycle" - "github.com/kubernetes-incubator/service-catalog/plugin/pkg/admission/serviceplan/changevalidator" - "github.com/kubernetes-incubator/service-catalog/plugin/pkg/admission/serviceplan/defaultserviceplan" - "github.com/spf13/cobra" - "k8s.io/apiserver/pkg/admission" - genericserveroptions "k8s.io/apiserver/pkg/server/options" ) -const ( - // Store generated SSL certificates in a place that won't collide with the - // k8s core API server. - certDirectory = "/var/run/kubernetes-service-catalog" - - // I made this up to match some existing paths. I am not sure if there - // are any restrictions on the format or structure beyond text - // separated by slashes. - etcdPathPrefix = "/k8s.io/service-catalog" - - // GroupName I made this up. Maybe we'll need it. - GroupName = "service-catalog.k8s.io" - - storageTypeFlagName = "storageType" -) - -// NewCommandServer creates a new cobra command to run our server. -func NewCommandServer( - out io.Writer, -) (*cobra.Command, error) { - // Create the command that runs the API server - cmd := &cobra.Command{ - Short: "run a service-catalog server", - } - // We pass flags object to sub option structs to have them configure - // themselves. Each options adds its own command line flags - // in addition to the flags that are defined above. - flags := cmd.Flags() - flags.AddGoFlagSet(flag.CommandLine) - - stopCh := make(chan struct{}) - opts := &ServiceCatalogServerOptions{ - GenericServerRunOptions: genericserveroptions.NewServerRunOptions(), - AdmissionOptions: genericserveroptions.NewAdmissionOptions(), - SecureServingOptions: genericserveroptions.NewSecureServingOptions(), - AuthenticationOptions: genericserveroptions.NewDelegatingAuthenticationOptions(), - AuthorizationOptions: genericserveroptions.NewDelegatingAuthorizationOptions(), - AuditOptions: genericserveroptions.NewAuditOptions(), - EtcdOptions: NewEtcdOptions(), - StopCh: stopCh, - StandaloneMode: standaloneMode(), - } - opts.addFlags(flags) - // register all admission plugins - registerAllAdmissionPlugins(opts.AdmissionOptions.Plugins) - // Set generated SSL cert path correctly - opts.SecureServingOptions.ServerCert.CertDirectory = certDirectory - - version := pkg.VersionFlag(cmd.Flags()) - - flags.Parse(os.Args[1:]) - - version.PrintAndExitIfRequested() - +// Run runs the specified APIServer. This should never exit. +func Run(opts *ServiceCatalogServerOptions, stopCh <-chan struct{}) error { storageType, err := opts.StorageType() if err != nil { glog.Fatalf("invalid storage type '%s' (%s)", storageType, err) - return nil, err - } - if storageType == server.StorageTypeEtcd { - glog.Infof("using etcd for storage") - // Store resources in etcd under our special prefix - opts.EtcdOptions.StorageConfig.Prefix = etcdPathPrefix - } else { - // This should never happen, catch for potential bugs - panic("Unexpected storage type: " + storageType) + return err } - cmd.Run = func(c *cobra.Command, args []string) { - h := interrupt.New(nil, func() { - close(stopCh) - }) - if err := h.Run(func() error { return RunServer(opts) }); err != nil { - glog.Fatalf("error running server (%s)", err) - return - } - } - - return cmd, nil -} - -// registerAllAdmissionPlugins registers all admission plugins -func registerAllAdmissionPlugins(plugins *admission.Plugins) { - lifecycle.Register(plugins) - defaultserviceplan.Register(plugins) - siclifecycle.Register(plugins) - changevalidator.Register(plugins) - authsarcheck.Register(plugins) + return RunServer(opts, stopCh) } diff --git a/cmd/service-catalog/apiserver.go b/cmd/service-catalog/apiserver.go new file mode 100644 index 000000000000..69ea03d1de87 --- /dev/null +++ b/cmd/service-catalog/apiserver.go @@ -0,0 +1,41 @@ +/* +Copyright 2017 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 main + +import ( + "github.com/kubernetes-incubator/service-catalog/cmd/apiserver/app/server" + "github.com/kubernetes-incubator/service-catalog/pkg/hyperkube" +) + +// NewAPIServer creates a new hyperkube Server object that includes the +// description and flags. +func NewAPIServer() *hyperkube.Server { + s := server.NewServiceCatalogServerOptions() + + hks := hyperkube.Server{ + PrimaryName: "apiserver", + AlternativeName: "service-catalog-apiserver", + SimpleUsage: "apiserver", + Long: "The main API entrypoint and interface to the storage system. The API server is also the focal point for all authorization decisions.", + Run: func(_ *hyperkube.Server, args []string, stopCh <-chan struct{}) error { + return server.Run(s, stopCh) + }, + RespectsStopCh: true, + } + s.AddFlags(hks.Flags()) + return &hks +} diff --git a/cmd/controller-manager/controller-manager.go b/cmd/service-catalog/controller-manager.go similarity index 52% rename from cmd/controller-manager/controller-manager.go rename to cmd/service-catalog/controller-manager.go index 8e60e181b25e..5a6149713bdb 100644 --- a/cmd/controller-manager/controller-manager.go +++ b/cmd/service-catalog/controller-manager.go @@ -14,44 +14,29 @@ See the License for the specific language governing permissions and limitations under the License. */ -// The controller manager is responsible for monitoring replication -// controllers, and creating corresponding pods to achieve the desired -// state. It uses the API to listen for new controllers and to create/delete -// pods. package main import ( - "fmt" - "os" - "github.com/kubernetes-incubator/service-catalog/cmd/controller-manager/app" "github.com/kubernetes-incubator/service-catalog/cmd/controller-manager/app/options" - "github.com/kubernetes-incubator/service-catalog/pkg" - - "k8s.io/apiserver/pkg/server/healthz" - "k8s.io/apiserver/pkg/util/flag" - "k8s.io/apiserver/pkg/util/logs" - - "github.com/spf13/pflag" + "github.com/kubernetes-incubator/service-catalog/pkg/hyperkube" ) -func init() { - healthz.DefaultHealthz() -} - -func main() { +// NewControllerManager creates a new hyperkube Server object that includes the +// description and flags. +func NewControllerManager() *hyperkube.Server { s := options.NewControllerManagerServer() - s.AddFlags(pflag.CommandLine) - version := pkg.VersionFlag(pflag.CommandLine) - - flag.InitFlags() - logs.InitLogs() - defer logs.FlushLogs() - - version.PrintAndExitIfRequested() - if err := app.Run(s); err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) - os.Exit(1) + hks := hyperkube.Server{ + PrimaryName: "controller-manager", + AlternativeName: "service-catalog-controller-manager", + SimpleUsage: "controller-manager", + Long: `The service-catalog controller manager is a daemon that embeds the core control loops shipped with the service catalog.`, + Run: func(_ *hyperkube.Server, args []string, stopCh <-chan struct{}) error { + return app.Run(s) + }, + RespectsStopCh: false, } + s.AddFlags(hks.Flags()) + return &hks } diff --git a/cmd/service-catalog/main.go b/cmd/service-catalog/main.go new file mode 100644 index 000000000000..de5b8050b887 --- /dev/null +++ b/cmd/service-catalog/main.go @@ -0,0 +1,38 @@ +/* +Copyright 2017 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. +*/ + +// A binary that can morph into all of the other kubernetes service-catalog +// binaries. You can also soft-link to it busybox style. +// +package main + +import ( + "os" + + "github.com/kubernetes-incubator/service-catalog/pkg/hyperkube" +) + +func main() { + hk := hyperkube.HyperKube{ + Name: "service-catalog", + Long: "This is an all-in-one binary that can run any of the various Kubernetes service-catalog servers.", + } + + hk.AddServer(NewAPIServer()) + hk.AddServer(NewControllerManager()) + + hk.RunToExit(os.Args) +} diff --git a/contrib/examples/apiserver/apiserver.yaml b/contrib/examples/apiserver/apiserver.yaml index 3e1714306cf6..aad1853d6eb4 100644 --- a/contrib/examples/apiserver/apiserver.yaml +++ b/contrib/examples/apiserver/apiserver.yaml @@ -7,9 +7,10 @@ metadata: spec: containers: - name: apiserver - image: apiserver + image: service-catalog imagePullPolicy: IfNotPresent args: + - apiserver - --etcd-servers - http://localhost:2379 - -v diff --git a/contrib/examples/apiserver/instance.yaml b/contrib/examples/apiserver/instance.yaml index 47b29f0b58c1..4eaf83f12b4e 100644 --- a/contrib/examples/apiserver/instance.yaml +++ b/contrib/examples/apiserver/instance.yaml @@ -4,5 +4,5 @@ metadata: name: test-instance namespace: test-ns spec: - externalClusterServiceClassName: test-serviceclass - externalClusterServicePlanName: example-plan-1 + clusterServiceClassExternalName: test-serviceclass + clusterServicePlanExternalName: example-plan-1 diff --git a/contrib/examples/walkthrough/ups-instance-default-sp.yaml b/contrib/examples/walkthrough/ups-instance-default-sp.yaml index f292d8c6ea98..26ca639967a5 100644 --- a/contrib/examples/walkthrough/ups-instance-default-sp.yaml +++ b/contrib/examples/walkthrough/ups-instance-default-sp.yaml @@ -4,7 +4,7 @@ metadata: name: ups-instance-default namespace: test-ns spec: - externalClusterServiceClassName: user-provided-service + clusterServiceClassExternalName: user-provided-service parameters: credentials: name: root diff --git a/contrib/examples/walkthrough/ups-instance.yaml b/contrib/examples/walkthrough/ups-instance.yaml index 12976222504c..3f766a806342 100644 --- a/contrib/examples/walkthrough/ups-instance.yaml +++ b/contrib/examples/walkthrough/ups-instance.yaml @@ -4,8 +4,8 @@ metadata: name: ups-instance namespace: test-ns spec: - externalClusterServiceClassName: user-provided-service - externalClusterServicePlanName: default + clusterServiceClassExternalName: user-provided-service + clusterServicePlanExternalName: default parameters: credentials: param-1: value-1 diff --git a/contrib/hack/start-server.sh b/contrib/hack/start-server.sh index 225a7521e855..6783b9dd8f36 100755 --- a/contrib/hack/start-server.sh +++ b/contrib/hack/start-server.sh @@ -43,7 +43,7 @@ docker run -d --name apiserver \ --privileged \ --net container:etcd-svc-cat \ scbuildimage \ - bin/apiserver -v 10 --etcd-servers http://localhost:2379 \ + bin/service-catalog apiserver -v 10 --etcd-servers http://localhost:2379 \ --storage-type=etcd --disable-auth # Wait for apiserver to be up and running diff --git a/contrib/jenkins/install_catalog.sh b/contrib/jenkins/install_catalog.sh index 223518756243..9b80042550aa 100755 --- a/contrib/jenkins/install_catalog.sh +++ b/contrib/jenkins/install_catalog.sh @@ -35,13 +35,11 @@ VERSION="${VERSION:-"canary"}" CERT_FOLDER="${CERT_FOLDER:-"/tmp/sc-certs/"}" FIX_CONFIGMAP="${FIX_CONFIGMAP:-false}" -CONTROLLER_MANAGER_IMAGE="${REGISTRY}controller-manager:${VERSION}" -APISERVER_IMAGE="${REGISTRY}apiserver:${VERSION}" +SERVICE_CATALOG_IMAGE="${REGISTRY}service-catalog:${VERSION}" echo 'INSTALLING SERVICE CATALOG' echo '-------------------' -echo "Using controller-manager image: ${CONTROLLER_MANAGER_IMAGE}" -echo "Using apiserver image: ${APISERVER_IMAGE}" +echo "Using service-catalot image: ${SERVICE_CATALOG_IMAGE}" echo '-------------------' # Create certificates for API server @@ -75,8 +73,7 @@ PARAMETERS="$(cat <<-EOF --set apiserver.tls.ca=$(base64 --wrap 0 ${SC_SERVING_CA}) \ --set apiserver.tls.cert=$(base64 --wrap 0 ${SC_SERVING_CERT}) \ --set apiserver.tls.key=$(base64 --wrap 0 ${SC_SERVING_KEY}) \ - --set controllerManager.image=${CONTROLLER_MANAGER_IMAGE} \ - --set apiserver.image=${APISERVER_IMAGE} + --set image=${SERVICE_CATALOG_IMAGE} EOF )" diff --git a/docs/devguide.md b/docs/devguide.md index 946161aac5c5..1bc153b6ece0 100644 --- a/docs/devguide.md +++ b/docs/devguide.md @@ -25,8 +25,9 @@ layout: │   └── catalog # Helm chart for deploying the service catalog │   └── ups-broker # Helm chart for deploying the user-provided service broker ├── cmd # Contains "main" Go packages for each service catalog component binary - │   └── apiserver # The service catalog API server binary - │   └── controller-manager # The service catalog controller manager binary + │   └── apiserver # The service catalog API server service-catalog command + │   └── controller-manager # The service catalog controller manager service-catalog command + │   └── service-catalog # The service catalog binary, which is used to run commands ├── contrib # Contains examples, non-essential golang source, CI configurations, etc │   └── build # Dockerfiles for contrib images (example: ups-broker) │   └── cmd # Entrypoints for contrib binaries @@ -229,14 +230,14 @@ most contributors who hack on service catalog components will wish to produce custom-built images, but will be unable to push to this location, it can be overridden through use of the `REGISTRY` environment variable. -Examples of apiserver image names: +Examples of service-catalog image names: | `REGISTRY` | Fully Qualified Image Name | Notes | |----------|----------------------------|-------| -| Unset; default | `quay.io/kubernetes-service-catalog/apiserver` | You probably don't have permissions to push to here | -| Dockerhub username + trailing slash, e.g. `krancour/` | `krancour/apiserver` | Missing hostname == Dockerhub | -| Dockerhub username + slash + some prefix, e.g. `krancour/sc-` | `krancour/sc-apiserver` | The prefix is useful for disambiguating similarly names images within a single namespace. | -| 192.168.99.102:5000/ | `192.168.99.102:5000/apiserver` | A local registry | +| Unset; default | `quay.io/kubernetes-service-catalog/service-catalog` | You probably don't have permissions to push to here | +| Dockerhub username + trailing slash, e.g. `krancour/` | `krancour/service-catalog` | Missing hostname == Dockerhub | +| Dockerhub username + slash + some prefix, e.g. `krancour/sc-` | `krancour/sc-service-catalog` | The prefix is useful for disambiguating similarly names images within a single namespace. | +| 192.168.99.102:5000/ | `192.168.99.102:5000/service-catalog` | A local registry | With `REGISTRY` set appropriately: @@ -277,8 +278,7 @@ helm install ../charts/catalog \ --set apiserver.tls.ca=$(base64 --wrap 0 ${SC_SERVING_CA}) \ --set apiserver.tls.cert=$(base64 --wrap 0 ${SC_SERVING_CERT}) \ --set apiserver.tls.key=$(base64 --wrap 0 ${SC_SERVING_KEY}) \ - --set apiserver.image=quay.io/kubernetes-service-catalog/apiserver:canary \ - --set controllerManager.image=quay.io/kubernetes-service-catalog/controller-manager:canary + --set image=quay.io/kubernetes-service-catalog/service-catalog:canary ``` If you choose etcd storage, the helm chart will launch an etcd server for you diff --git a/docs/v1/api-server-walkthrough.md b/docs/v1/api-server-walkthrough.md index 187c7db32406..438267633e08 100644 --- a/docs/v1/api-server-walkthrough.md +++ b/docs/v1/api-server-walkthrough.md @@ -8,8 +8,8 @@ changes on a daily basis so thing may break w/o being updated as K8s changes. -Invoking `make apiserver` in the root directory will result in an -`apiserver` binary in the `bin/` directory. +Invoking `make service-catalog` in the root directory will result in a +`service-catalog` binary in the `bin/` directory. When the API server starts up, it will generate a certificate will be generated in `/var/run/kubernetes-service-catalog/` so that directory must be @@ -29,7 +29,7 @@ To run it locally, start with: # run etcd locally on the default port $ etcd # switch to another shell and run -$ ./bin/apiserver -v 10 --etcd-servers http://localhost:2379 +$ ./bin/service-catalog apiserver -v 10 --etcd-servers http://localhost:2379 ``` Alternatively, you can run the apiserver and etcd as a pod: diff --git a/docs/walkthrough-1.6.md b/docs/walkthrough-1.6.md index b5e32d025b3a..f99f180a760b 100644 --- a/docs/walkthrough-1.6.md +++ b/docs/walkthrough-1.6.md @@ -201,8 +201,8 @@ metadata: uid: 07ecf19d-a781-11e7-8b18-0242ac110005 spec: externalID: 7f2c176a-ae67-4b5e-a826-58591d85a1d7 - externalClusterServiceClassName: user-provided-service - externalClusterServicePlanName: default + clusterServiceClassExternalName: user-provided-service + clusterServicePlanExternalName: default parameters: credentials: param-1: value-1 @@ -216,7 +216,7 @@ status: status: "True" type: Ready externalProperties: - externalClusterServicePlanName: default + clusterServicePlanExternalName: default parameterChecksum: e65c764db8429f9afef45f1e8f71bcbf9fdbe9a13306b86fd5dcc3c5d11e5dd3 parameters: credentials: diff --git a/docs/walkthrough-1.7.md b/docs/walkthrough-1.7.md index 347b0e79a382..413204ae0980 100644 --- a/docs/walkthrough-1.7.md +++ b/docs/walkthrough-1.7.md @@ -189,8 +189,8 @@ metadata: uid: 07ecf19d-a781-11e7-8b18-0242ac110005 spec: externalID: 7f2c176a-ae67-4b5e-a826-58591d85a1d7 - externalClusterServiceClassName: user-provided-service - externalClusterServicePlanName: default + clusterServiceClassExternalName: user-provided-service + clusterServicePlanExternalName: default parameters: credentials: param-1: value-1 @@ -204,7 +204,7 @@ status: status: "True" type: Ready externalProperties: - externalClusterServicePlanName: default + clusterServicePlanExternalName: default parameterChecksum: e65c764db8429f9afef45f1e8f71bcbf9fdbe9a13306b86fd5dcc3c5d11e5dd3 parameters: credentials: diff --git a/hack/common.sh b/hack/common.sh index 59ef028ea8a6..4598878dcbad 100755 --- a/hack/common.sh +++ b/hack/common.sh @@ -588,16 +588,18 @@ readonly -f os::build::get_version_vars function os::build::os_version_vars() { local git=(git --work-tree "${OS_ROOT}") - if [[ -z ${OS_GIT_CATALOG_VERSION-} ]]; then + if [[ -z "${OS_GIT_CATALOG_VERSION:-}" ]]; then # search git merge commits for template text and extract version # subject template: Merge version v0.0.14 of Service Catalog from https://github.com/openshift/service-catalog:v0.0.14+origin - local summary_text=$(${git[@]} log --merges --grep "Merge version v.* of Service Catalog from https://github.com/openshift/service-catalog" --pretty=%s -1) - if [[ $summary_text =~ Merge[[:space:]]version[[:space:]](v.*)[[:space:]]of[[:space:]]Service[[:space:]]Catalog ]]; then - OS_GIT_CATALOG_VERSION=${BASH_REMATCH[1]} + summary_text="$(${git[@]} log --merges --grep "Merge version v.* of Service Catalog from https://github.com/openshift/service-catalog" --pretty=%s -1)" + if [[ "${summary_text}" =~ Merge[[:space:]]version[[:space:]](v.*)[[:space:]]of[[:space:]]Service[[:space:]]Catalog ]]; then + OS_GIT_CATALOG_VERSION="${BASH_REMATCH[1]}" + else + os::log::fatal "Unable to find version for service catalog - (this should never happen)" fi if git_status=$("${git[@]}" status --porcelain cmd/service-catalog 2>/dev/null) && [[ -n ${git_status} ]]; then - OS_GIT_CATALOG_VERSION+="dirty" + OS_GIT_CATALOG_VERSION+="dirty" fi fi @@ -732,24 +734,33 @@ function os::build::ldflags() { declare -a ldflags=() - # ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/pkg/bootstrap/docker.defaultImageStreams" "${OS_BUILD_LDFLAGS_DEFAULT_IMAGE_STREAMS}")) - # ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/pkg/cmd/util/variable.DefaultImagePrefix" "${OS_BUILD_LDFLAGS_IMAGE_PREFIX}")) - # ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/pkg/version.majorFromGit" "${OS_GIT_MAJOR}")) - # ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/pkg/version.minorFromGit" "${OS_GIT_MINOR}")) - # ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/pkg/version.versionFromGit" "${OS_GIT_VERSION}")) - # ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/pkg/version.commitFromGit" "${OS_GIT_COMMIT}")) - ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/pkg/version.buildDate" "${buildDate}")) - # ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/vendor/k8s.io/kubernetes/pkg/version.gitCommit" "${KUBE_GIT_COMMIT}")) - # ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/vendor/k8s.io/kubernetes/pkg/version.gitVersion" "${KUBE_GIT_VERSION}")) - # ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/vendor/k8s.io/kubernetes/pkg/version.buildDate" "${buildDate}")) - # ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/vendor/k8s.io/kubernetes/pkg/version.gitTreeState" "clean")) - # ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/vendor/k8s.io/client-go/pkg/version.gitCommit" "${KUBE_GIT_COMMIT}")) - # ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/vendor/k8s.io/client-go/pkg/version.gitVersion" "${KUBE_GIT_VERSION}")) - # ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/vendor/k8s.io/client-go/pkg/version.buildDate" "${buildDate}")) - # ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/vendor/k8s.io/client-go/pkg/version.gitTreeState" "clean")) + # Origin tooling hardcodes tree state to clean, but it would look pretty + # strange to see the version not match the tree state. Since the version is + # passed in from outside of the catalog vendor directory (and is not hardcoded + # to simply "clean"), parse the suffix of the correct version string. + if [[ ${OS_GIT_CATALOG_VERSION} =~ .dirty$ ]]; then + CATALOG_DIRTY_STATUS="dirty" + else + CATALOG_DIRTY_STATUS="clean" + fi + # note that OS_GIT_CATALOG_VERSION is passed from the tito build tooling ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/pkg.VERSION" ${OS_GIT_CATALOG_VERSION})) + ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/pkg/version.gitMajor" "${OS_GIT_MAJOR}")) + ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/pkg/version.gitMinor" "${OS_GIT_MINOR}")) + ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/pkg/version.gitVersion" "${OS_GIT_VERSION}")) + ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/pkg/version.gitCommit" "${OS_GIT_COMMIT}")) + ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/pkg/version.gitTreeState" "${CATALOG_DIRTY_STATUS}")) + ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/pkg/version.buildDate" "${buildDate}")) + + ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/vendor/k8s.io/client-go/pkg/version.gitMajor" "${OS_GIT_MAJOR}")) + ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/vendor/k8s.io/client-go/pkg/version.gitMinor" "${OS_GIT_MINOR}")) + ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/vendor/k8s.io/client-go/pkg/version.gitVersion" "${OS_GIT_VERSION}")) + ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/vendor/k8s.io/client-go/pkg/version.gitCommit" "${OS_GIT_COMMIT}")) + ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/vendor/k8s.io/client-go/pkg/version.gitTreeState" "${CATALOG_DIRTY_STATUS}")) + ldflags+=($(os::build::ldflag "${OS_GO_PACKAGE}/vendor/k8s.io/client-go/pkg/version.buildDate" "${buildDate}")) + # The -ldflags parameter takes a single string, so join the output. echo "${ldflags[*]-}" } diff --git a/hack/lib/build/constants.sh b/hack/lib/build/constants.sh index 0431f8d3e3f9..035f8b210e7d 100644 --- a/hack/lib/build/constants.sh +++ b/hack/lib/build/constants.sh @@ -32,8 +32,7 @@ readonly OS_IMAGE_COMPILE_BINARIES=("${OS_SCRATCH_IMAGE_COMPILE_TARGETS_LINUX[@] readonly OS_CROSS_COMPILE_TARGETS=( #cmd/openshift #cmd/oc - cmd/apiserver - cmd/controller-manager + cmd/service-catalog ) readonly OS_CROSS_COMPILE_BINARIES=("${OS_CROSS_COMPILE_TARGETS[@]##*/}") diff --git a/pkg/apis/servicecatalog/types.go b/pkg/apis/servicecatalog/types.go index 727e9b4e7847..b044a9e4eff1 100644 --- a/pkg/apis/servicecatalog/types.go +++ b/pkg/apis/servicecatalog/types.go @@ -17,7 +17,6 @@ limitations under the License. package servicecatalog import ( - "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -101,11 +100,6 @@ type ServiceBrokerAuthInfo struct { // The value is referenced from the 'token' field of the given secret. This value should only // contain the token value and not the `Bearer` scheme. Bearer *BearerTokenAuthConfig - - // DEPRECATED: use `Basic` field for configuring basic authentication instead. - // BasicAuthSecret is a reference to a Secret containing auth information the - // catalog should use to authenticate to this ServiceBroker using basic auth. - BasicAuthSecret *v1.ObjectReference } // BasicAuthConfig provides config for the basic authentication. @@ -116,7 +110,7 @@ type BasicAuthConfig struct { // Required at least one of the fields: // - Secret.Data["username"] - username used for authentication // - Secret.Data["password"] - password or token needed for authentication - SecretRef *v1.ObjectReference + SecretRef *ObjectReference } // BearerTokenAuthConfig provides config for the bearer token authentication. @@ -126,7 +120,7 @@ type BearerTokenAuthConfig struct { // // Required field: // - Secret.Data["token"] - bearer token for authentication - SecretRef *v1.ObjectReference + SecretRef *ObjectReference } const ( @@ -367,7 +361,7 @@ type ClusterServicePlanSpec struct { // ClusterServiceClassRef is a reference to the service class that // owns this plan. - ClusterServiceClassRef v1.LocalObjectReference + ClusterServiceClassRef ClusterObjectReference } // ClusterServicePlanStatus represents status information about a @@ -417,20 +411,20 @@ type ServiceInstance struct { // specify the desired Class/Plan, this structure specifies the // allowed ways to specify the intent. type PlanReference struct { - // ExternalClusterServiceClassName is the human-readable name of the + // ClusterServiceClassExternalName is the human-readable name of the // service as reported by the broker. Note that if the broker changes // the name of the ClusterServiceClass, it will not be reflected here, // and to see the current name of the ClusterServiceClass, you should // follow the ClusterServiceClassRef below. // // Immutable. - ExternalClusterServiceClassName string - // ExternalClusterServicePlanName is the human-readable name of the plan + ClusterServiceClassExternalName string + // ClusterServicePlanExternalName is the human-readable name of the plan // as reported by the broker. Note that if the broker changes the name // of the ClusterServicePlan, it will not be reflected here, and to see // the current name of the ClusterServicePlan, you should follow the // ClusterServicePlanRef below. - ExternalClusterServicePlanName string + ClusterServicePlanExternalName string // ClusterServiceClassName is the kubernetes name of the // ClusterServiceClass. @@ -447,12 +441,12 @@ type ServiceInstanceSpec struct { // ClusterServiceClassRef is a reference to the ClusterServiceClass // that the user selected. - // This is set by the controller based on ExternalClusterServiceClassName - ClusterServiceClassRef *v1.ObjectReference + // This is set by the controller based on ClusterServiceClassExternalName + ClusterServiceClassRef *ClusterObjectReference // ClusterServicePlanRef is a reference to the ClusterServicePlan // that the user selected. - // This is set by the controller based on ExternalClusterServicePlanName - ClusterServicePlanRef *v1.ObjectReference + // This is set by the controller based on ClusterServicePlanExternalName + ClusterServicePlanRef *ClusterObjectReference // Parameters is a set of the parameters to be passed to the underlying // broker. The inline YAML/JSON payload to be translated into equivalent @@ -594,10 +588,10 @@ const ( // ServiceInstancePropertiesState is the state of a ServiceInstance that // the ServiceBroker knows about. type ServiceInstancePropertiesState struct { - // ExternalClusterServicePlanName is the name of the plan that the broker knows this + // ClusterServicePlanExternalName is the name of the plan that the broker knows this // ServiceInstance to be on. This is the human readable plan name from the // OSB API. - ExternalClusterServicePlanName string + ClusterServicePlanExternalName string // Parameters is a blob of the parameters and their values that the broker // knows about for this ServiceInstance. If a parameter was sourced from @@ -643,7 +637,7 @@ type ServiceBindingSpec struct { // ServiceInstanceRef is the reference to the Instance this ServiceBinding is to. // // Immutable. - ServiceInstanceRef v1.LocalObjectReference + ServiceInstanceRef LocalObjectReference // Parameters is a set of the parameters to be passed to the underlying // broker. The inline YAML/JSON payload to be translated into equivalent @@ -797,3 +791,26 @@ type SecretKeyReference struct { // The key of the secret to select from. Must be a valid secret key. Key string } + +// ObjectReference contains enough information to let you locate the +// referenced object. +type ObjectReference struct { + // Namespace of the referent. + Namespace string + // Name of the referent. + Name string +} + +// LocalObjectReference contains enough information to let you locate the +// referenced object inside the same namespace. +type LocalObjectReference struct { + // Name of the referent. + Name string +} + +// ClusterObjectReference contains enough information to let you locate the +// cluster-scoped referenced object. +type ClusterObjectReference struct { + // Name of the referent. + Name string +} diff --git a/pkg/apis/servicecatalog/v1beta1/conversion.go b/pkg/apis/servicecatalog/v1beta1/conversion.go index 056415033f1f..07983615b96a 100644 --- a/pkg/apis/servicecatalog/v1beta1/conversion.go +++ b/pkg/apis/servicecatalog/v1beta1/conversion.go @@ -49,3 +49,15 @@ func ClusterServiceClassFieldLabelConversionFunc(label, value string) (string, s return "", "", fmt.Errorf("field label not supported: %s", label) } } + +// ServiceInstanceFieldLabelConversionFunc does not convert anything, just returns +// what it's given for the supported fields, and errors for unsupported. +func ServiceInstanceFieldLabelConversionFunc(label, value string) (string, string, error) { + switch label { + case "spec.clusterServiceClassRef.name", + "spec.clusterServicePlanRef.name": + return label, value, nil + default: + return "", "", fmt.Errorf("field label not supported: %s", label) + } +} diff --git a/pkg/apis/servicecatalog/v1beta1/conversion_test.go b/pkg/apis/servicecatalog/v1beta1/conversion_test.go index 68cba721b3b1..04fa43c0e2fe 100644 --- a/pkg/apis/servicecatalog/v1beta1/conversion_test.go +++ b/pkg/apis/servicecatalog/v1beta1/conversion_test.go @@ -130,6 +130,38 @@ func TestClusterServiceClassFieldLabelConversionFunc(t *testing.T) { } +func TestServiceInstanceFieldLabelConversionFunc(t *testing.T) { + cases := []testcase{ + { + name: "spec.clusterServiceClassRef.name works", + inLabel: "spec.clusterServiceClassRef.name", + inValue: "someref", + outLabel: "spec.clusterServiceClassRef.name", + outValue: "someref", + success: true, + }, + { + name: "spec.clusterServicePlanRef.name works", + inLabel: "spec.clusterServicePlanRef.name", + inValue: "someref", + outLabel: "spec.clusterServicePlanRef.name", + outValue: "someref", + success: true, + }, + { + name: "random fails", + inLabel: "spec.random", + inValue: "randomvalue", + outLabel: "", + outValue: "", + success: false, + expectedError: "field label not supported: spec.random", + }, + } + runTestCases(t, cases, "ServiceInstanceFieldLabelConversionFunc", ServiceInstanceFieldLabelConversionFunc) + +} + func runTestCases(t *testing.T, cases []testcase, testFuncName string, testFunc conversionFunc) { for _, tc := range cases { outLabel, outValue, err := testFunc(tc.inLabel, tc.inValue) diff --git a/pkg/apis/servicecatalog/v1beta1/register.go b/pkg/apis/servicecatalog/v1beta1/register.go index 397ec6ec8250..e6c979f778ed 100644 --- a/pkg/apis/servicecatalog/v1beta1/register.go +++ b/pkg/apis/servicecatalog/v1beta1/register.go @@ -65,5 +65,7 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(schema.GroupVersion{Version: "v1"}, &metav1.Status{}) scheme.AddFieldLabelConversionFunc("servicecatalog.k8s.io/v1beta1", "ClusterServiceClass", ClusterServiceClassFieldLabelConversionFunc) scheme.AddFieldLabelConversionFunc("servicecatalog.k8s.io/v1beta1", "ClusterServicePlan", ClusterServicePlanFieldLabelConversionFunc) + scheme.AddFieldLabelConversionFunc("servicecatalog.k8s.io/v1beta1", "ServiceInstance", ServiceInstanceFieldLabelConversionFunc) + return nil } diff --git a/pkg/apis/servicecatalog/v1beta1/types.go b/pkg/apis/servicecatalog/v1beta1/types.go index d2ec1128b3ea..6aad01402c9a 100644 --- a/pkg/apis/servicecatalog/v1beta1/types.go +++ b/pkg/apis/servicecatalog/v1beta1/types.go @@ -17,7 +17,6 @@ limitations under the License. package v1beta1 import ( - "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -101,11 +100,6 @@ type ServiceBrokerAuthInfo struct { // The value is referenced from the 'token' field of the given secret. This value should only // contain the token value and not the `Bearer` scheme. Bearer *BearerTokenAuthConfig `json:"bearer,omitempty"` - - // DEPRECATED: use `Basic` field for configuring basic authentication instead. - // BasicAuthSecret is a reference to a Secret containing auth information the - // catalog should use to authenticate to this ServiceBroker using basic auth. - BasicAuthSecret *v1.ObjectReference `json:"basicAuthSecret,omitempty"` } // BasicAuthConfig provides config for the basic authentication. @@ -116,7 +110,7 @@ type BasicAuthConfig struct { // Required at least one of the fields: // - Secret.Data["username"] - username used for authentication // - Secret.Data["password"] - password or token needed for authentication - SecretRef *v1.ObjectReference `json:"secretRef,omitempty"` + SecretRef *ObjectReference `json:"secretRef,omitempty"` } // BearerTokenAuthConfig provides config for the bearer token authentication. @@ -126,7 +120,7 @@ type BearerTokenAuthConfig struct { // // Required field: // - Secret.Data["token"] - bearer token for authentication - SecretRef *v1.ObjectReference `json:"secretRef,omitempty"` + SecretRef *ObjectReference `json:"secretRef,omitempty"` } const ( @@ -372,7 +366,7 @@ type ClusterServicePlanSpec struct { // ClusterServiceClassRef is a reference to the service class that // owns this plan. - ClusterServiceClassRef v1.LocalObjectReference `json:"clusterServiceClassRef"` + ClusterServiceClassRef ClusterObjectReference `json:"clusterServiceClassRef"` } // ClusterServicePlanStatus represents status information about a @@ -429,26 +423,26 @@ type ServiceInstance struct { // allowed ways to specify the intent. // // Currently supported ways: -// - ExternalClusterServiceClassName and ExternalClusterServicePlanName +// - ClusterServiceClassExternalName and ClusterServicePlanExternalName // - ClusterServiceClassName and ClusterServicePlanName // // For both of these ways, if a ClusterServiceClass only has one plan // then leaving the *ServicePlanName is optional. type PlanReference struct { - // ExternalClusterServiceClassName is the human-readable name of the + // ClusterServiceClassExternalName is the human-readable name of the // service as reported by the broker. Note that if the broker changes // the name of the ClusterServiceClass, it will not be reflected here, // and to see the current name of the ClusterServiceClass, you should // follow the ClusterServiceClassRef below. // // Immutable. - ExternalClusterServiceClassName string `json:"externalClusterServiceClassName,omitempty"` - // ExternalClusterServicePlanName is the human-readable name of the plan + ClusterServiceClassExternalName string `json:"clusterServiceClassExternalName,omitempty"` + // ClusterServicePlanExternalName is the human-readable name of the plan // as reported by the broker. Note that if the broker changes the name // of the ClusterServicePlan, it will not be reflected here, and to see // the current name of the ClusterServicePlan, you should follow the // ClusterServicePlanRef below. - ExternalClusterServicePlanName string `json:"externalClusterServicePlanName,omitempty"` + ClusterServicePlanExternalName string `json:"clusterServicePlanExternalName,omitempty"` // ClusterServiceClassName is the kubernetes name of the // ClusterServiceClass. @@ -467,13 +461,13 @@ type ServiceInstanceSpec struct { // ClusterServiceClassRef is a reference to the ClusterServiceClass // that the user selected. // This is set by the controller based on - // ExternalClusterServiceClassName - ClusterServiceClassRef *v1.ObjectReference `json:"clusterServiceClassRef,omitempty"` + // ClusterServiceClassExternalName + ClusterServiceClassRef *ClusterObjectReference `json:"clusterServiceClassRef,omitempty"` // ClusterServicePlanRef is a reference to the ClusterServicePlan // that the user selected. // This is set by the controller based on - // ExternalClusterServicePlanName - ClusterServicePlanRef *v1.ObjectReference `json:"clusterServicePlanRef,omitempty"` + // ClusterServicePlanExternalName + ClusterServicePlanRef *ClusterObjectReference `json:"clusterServicePlanRef,omitempty"` // Parameters is a set of the parameters to be passed to the underlying // broker. The inline YAML/JSON payload to be translated into equivalent @@ -615,10 +609,10 @@ const ( // ServiceInstancePropertiesState is the state of a ServiceInstance that // the ClusterServiceBroker knows about. type ServiceInstancePropertiesState struct { - // ExternalClusterServicePlanName is the name of the plan that the + // ClusterServicePlanExternalName is the name of the plan that the // broker knows this ServiceInstance to be on. This is the human // readable plan name from the OSB API. - ExternalClusterServicePlanName string `json:"externalClusterServicePlanName"` + ClusterServicePlanExternalName string `json:"clusterServicePlanExternalName"` // Parameters is a blob of the parameters and their values that the broker // knows about for this ServiceInstance. If a parameter was sourced from @@ -664,7 +658,7 @@ type ServiceBindingSpec struct { // ServiceInstanceRef is the reference to the Instance this ServiceBinding is to. // // Immutable. - ServiceInstanceRef v1.LocalObjectReference `json:"instanceRef"` + ServiceInstanceRef LocalObjectReference `json:"instanceRef"` // Parameters is a set of the parameters to be passed to the underlying // broker. The inline YAML/JSON payload to be translated into equivalent @@ -818,3 +812,26 @@ type SecretKeyReference struct { // The key of the secret to select from. Must be a valid secret key. Key string `json:"key"` } + +// ObjectReference contains enough information to let you locate the +// referenced object. +type ObjectReference struct { + // Namespace of the referent. + Namespace string `json:"namespace,omitempty"` + // Name of the referent. + Name string `json:"name,omitempty"` +} + +// LocalObjectReference contains enough information to let you locate the +// referenced object inside the same namespace. +type LocalObjectReference struct { + // Name of the referent. + Name string `json:"name,omitempty"` +} + +// ClusterObjectReference contains enough information to let you locate the +// cluster-scoped referenced object. +type ClusterObjectReference struct { + // Name of the referent. + Name string `json:"name,omitempty"` +} diff --git a/pkg/apis/servicecatalog/v1beta1/zz_generated.conversion.go b/pkg/apis/servicecatalog/v1beta1/zz_generated.conversion.go index 2beb02c5ae40..e0289ba0fb0e 100644 --- a/pkg/apis/servicecatalog/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/servicecatalog/v1beta1/zz_generated.conversion.go @@ -22,8 +22,7 @@ package v1beta1 import ( servicecatalog "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog" - v1 "k8s.io/api/core/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" unsafe "unsafe" @@ -41,6 +40,8 @@ func RegisterConversions(scheme *runtime.Scheme) error { Convert_servicecatalog_BasicAuthConfig_To_v1beta1_BasicAuthConfig, Convert_v1beta1_BearerTokenAuthConfig_To_servicecatalog_BearerTokenAuthConfig, Convert_servicecatalog_BearerTokenAuthConfig_To_v1beta1_BearerTokenAuthConfig, + Convert_v1beta1_ClusterObjectReference_To_servicecatalog_ClusterObjectReference, + Convert_servicecatalog_ClusterObjectReference_To_v1beta1_ClusterObjectReference, Convert_v1beta1_ClusterServiceBroker_To_servicecatalog_ClusterServiceBroker, Convert_servicecatalog_ClusterServiceBroker_To_v1beta1_ClusterServiceBroker, Convert_v1beta1_ClusterServiceBrokerList_To_servicecatalog_ClusterServiceBrokerList, @@ -65,6 +66,10 @@ func RegisterConversions(scheme *runtime.Scheme) error { Convert_servicecatalog_ClusterServicePlanSpec_To_v1beta1_ClusterServicePlanSpec, Convert_v1beta1_ClusterServicePlanStatus_To_servicecatalog_ClusterServicePlanStatus, Convert_servicecatalog_ClusterServicePlanStatus_To_v1beta1_ClusterServicePlanStatus, + Convert_v1beta1_LocalObjectReference_To_servicecatalog_LocalObjectReference, + Convert_servicecatalog_LocalObjectReference_To_v1beta1_LocalObjectReference, + Convert_v1beta1_ObjectReference_To_servicecatalog_ObjectReference, + Convert_servicecatalog_ObjectReference_To_v1beta1_ObjectReference, Convert_v1beta1_ParametersFromSource_To_servicecatalog_ParametersFromSource, Convert_servicecatalog_ParametersFromSource_To_v1beta1_ParametersFromSource, Convert_v1beta1_PlanReference_To_servicecatalog_PlanReference, @@ -105,7 +110,7 @@ func RegisterConversions(scheme *runtime.Scheme) error { } func autoConvert_v1beta1_BasicAuthConfig_To_servicecatalog_BasicAuthConfig(in *BasicAuthConfig, out *servicecatalog.BasicAuthConfig, s conversion.Scope) error { - out.SecretRef = (*v1.ObjectReference)(unsafe.Pointer(in.SecretRef)) + out.SecretRef = (*servicecatalog.ObjectReference)(unsafe.Pointer(in.SecretRef)) return nil } @@ -115,7 +120,7 @@ func Convert_v1beta1_BasicAuthConfig_To_servicecatalog_BasicAuthConfig(in *Basic } func autoConvert_servicecatalog_BasicAuthConfig_To_v1beta1_BasicAuthConfig(in *servicecatalog.BasicAuthConfig, out *BasicAuthConfig, s conversion.Scope) error { - out.SecretRef = (*v1.ObjectReference)(unsafe.Pointer(in.SecretRef)) + out.SecretRef = (*ObjectReference)(unsafe.Pointer(in.SecretRef)) return nil } @@ -125,7 +130,7 @@ func Convert_servicecatalog_BasicAuthConfig_To_v1beta1_BasicAuthConfig(in *servi } func autoConvert_v1beta1_BearerTokenAuthConfig_To_servicecatalog_BearerTokenAuthConfig(in *BearerTokenAuthConfig, out *servicecatalog.BearerTokenAuthConfig, s conversion.Scope) error { - out.SecretRef = (*v1.ObjectReference)(unsafe.Pointer(in.SecretRef)) + out.SecretRef = (*servicecatalog.ObjectReference)(unsafe.Pointer(in.SecretRef)) return nil } @@ -135,7 +140,7 @@ func Convert_v1beta1_BearerTokenAuthConfig_To_servicecatalog_BearerTokenAuthConf } func autoConvert_servicecatalog_BearerTokenAuthConfig_To_v1beta1_BearerTokenAuthConfig(in *servicecatalog.BearerTokenAuthConfig, out *BearerTokenAuthConfig, s conversion.Scope) error { - out.SecretRef = (*v1.ObjectReference)(unsafe.Pointer(in.SecretRef)) + out.SecretRef = (*ObjectReference)(unsafe.Pointer(in.SecretRef)) return nil } @@ -144,6 +149,26 @@ func Convert_servicecatalog_BearerTokenAuthConfig_To_v1beta1_BearerTokenAuthConf return autoConvert_servicecatalog_BearerTokenAuthConfig_To_v1beta1_BearerTokenAuthConfig(in, out, s) } +func autoConvert_v1beta1_ClusterObjectReference_To_servicecatalog_ClusterObjectReference(in *ClusterObjectReference, out *servicecatalog.ClusterObjectReference, s conversion.Scope) error { + out.Name = in.Name + return nil +} + +// Convert_v1beta1_ClusterObjectReference_To_servicecatalog_ClusterObjectReference is an autogenerated conversion function. +func Convert_v1beta1_ClusterObjectReference_To_servicecatalog_ClusterObjectReference(in *ClusterObjectReference, out *servicecatalog.ClusterObjectReference, s conversion.Scope) error { + return autoConvert_v1beta1_ClusterObjectReference_To_servicecatalog_ClusterObjectReference(in, out, s) +} + +func autoConvert_servicecatalog_ClusterObjectReference_To_v1beta1_ClusterObjectReference(in *servicecatalog.ClusterObjectReference, out *ClusterObjectReference, s conversion.Scope) error { + out.Name = in.Name + return nil +} + +// Convert_servicecatalog_ClusterObjectReference_To_v1beta1_ClusterObjectReference is an autogenerated conversion function. +func Convert_servicecatalog_ClusterObjectReference_To_v1beta1_ClusterObjectReference(in *servicecatalog.ClusterObjectReference, out *ClusterObjectReference, s conversion.Scope) error { + return autoConvert_servicecatalog_ClusterObjectReference_To_v1beta1_ClusterObjectReference(in, out, s) +} + func autoConvert_v1beta1_ClusterServiceBroker_To_servicecatalog_ClusterServiceBroker(in *ClusterServiceBroker, out *servicecatalog.ClusterServiceBroker, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if err := Convert_v1beta1_ClusterServiceBrokerSpec_To_servicecatalog_ClusterServiceBrokerSpec(&in.Spec, &out.Spec, s); err != nil { @@ -204,7 +229,7 @@ func autoConvert_v1beta1_ClusterServiceBrokerSpec_To_servicecatalog_ClusterServi out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify out.CABundle = *(*[]byte)(unsafe.Pointer(&in.CABundle)) out.RelistBehavior = servicecatalog.ServiceBrokerRelistBehavior(in.RelistBehavior) - out.RelistDuration = (*meta_v1.Duration)(unsafe.Pointer(in.RelistDuration)) + out.RelistDuration = (*v1.Duration)(unsafe.Pointer(in.RelistDuration)) out.RelistRequests = in.RelistRequests return nil } @@ -220,7 +245,7 @@ func autoConvert_servicecatalog_ClusterServiceBrokerSpec_To_v1beta1_ClusterServi out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify out.CABundle = *(*[]byte)(unsafe.Pointer(&in.CABundle)) out.RelistBehavior = ServiceBrokerRelistBehavior(in.RelistBehavior) - out.RelistDuration = (*meta_v1.Duration)(unsafe.Pointer(in.RelistDuration)) + out.RelistDuration = (*v1.Duration)(unsafe.Pointer(in.RelistDuration)) out.RelistRequests = in.RelistRequests return nil } @@ -233,7 +258,7 @@ func Convert_servicecatalog_ClusterServiceBrokerSpec_To_v1beta1_ClusterServiceBr func autoConvert_v1beta1_ClusterServiceBrokerStatus_To_servicecatalog_ClusterServiceBrokerStatus(in *ClusterServiceBrokerStatus, out *servicecatalog.ClusterServiceBrokerStatus, s conversion.Scope) error { out.Conditions = *(*[]servicecatalog.ServiceBrokerCondition)(unsafe.Pointer(&in.Conditions)) out.ReconciledGeneration = in.ReconciledGeneration - out.OperationStartTime = (*meta_v1.Time)(unsafe.Pointer(in.OperationStartTime)) + out.OperationStartTime = (*v1.Time)(unsafe.Pointer(in.OperationStartTime)) return nil } @@ -245,7 +270,7 @@ func Convert_v1beta1_ClusterServiceBrokerStatus_To_servicecatalog_ClusterService func autoConvert_servicecatalog_ClusterServiceBrokerStatus_To_v1beta1_ClusterServiceBrokerStatus(in *servicecatalog.ClusterServiceBrokerStatus, out *ClusterServiceBrokerStatus, s conversion.Scope) error { out.Conditions = *(*[]ServiceBrokerCondition)(unsafe.Pointer(&in.Conditions)) out.ReconciledGeneration = in.ReconciledGeneration - out.OperationStartTime = (*meta_v1.Time)(unsafe.Pointer(in.OperationStartTime)) + out.OperationStartTime = (*v1.Time)(unsafe.Pointer(in.OperationStartTime)) return nil } @@ -429,7 +454,9 @@ func autoConvert_v1beta1_ClusterServicePlanSpec_To_servicecatalog_ClusterService out.ServiceInstanceCreateParameterSchema = (*runtime.RawExtension)(unsafe.Pointer(in.ServiceInstanceCreateParameterSchema)) out.ServiceInstanceUpdateParameterSchema = (*runtime.RawExtension)(unsafe.Pointer(in.ServiceInstanceUpdateParameterSchema)) out.ServiceBindingCreateParameterSchema = (*runtime.RawExtension)(unsafe.Pointer(in.ServiceBindingCreateParameterSchema)) - out.ClusterServiceClassRef = in.ClusterServiceClassRef + if err := Convert_v1beta1_ClusterObjectReference_To_servicecatalog_ClusterObjectReference(&in.ClusterServiceClassRef, &out.ClusterServiceClassRef, s); err != nil { + return err + } return nil } @@ -449,7 +476,9 @@ func autoConvert_servicecatalog_ClusterServicePlanSpec_To_v1beta1_ClusterService out.ServiceInstanceCreateParameterSchema = (*runtime.RawExtension)(unsafe.Pointer(in.ServiceInstanceCreateParameterSchema)) out.ServiceInstanceUpdateParameterSchema = (*runtime.RawExtension)(unsafe.Pointer(in.ServiceInstanceUpdateParameterSchema)) out.ServiceBindingCreateParameterSchema = (*runtime.RawExtension)(unsafe.Pointer(in.ServiceBindingCreateParameterSchema)) - out.ClusterServiceClassRef = in.ClusterServiceClassRef + if err := Convert_servicecatalog_ClusterObjectReference_To_v1beta1_ClusterObjectReference(&in.ClusterServiceClassRef, &out.ClusterServiceClassRef, s); err != nil { + return err + } return nil } @@ -478,6 +507,48 @@ func Convert_servicecatalog_ClusterServicePlanStatus_To_v1beta1_ClusterServicePl return autoConvert_servicecatalog_ClusterServicePlanStatus_To_v1beta1_ClusterServicePlanStatus(in, out, s) } +func autoConvert_v1beta1_LocalObjectReference_To_servicecatalog_LocalObjectReference(in *LocalObjectReference, out *servicecatalog.LocalObjectReference, s conversion.Scope) error { + out.Name = in.Name + return nil +} + +// Convert_v1beta1_LocalObjectReference_To_servicecatalog_LocalObjectReference is an autogenerated conversion function. +func Convert_v1beta1_LocalObjectReference_To_servicecatalog_LocalObjectReference(in *LocalObjectReference, out *servicecatalog.LocalObjectReference, s conversion.Scope) error { + return autoConvert_v1beta1_LocalObjectReference_To_servicecatalog_LocalObjectReference(in, out, s) +} + +func autoConvert_servicecatalog_LocalObjectReference_To_v1beta1_LocalObjectReference(in *servicecatalog.LocalObjectReference, out *LocalObjectReference, s conversion.Scope) error { + out.Name = in.Name + return nil +} + +// Convert_servicecatalog_LocalObjectReference_To_v1beta1_LocalObjectReference is an autogenerated conversion function. +func Convert_servicecatalog_LocalObjectReference_To_v1beta1_LocalObjectReference(in *servicecatalog.LocalObjectReference, out *LocalObjectReference, s conversion.Scope) error { + return autoConvert_servicecatalog_LocalObjectReference_To_v1beta1_LocalObjectReference(in, out, s) +} + +func autoConvert_v1beta1_ObjectReference_To_servicecatalog_ObjectReference(in *ObjectReference, out *servicecatalog.ObjectReference, s conversion.Scope) error { + out.Namespace = in.Namespace + out.Name = in.Name + return nil +} + +// Convert_v1beta1_ObjectReference_To_servicecatalog_ObjectReference is an autogenerated conversion function. +func Convert_v1beta1_ObjectReference_To_servicecatalog_ObjectReference(in *ObjectReference, out *servicecatalog.ObjectReference, s conversion.Scope) error { + return autoConvert_v1beta1_ObjectReference_To_servicecatalog_ObjectReference(in, out, s) +} + +func autoConvert_servicecatalog_ObjectReference_To_v1beta1_ObjectReference(in *servicecatalog.ObjectReference, out *ObjectReference, s conversion.Scope) error { + out.Namespace = in.Namespace + out.Name = in.Name + return nil +} + +// Convert_servicecatalog_ObjectReference_To_v1beta1_ObjectReference is an autogenerated conversion function. +func Convert_servicecatalog_ObjectReference_To_v1beta1_ObjectReference(in *servicecatalog.ObjectReference, out *ObjectReference, s conversion.Scope) error { + return autoConvert_servicecatalog_ObjectReference_To_v1beta1_ObjectReference(in, out, s) +} + func autoConvert_v1beta1_ParametersFromSource_To_servicecatalog_ParametersFromSource(in *ParametersFromSource, out *servicecatalog.ParametersFromSource, s conversion.Scope) error { out.SecretKeyRef = (*servicecatalog.SecretKeyReference)(unsafe.Pointer(in.SecretKeyRef)) return nil @@ -499,8 +570,8 @@ func Convert_servicecatalog_ParametersFromSource_To_v1beta1_ParametersFromSource } func autoConvert_v1beta1_PlanReference_To_servicecatalog_PlanReference(in *PlanReference, out *servicecatalog.PlanReference, s conversion.Scope) error { - out.ExternalClusterServiceClassName = in.ExternalClusterServiceClassName - out.ExternalClusterServicePlanName = in.ExternalClusterServicePlanName + out.ClusterServiceClassExternalName = in.ClusterServiceClassExternalName + out.ClusterServicePlanExternalName = in.ClusterServicePlanExternalName out.ClusterServiceClassName = in.ClusterServiceClassName out.ClusterServicePlanName = in.ClusterServicePlanName return nil @@ -512,8 +583,8 @@ func Convert_v1beta1_PlanReference_To_servicecatalog_PlanReference(in *PlanRefer } func autoConvert_servicecatalog_PlanReference_To_v1beta1_PlanReference(in *servicecatalog.PlanReference, out *PlanReference, s conversion.Scope) error { - out.ExternalClusterServiceClassName = in.ExternalClusterServiceClassName - out.ExternalClusterServicePlanName = in.ExternalClusterServicePlanName + out.ClusterServiceClassExternalName = in.ClusterServiceClassExternalName + out.ClusterServicePlanExternalName = in.ClusterServicePlanExternalName out.ClusterServiceClassName = in.ClusterServiceClassName out.ClusterServicePlanName = in.ClusterServicePlanName return nil @@ -653,7 +724,9 @@ func Convert_servicecatalog_ServiceBindingPropertiesState_To_v1beta1_ServiceBind } func autoConvert_v1beta1_ServiceBindingSpec_To_servicecatalog_ServiceBindingSpec(in *ServiceBindingSpec, out *servicecatalog.ServiceBindingSpec, s conversion.Scope) error { - out.ServiceInstanceRef = in.ServiceInstanceRef + if err := Convert_v1beta1_LocalObjectReference_To_servicecatalog_LocalObjectReference(&in.ServiceInstanceRef, &out.ServiceInstanceRef, s); err != nil { + return err + } out.Parameters = (*runtime.RawExtension)(unsafe.Pointer(in.Parameters)) out.ParametersFrom = *(*[]servicecatalog.ParametersFromSource)(unsafe.Pointer(&in.ParametersFrom)) out.SecretName = in.SecretName @@ -668,7 +741,9 @@ func Convert_v1beta1_ServiceBindingSpec_To_servicecatalog_ServiceBindingSpec(in } func autoConvert_servicecatalog_ServiceBindingSpec_To_v1beta1_ServiceBindingSpec(in *servicecatalog.ServiceBindingSpec, out *ServiceBindingSpec, s conversion.Scope) error { - out.ServiceInstanceRef = in.ServiceInstanceRef + if err := Convert_servicecatalog_LocalObjectReference_To_v1beta1_LocalObjectReference(&in.ServiceInstanceRef, &out.ServiceInstanceRef, s); err != nil { + return err + } out.Parameters = (*runtime.RawExtension)(unsafe.Pointer(in.Parameters)) out.ParametersFrom = *(*[]ParametersFromSource)(unsafe.Pointer(&in.ParametersFrom)) out.SecretName = in.SecretName @@ -686,7 +761,7 @@ func autoConvert_v1beta1_ServiceBindingStatus_To_servicecatalog_ServiceBindingSt out.Conditions = *(*[]servicecatalog.ServiceBindingCondition)(unsafe.Pointer(&in.Conditions)) out.CurrentOperation = servicecatalog.ServiceBindingOperation(in.CurrentOperation) out.ReconciledGeneration = in.ReconciledGeneration - out.OperationStartTime = (*meta_v1.Time)(unsafe.Pointer(in.OperationStartTime)) + out.OperationStartTime = (*v1.Time)(unsafe.Pointer(in.OperationStartTime)) out.InProgressProperties = (*servicecatalog.ServiceBindingPropertiesState)(unsafe.Pointer(in.InProgressProperties)) out.ExternalProperties = (*servicecatalog.ServiceBindingPropertiesState)(unsafe.Pointer(in.ExternalProperties)) out.OrphanMitigationInProgress = in.OrphanMitigationInProgress @@ -702,7 +777,7 @@ func autoConvert_servicecatalog_ServiceBindingStatus_To_v1beta1_ServiceBindingSt out.Conditions = *(*[]ServiceBindingCondition)(unsafe.Pointer(&in.Conditions)) out.CurrentOperation = ServiceBindingOperation(in.CurrentOperation) out.ReconciledGeneration = in.ReconciledGeneration - out.OperationStartTime = (*meta_v1.Time)(unsafe.Pointer(in.OperationStartTime)) + out.OperationStartTime = (*v1.Time)(unsafe.Pointer(in.OperationStartTime)) out.InProgressProperties = (*ServiceBindingPropertiesState)(unsafe.Pointer(in.InProgressProperties)) out.ExternalProperties = (*ServiceBindingPropertiesState)(unsafe.Pointer(in.ExternalProperties)) out.OrphanMitigationInProgress = in.OrphanMitigationInProgress @@ -717,7 +792,6 @@ func Convert_servicecatalog_ServiceBindingStatus_To_v1beta1_ServiceBindingStatus func autoConvert_v1beta1_ServiceBrokerAuthInfo_To_servicecatalog_ServiceBrokerAuthInfo(in *ServiceBrokerAuthInfo, out *servicecatalog.ServiceBrokerAuthInfo, s conversion.Scope) error { out.Basic = (*servicecatalog.BasicAuthConfig)(unsafe.Pointer(in.Basic)) out.Bearer = (*servicecatalog.BearerTokenAuthConfig)(unsafe.Pointer(in.Bearer)) - out.BasicAuthSecret = (*v1.ObjectReference)(unsafe.Pointer(in.BasicAuthSecret)) return nil } @@ -729,7 +803,6 @@ func Convert_v1beta1_ServiceBrokerAuthInfo_To_servicecatalog_ServiceBrokerAuthIn func autoConvert_servicecatalog_ServiceBrokerAuthInfo_To_v1beta1_ServiceBrokerAuthInfo(in *servicecatalog.ServiceBrokerAuthInfo, out *ServiceBrokerAuthInfo, s conversion.Scope) error { out.Basic = (*BasicAuthConfig)(unsafe.Pointer(in.Basic)) out.Bearer = (*BearerTokenAuthConfig)(unsafe.Pointer(in.Bearer)) - out.BasicAuthSecret = (*v1.ObjectReference)(unsafe.Pointer(in.BasicAuthSecret)) return nil } @@ -849,7 +922,7 @@ func Convert_servicecatalog_ServiceInstanceList_To_v1beta1_ServiceInstanceList(i } func autoConvert_v1beta1_ServiceInstancePropertiesState_To_servicecatalog_ServiceInstancePropertiesState(in *ServiceInstancePropertiesState, out *servicecatalog.ServiceInstancePropertiesState, s conversion.Scope) error { - out.ExternalClusterServicePlanName = in.ExternalClusterServicePlanName + out.ClusterServicePlanExternalName = in.ClusterServicePlanExternalName out.Parameters = (*runtime.RawExtension)(unsafe.Pointer(in.Parameters)) out.ParametersChecksum = in.ParametersChecksum out.UserInfo = (*servicecatalog.UserInfo)(unsafe.Pointer(in.UserInfo)) @@ -862,7 +935,7 @@ func Convert_v1beta1_ServiceInstancePropertiesState_To_servicecatalog_ServiceIns } func autoConvert_servicecatalog_ServiceInstancePropertiesState_To_v1beta1_ServiceInstancePropertiesState(in *servicecatalog.ServiceInstancePropertiesState, out *ServiceInstancePropertiesState, s conversion.Scope) error { - out.ExternalClusterServicePlanName = in.ExternalClusterServicePlanName + out.ClusterServicePlanExternalName = in.ClusterServicePlanExternalName out.Parameters = (*runtime.RawExtension)(unsafe.Pointer(in.Parameters)) out.ParametersChecksum = in.ParametersChecksum out.UserInfo = (*UserInfo)(unsafe.Pointer(in.UserInfo)) @@ -878,8 +951,8 @@ func autoConvert_v1beta1_ServiceInstanceSpec_To_servicecatalog_ServiceInstanceSp if err := Convert_v1beta1_PlanReference_To_servicecatalog_PlanReference(&in.PlanReference, &out.PlanReference, s); err != nil { return err } - out.ClusterServiceClassRef = (*v1.ObjectReference)(unsafe.Pointer(in.ClusterServiceClassRef)) - out.ClusterServicePlanRef = (*v1.ObjectReference)(unsafe.Pointer(in.ClusterServicePlanRef)) + out.ClusterServiceClassRef = (*servicecatalog.ClusterObjectReference)(unsafe.Pointer(in.ClusterServiceClassRef)) + out.ClusterServicePlanRef = (*servicecatalog.ClusterObjectReference)(unsafe.Pointer(in.ClusterServicePlanRef)) out.Parameters = (*runtime.RawExtension)(unsafe.Pointer(in.Parameters)) out.ParametersFrom = *(*[]servicecatalog.ParametersFromSource)(unsafe.Pointer(&in.ParametersFrom)) out.ExternalID = in.ExternalID @@ -897,8 +970,8 @@ func autoConvert_servicecatalog_ServiceInstanceSpec_To_v1beta1_ServiceInstanceSp if err := Convert_servicecatalog_PlanReference_To_v1beta1_PlanReference(&in.PlanReference, &out.PlanReference, s); err != nil { return err } - out.ClusterServiceClassRef = (*v1.ObjectReference)(unsafe.Pointer(in.ClusterServiceClassRef)) - out.ClusterServicePlanRef = (*v1.ObjectReference)(unsafe.Pointer(in.ClusterServicePlanRef)) + out.ClusterServiceClassRef = (*ClusterObjectReference)(unsafe.Pointer(in.ClusterServiceClassRef)) + out.ClusterServicePlanRef = (*ClusterObjectReference)(unsafe.Pointer(in.ClusterServicePlanRef)) out.Parameters = (*runtime.RawExtension)(unsafe.Pointer(in.Parameters)) out.ParametersFrom = *(*[]ParametersFromSource)(unsafe.Pointer(&in.ParametersFrom)) out.ExternalID = in.ExternalID @@ -920,7 +993,7 @@ func autoConvert_v1beta1_ServiceInstanceStatus_To_servicecatalog_ServiceInstance out.DashboardURL = (*string)(unsafe.Pointer(in.DashboardURL)) out.CurrentOperation = servicecatalog.ServiceInstanceOperation(in.CurrentOperation) out.ReconciledGeneration = in.ReconciledGeneration - out.OperationStartTime = (*meta_v1.Time)(unsafe.Pointer(in.OperationStartTime)) + out.OperationStartTime = (*v1.Time)(unsafe.Pointer(in.OperationStartTime)) out.InProgressProperties = (*servicecatalog.ServiceInstancePropertiesState)(unsafe.Pointer(in.InProgressProperties)) out.ExternalProperties = (*servicecatalog.ServiceInstancePropertiesState)(unsafe.Pointer(in.ExternalProperties)) return nil @@ -939,7 +1012,7 @@ func autoConvert_servicecatalog_ServiceInstanceStatus_To_v1beta1_ServiceInstance out.DashboardURL = (*string)(unsafe.Pointer(in.DashboardURL)) out.CurrentOperation = ServiceInstanceOperation(in.CurrentOperation) out.ReconciledGeneration = in.ReconciledGeneration - out.OperationStartTime = (*meta_v1.Time)(unsafe.Pointer(in.OperationStartTime)) + out.OperationStartTime = (*v1.Time)(unsafe.Pointer(in.OperationStartTime)) out.InProgressProperties = (*ServiceInstancePropertiesState)(unsafe.Pointer(in.InProgressProperties)) out.ExternalProperties = (*ServiceInstancePropertiesState)(unsafe.Pointer(in.ExternalProperties)) return nil diff --git a/pkg/apis/servicecatalog/v1beta1/zz_generated.deepcopy.go b/pkg/apis/servicecatalog/v1beta1/zz_generated.deepcopy.go index ad73f1545743..73ad66140a70 100644 --- a/pkg/apis/servicecatalog/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/servicecatalog/v1beta1/zz_generated.deepcopy.go @@ -21,8 +21,7 @@ limitations under the License. package v1beta1 import ( - v1 "k8s.io/api/core/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" reflect "reflect" @@ -46,6 +45,10 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { in.(*BearerTokenAuthConfig).DeepCopyInto(out.(*BearerTokenAuthConfig)) return nil }, InType: reflect.TypeOf(&BearerTokenAuthConfig{})}, + conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { + in.(*ClusterObjectReference).DeepCopyInto(out.(*ClusterObjectReference)) + return nil + }, InType: reflect.TypeOf(&ClusterObjectReference{})}, conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { in.(*ClusterServiceBroker).DeepCopyInto(out.(*ClusterServiceBroker)) return nil @@ -94,6 +97,14 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { in.(*ClusterServicePlanStatus).DeepCopyInto(out.(*ClusterServicePlanStatus)) return nil }, InType: reflect.TypeOf(&ClusterServicePlanStatus{})}, + conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { + in.(*LocalObjectReference).DeepCopyInto(out.(*LocalObjectReference)) + return nil + }, InType: reflect.TypeOf(&LocalObjectReference{})}, + conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { + in.(*ObjectReference).DeepCopyInto(out.(*ObjectReference)) + return nil + }, InType: reflect.TypeOf(&ObjectReference{})}, conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { in.(*ParametersFromSource).DeepCopyInto(out.(*ParametersFromSource)) return nil @@ -177,7 +188,7 @@ func (in *BasicAuthConfig) DeepCopyInto(out *BasicAuthConfig) { if *in == nil { *out = nil } else { - *out = new(v1.ObjectReference) + *out = new(ObjectReference) **out = **in } } @@ -202,7 +213,7 @@ func (in *BearerTokenAuthConfig) DeepCopyInto(out *BearerTokenAuthConfig) { if *in == nil { *out = nil } else { - *out = new(v1.ObjectReference) + *out = new(ObjectReference) **out = **in } } @@ -219,6 +230,22 @@ func (in *BearerTokenAuthConfig) DeepCopy() *BearerTokenAuthConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterObjectReference) DeepCopyInto(out *ClusterObjectReference) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterObjectReference. +func (in *ClusterObjectReference) DeepCopy() *ClusterObjectReference { + if in == nil { + return nil + } + out := new(ClusterObjectReference) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterServiceBroker) DeepCopyInto(out *ClusterServiceBroker) { *out = *in @@ -304,7 +331,7 @@ func (in *ClusterServiceBrokerSpec) DeepCopyInto(out *ClusterServiceBrokerSpec) if *in == nil { *out = nil } else { - *out = new(meta_v1.Duration) + *out = new(v1.Duration) **out = **in } } @@ -336,7 +363,7 @@ func (in *ClusterServiceBrokerStatus) DeepCopyInto(out *ClusterServiceBrokerStat if *in == nil { *out = nil } else { - *out = new(meta_v1.Time) + *out = new(v1.Time) (*in).DeepCopyInto(*out) } } @@ -608,6 +635,38 @@ func (in *ClusterServicePlanStatus) DeepCopy() *ClusterServicePlanStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalObjectReference) DeepCopyInto(out *LocalObjectReference) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalObjectReference. +func (in *LocalObjectReference) DeepCopy() *LocalObjectReference { + if in == nil { + return nil + } + out := new(LocalObjectReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectReference) DeepCopyInto(out *ObjectReference) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectReference. +func (in *ObjectReference) DeepCopy() *ObjectReference { + if in == nil { + return nil + } + out := new(ObjectReference) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ParametersFromSource) DeepCopyInto(out *ParametersFromSource) { *out = *in @@ -836,7 +895,7 @@ func (in *ServiceBindingStatus) DeepCopyInto(out *ServiceBindingStatus) { if *in == nil { *out = nil } else { - *out = new(meta_v1.Time) + *out = new(v1.Time) (*in).DeepCopyInto(*out) } } @@ -892,15 +951,6 @@ func (in *ServiceBrokerAuthInfo) DeepCopyInto(out *ServiceBrokerAuthInfo) { (*in).DeepCopyInto(*out) } } - if in.BasicAuthSecret != nil { - in, out := &in.BasicAuthSecret, &out.BasicAuthSecret - if *in == nil { - *out = nil - } else { - *out = new(v1.ObjectReference) - **out = **in - } - } return } @@ -1054,7 +1104,7 @@ func (in *ServiceInstanceSpec) DeepCopyInto(out *ServiceInstanceSpec) { if *in == nil { *out = nil } else { - *out = new(v1.ObjectReference) + *out = new(ClusterObjectReference) **out = **in } } @@ -1063,7 +1113,7 @@ func (in *ServiceInstanceSpec) DeepCopyInto(out *ServiceInstanceSpec) { if *in == nil { *out = nil } else { - *out = new(v1.ObjectReference) + *out = new(ClusterObjectReference) **out = **in } } @@ -1138,7 +1188,7 @@ func (in *ServiceInstanceStatus) DeepCopyInto(out *ServiceInstanceStatus) { if *in == nil { *out = nil } else { - *out = new(meta_v1.Time) + *out = new(v1.Time) (*in).DeepCopyInto(*out) } } diff --git a/pkg/apis/servicecatalog/validation/binding_test.go b/pkg/apis/servicecatalog/validation/binding_test.go index 67c3555de994..10fc35347f39 100644 --- a/pkg/apis/servicecatalog/validation/binding_test.go +++ b/pkg/apis/servicecatalog/validation/binding_test.go @@ -19,7 +19,6 @@ package validation import ( "testing" - "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -33,7 +32,7 @@ func validServiceBinding() *servicecatalog.ServiceBinding { Namespace: "test-ns", }, Spec: servicecatalog.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{ + ServiceInstanceRef: servicecatalog.LocalObjectReference{ Name: "test-instance", }, SecretName: "test-secret", diff --git a/pkg/apis/servicecatalog/validation/broker.go b/pkg/apis/servicecatalog/validation/broker.go index aa5546fad9c4..d423e9f35b8d 100644 --- a/pkg/apis/servicecatalog/validation/broker.go +++ b/pkg/apis/servicecatalog/validation/broker.go @@ -82,16 +82,6 @@ func validateClusterServiceBrokerSpec(spec *sc.ClusterServiceBrokerSpec, fldPath field.Required(fldPath.Child("authInfo", "bearer", "secretRef"), "a basic auth secret is required"), ) } - } else if spec.AuthInfo.BasicAuthSecret != nil { - basicAuthSecret := spec.AuthInfo.BasicAuthSecret - if basicAuthSecret != nil { - for _, msg := range apivalidation.ValidateNamespaceName(basicAuthSecret.Namespace, false /* prefix */) { - allErrs = append(allErrs, field.Invalid(fldPath.Child("authInfo", "basicAuthSecret", "namespace"), basicAuthSecret.Namespace, msg)) - } - for _, msg := range apivalidation.NameIsDNSSubdomain(basicAuthSecret.Name, false /* prefix */) { - allErrs = append(allErrs, field.Invalid(fldPath.Child("authInfo", "basicAuthSecret", "name"), basicAuthSecret.Name, msg)) - } - } } else { // Authentication allErrs = append( @@ -143,7 +133,7 @@ func validateClusterServiceBrokerSpec(spec *sc.ClusterServiceBrokerSpec, fldPath } if spec.RelistDuration != nil { - zeroDuration := metav1.Duration{0} + zeroDuration := metav1.Duration{Duration: 0} if spec.RelistDuration.Duration <= zeroDuration.Duration { allErrs = append( allErrs, diff --git a/pkg/apis/servicecatalog/validation/broker_test.go b/pkg/apis/servicecatalog/validation/broker_test.go index 6655944e2d26..ccff544967af 100644 --- a/pkg/apis/servicecatalog/validation/broker_test.go +++ b/pkg/apis/servicecatalog/validation/broker_test.go @@ -20,7 +20,6 @@ import ( "testing" "time" - "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog" @@ -58,7 +57,7 @@ func TestValidateClusterServiceBroker(t *testing.T) { URL: "http://example.com", AuthInfo: &servicecatalog.ServiceBrokerAuthInfo{ Basic: &servicecatalog.BasicAuthConfig{ - SecretRef: &v1.ObjectReference{ + SecretRef: &servicecatalog.ObjectReference{ Namespace: "test-ns", Name: "test-secret", }, @@ -80,7 +79,7 @@ func TestValidateClusterServiceBroker(t *testing.T) { URL: "http://example.com", AuthInfo: &servicecatalog.ServiceBrokerAuthInfo{ Bearer: &servicecatalog.BearerTokenAuthConfig{ - SecretRef: &v1.ObjectReference{ + SecretRef: &servicecatalog.ObjectReference{ Namespace: "test-ns", Name: "test-secret", }, @@ -117,7 +116,7 @@ func TestValidateClusterServiceBroker(t *testing.T) { URL: "http://example.com", AuthInfo: &servicecatalog.ServiceBrokerAuthInfo{ Basic: &servicecatalog.BasicAuthConfig{ - SecretRef: &v1.ObjectReference{ + SecretRef: &servicecatalog.ObjectReference{ Name: "test-secret", }, }, @@ -138,7 +137,7 @@ func TestValidateClusterServiceBroker(t *testing.T) { URL: "http://example.com", AuthInfo: &servicecatalog.ServiceBrokerAuthInfo{ Basic: &servicecatalog.BasicAuthConfig{ - SecretRef: &v1.ObjectReference{ + SecretRef: &servicecatalog.ObjectReference{ Namespace: "test-ns", }, }, @@ -159,7 +158,7 @@ func TestValidateClusterServiceBroker(t *testing.T) { URL: "http://example.com", AuthInfo: &servicecatalog.ServiceBrokerAuthInfo{ Bearer: &servicecatalog.BearerTokenAuthConfig{ - SecretRef: &v1.ObjectReference{ + SecretRef: &servicecatalog.ObjectReference{ Name: "test-secret", }, }, @@ -180,7 +179,7 @@ func TestValidateClusterServiceBroker(t *testing.T) { URL: "http://example.com", AuthInfo: &servicecatalog.ServiceBrokerAuthInfo{ Bearer: &servicecatalog.BearerTokenAuthConfig{ - SecretRef: &v1.ObjectReference{ + SecretRef: &servicecatalog.ObjectReference{ Namespace: "test-ns", }, }, diff --git a/pkg/apis/servicecatalog/validation/instance.go b/pkg/apis/servicecatalog/validation/instance.go index 123bdd941919..f47e200278f6 100644 --- a/pkg/apis/servicecatalog/validation/instance.go +++ b/pkg/apis/servicecatalog/validation/instance.go @@ -213,7 +213,7 @@ func validateServiceInstanceUpdate(instance *sc.ServiceInstance) field.ErrorList } if instance.Status.CurrentOperation != "" { if instance.Spec.ClusterServiceClassRef == nil { - allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("clusterServceClassRef"), "serviceClassRef is required when currentOperation is present")) + allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("clusterServiceClassRef"), "serviceClassRef is required when currentOperation is present")) } if instance.Spec.ClusterServicePlanRef == nil { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("clusterServicePlanRef"), "servicePlanRef is required when currentOperation is present")) @@ -230,8 +230,8 @@ func internalValidateServiceInstanceUpdateAllowed(new *sc.ServiceInstance, old * if old.Generation != new.Generation && old.Status.ReconciledGeneration != old.Generation { errors = append(errors, field.Forbidden(field.NewPath("spec"), "Another update for this service instance is in progress")) } - if old.Spec.ExternalClusterServicePlanName != new.Spec.ExternalClusterServicePlanName && new.Spec.ClusterServicePlanRef != nil { - errors = append(errors, field.Forbidden(field.NewPath("spec").Child("clusterServicePlanRef"), "clusterServicePlanRef must not be present when externalServicePlanName is being changed")) + if old.Spec.ClusterServicePlanExternalName != new.Spec.ClusterServicePlanExternalName && new.Spec.ClusterServicePlanRef != nil { + errors = append(errors, field.Forbidden(field.NewPath("spec").Child("clusterServicePlanRef"), "clusterServicePlanRef must not be present when clusterServicePlanExternalName is being changed")) } return errors } @@ -246,7 +246,7 @@ func ValidateServiceInstanceUpdate(new *sc.ServiceInstance, old *sc.ServiceInsta allErrs = append(allErrs, internalValidateServiceInstanceUpdateAllowed(new, old)...) allErrs = append(allErrs, internalValidateServiceInstance(new, false)...) - allErrs = append(allErrs, apivalidation.ValidateImmutableField(new.Spec.ExternalClusterServiceClassName, old.Spec.ExternalClusterServiceClassName, specFieldPath.Child("externalClusterServiceClassName"))...) + allErrs = append(allErrs, apivalidation.ValidateImmutableField(new.Spec.ClusterServiceClassExternalName, old.Spec.ClusterServiceClassExternalName, specFieldPath.Child("clusterServiceClassExternalName"))...) allErrs = append(allErrs, apivalidation.ValidateImmutableField(new.Spec.ExternalID, old.Spec.ExternalID, specFieldPath.Child("externalID"))...) if new.Spec.UpdateRequests < old.Spec.UpdateRequests { @@ -305,34 +305,34 @@ func validatePlanReference(p *sc.PlanReference, fldPath *field.Path) field.Error allErrs := field.ErrorList{} // Just to make reading of the conditionals in the code easier. - externalClassSet := p.ExternalClusterServiceClassName != "" - externalPlanSet := p.ExternalClusterServicePlanName != "" + externalClassSet := p.ClusterServiceClassExternalName != "" + externalPlanSet := p.ClusterServicePlanExternalName != "" k8sClassSet := p.ClusterServiceClassName != "" k8sPlanSet := p.ClusterServicePlanName != "" // Can't specify both External and k8s name but must specify one. if externalClassSet == k8sClassSet { - allErrs = append(allErrs, field.Required(fldPath.Child("externalClusterServiceClassName"), "exactly one of externalClusterServiceClassName or clusterServiceClassName required")) - allErrs = append(allErrs, field.Required(fldPath.Child("clusterServiceClassName"), "exactly one of externalClusterServiceClassName or clusterServiceClassName required")) + allErrs = append(allErrs, field.Required(fldPath.Child("clusterServiceClassExternalName"), "exactly one of clusterServiceClassExternalName or clusterServiceClassName required")) + allErrs = append(allErrs, field.Required(fldPath.Child("clusterServiceClassName"), "exactly one of clusterServiceClassExternalName or clusterServiceClassName required")) } // Can't specify both External and k8s name but must specify one. if externalPlanSet == k8sPlanSet { - allErrs = append(allErrs, field.Required(fldPath.Child("externalClusterServicePlanName"), "exactly one of externalClusterServicePlanName or clusterServicePlanName required")) - allErrs = append(allErrs, field.Required(fldPath.Child("clusterServicePlanName"), "exactly one of externalClusterServicePlanName or clusterServicePlanName required")) + allErrs = append(allErrs, field.Required(fldPath.Child("clusterServicePlanExternalName"), "exactly one of clusterServicePlanExternalName or clusterServicePlanName required")) + allErrs = append(allErrs, field.Required(fldPath.Child("clusterServicePlanName"), "exactly one of clusterServicePlanExternalName or clusterServicePlanName required")) } if externalClassSet { - for _, msg := range validateServiceClassName(p.ExternalClusterServiceClassName, false /* prefix */) { - allErrs = append(allErrs, field.Invalid(fldPath.Child("externalClusterServiceClassName"), p.ExternalClusterServiceClassName, msg)) + for _, msg := range validateServiceClassName(p.ClusterServiceClassExternalName, false /* prefix */) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("clusterServiceClassExternalName"), p.ClusterServiceClassExternalName, msg)) } - // If ExternalClusterServiceClassName given, must use ExternalClusterServicePlanName + // If ClusterServiceClassExternalName given, must use ClusterServicePlanExternalName if !externalPlanSet { - allErrs = append(allErrs, field.Required(fldPath.Child("externalClusterServicePlanName"), "must specify externalClusterServicePlanName with externalClusterServiceClassName")) + allErrs = append(allErrs, field.Required(fldPath.Child("clusterServicePlanExternalName"), "must specify clusterServicePlanExternalName with clusterServiceClassExternalName")) } - for _, msg := range validateServicePlanName(p.ExternalClusterServicePlanName, false /* prefix */) { - allErrs = append(allErrs, field.Invalid(fldPath.Child("externalClusterServicePlanName"), p.ClusterServicePlanName, msg)) + for _, msg := range validateServicePlanName(p.ClusterServicePlanExternalName, false /* prefix */) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("clusterServicePlanExternalName"), p.ClusterServicePlanName, msg)) } } if k8sClassSet { @@ -355,7 +355,7 @@ func validatePlanReferenceUpdate(pOld *sc.PlanReference, pNew *sc.PlanReference, allErrs := field.ErrorList{} allErrs = append(allErrs, validatePlanReference(pOld, fldPath)...) allErrs = append(allErrs, validatePlanReference(pNew, fldPath)...) - allErrs = append(allErrs, apivalidation.ValidateImmutableField(pNew.ExternalClusterServiceClassName, pOld.ExternalClusterServiceClassName, field.NewPath("spec").Child("externalClusterServiceClassName"))...) + allErrs = append(allErrs, apivalidation.ValidateImmutableField(pNew.ClusterServiceClassExternalName, pOld.ClusterServiceClassExternalName, field.NewPath("spec").Child("clusterServiceClassExternalName"))...) allErrs = append(allErrs, apivalidation.ValidateImmutableField(pNew.ClusterServiceClassName, pOld.ClusterServiceClassName, field.NewPath("spec").Child("clusterServiceClassName"))...) return allErrs } diff --git a/pkg/apis/servicecatalog/validation/instance_test.go b/pkg/apis/servicecatalog/validation/instance_test.go index 72ea686a87c8..b7652d744382 100644 --- a/pkg/apis/servicecatalog/validation/instance_test.go +++ b/pkg/apis/servicecatalog/validation/instance_test.go @@ -20,7 +20,6 @@ import ( "strings" "testing" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" @@ -29,16 +28,16 @@ import ( ) const ( - externalClusterServiceClassName = "test-serviceclass" - externalClusterServicePlanName = "test-plan" + clusterServiceClassExternalName = "test-serviceclass" + clusterServicePlanExternalName = "test-plan" clusterServiceClassName = "test-k8s-serviceclass" clusterServicePlanName = "test-k8s-plan-name" ) func validPlanReferenceExternal() servicecatalog.PlanReference { return servicecatalog.PlanReference{ - ExternalClusterServiceClassName: externalClusterServiceClassName, - ExternalClusterServicePlanName: externalClusterServicePlanName, + ClusterServiceClassExternalName: clusterServiceClassExternalName, + ClusterServicePlanExternalName: clusterServicePlanExternalName, } } @@ -64,8 +63,8 @@ func validServiceInstanceForCreate() *servicecatalog.ServiceInstance { func validServiceInstance() *servicecatalog.ServiceInstance { instance := validServiceInstanceForCreate() - instance.Spec.ClusterServiceClassRef = &corev1.ObjectReference{} - instance.Spec.ClusterServicePlanRef = &corev1.ObjectReference{} + instance.Spec.ClusterServiceClassRef = &servicecatalog.ClusterObjectReference{} + instance.Spec.ClusterServicePlanRef = &servicecatalog.ClusterObjectReference{} return instance } @@ -109,10 +108,10 @@ func TestValidateServiceInstance(t *testing.T) { valid: false, }, { - name: "missing externalClusterServiceClassName and clusterServiceClassName", + name: "missing clusterServiceClassExternalName and clusterServiceClassName", instance: func() *servicecatalog.ServiceInstance { i := validServiceInstance() - i.Spec.ExternalClusterServiceClassName = "" + i.Spec.ClusterServiceClassExternalName = "" return i }(), valid: false, @@ -121,7 +120,7 @@ func TestValidateServiceInstance(t *testing.T) { name: "invalid serviceClassName", instance: func() *servicecatalog.ServiceInstance { i := validServiceInstance() - i.Spec.ExternalClusterServiceClassName = "oing20&)*^&" + i.Spec.ClusterServiceClassExternalName = "oing20&)*^&" return i }(), valid: false, @@ -130,7 +129,7 @@ func TestValidateServiceInstance(t *testing.T) { name: "missing planName", instance: func() *servicecatalog.ServiceInstance { i := validServiceInstance() - i.Spec.ExternalClusterServicePlanName = "" + i.Spec.ClusterServicePlanExternalName = "" return i }(), valid: false, @@ -139,7 +138,7 @@ func TestValidateServiceInstance(t *testing.T) { name: "invalid planName", instance: func() *servicecatalog.ServiceInstance { i := validServiceInstance() - i.Spec.ExternalClusterServicePlanName = "9651.JVHbebe" + i.Spec.ClusterServicePlanExternalName = "9651.JVHbebe" return i }(), valid: false, @@ -424,8 +423,8 @@ func TestValidateServiceInstance(t *testing.T) { name: "valid create with k8s name", instance: func() *servicecatalog.ServiceInstance { i := validServiceInstanceForCreate() - i.Spec.ExternalClusterServiceClassName = "" - i.Spec.ExternalClusterServicePlanName = "" + i.Spec.ClusterServiceClassExternalName = "" + i.Spec.ClusterServicePlanExternalName = "" i.Spec.ClusterServiceClassName = clusterServiceClassName i.Spec.ClusterServicePlanName = clusterServicePlanName return i @@ -586,8 +585,8 @@ func TestInternalValidateServiceInstanceUpdateAllowed(t *testing.T) { }, Spec: servicecatalog.ServiceInstanceSpec{ PlanReference: servicecatalog.PlanReference{ - ExternalClusterServiceClassName: externalClusterServiceClassName, - ExternalClusterServicePlanName: externalClusterServicePlanName, + ClusterServiceClassExternalName: clusterServiceClassExternalName, + ClusterServicePlanExternalName: clusterServicePlanExternalName, }, }, } @@ -605,8 +604,8 @@ func TestInternalValidateServiceInstanceUpdateAllowed(t *testing.T) { }, Spec: servicecatalog.ServiceInstanceSpec{ PlanReference: servicecatalog.PlanReference{ - ExternalClusterServiceClassName: "test-serviceclass", - ExternalClusterServicePlanName: "test-plan", + ClusterServiceClassExternalName: "test-serviceclass", + ClusterServicePlanExternalName: "test-plan", }, }, } @@ -632,7 +631,7 @@ func TestInternalValidateServiceInstanceUpdateAllowedForPlanChange(t *testing.T) name string oldPlan string newPlan string - newPlanRef *corev1.ObjectReference + newPlanRef *servicecatalog.ClusterObjectReference valid bool }{ { @@ -646,14 +645,14 @@ func TestInternalValidateServiceInstanceUpdateAllowedForPlanChange(t *testing.T) name: "plan ref not cleared", oldPlan: "old-plan", newPlan: "new-plan", - newPlanRef: &corev1.ObjectReference{}, + newPlanRef: &servicecatalog.ClusterObjectReference{}, valid: false, }, { name: "no plan change", oldPlan: "plan-name", newPlan: "plan-name", - newPlanRef: &corev1.ObjectReference{}, + newPlanRef: &servicecatalog.ClusterObjectReference{}, valid: true, }, } @@ -666,11 +665,11 @@ func TestInternalValidateServiceInstanceUpdateAllowedForPlanChange(t *testing.T) }, Spec: servicecatalog.ServiceInstanceSpec{ PlanReference: servicecatalog.PlanReference{ - ExternalClusterServiceClassName: "test-serviceclass", - ExternalClusterServicePlanName: tc.oldPlan, + ClusterServiceClassExternalName: "test-serviceclass", + ClusterServicePlanExternalName: tc.oldPlan, }, - ClusterServiceClassRef: &corev1.ObjectReference{}, - ClusterServicePlanRef: &corev1.ObjectReference{}, + ClusterServiceClassRef: &servicecatalog.ClusterObjectReference{}, + ClusterServicePlanRef: &servicecatalog.ClusterObjectReference{}, }, } @@ -681,10 +680,10 @@ func TestInternalValidateServiceInstanceUpdateAllowedForPlanChange(t *testing.T) }, Spec: servicecatalog.ServiceInstanceSpec{ PlanReference: servicecatalog.PlanReference{ - ExternalClusterServiceClassName: externalClusterServiceClassName, - ExternalClusterServicePlanName: tc.newPlan, + ClusterServiceClassExternalName: clusterServiceClassExternalName, + ClusterServicePlanExternalName: tc.newPlan, }, - ClusterServiceClassRef: &corev1.ObjectReference{}, + ClusterServiceClassRef: &servicecatalog.ClusterObjectReference{}, ClusterServicePlanRef: tc.newPlanRef, }, } @@ -834,11 +833,11 @@ func TestValidateServiceInstanceStatusUpdate(t *testing.T) { }, Spec: servicecatalog.ServiceInstanceSpec{ PlanReference: servicecatalog.PlanReference{ - ExternalClusterServiceClassName: externalClusterServiceClassName, - ExternalClusterServicePlanName: externalClusterServicePlanName, + ClusterServiceClassExternalName: clusterServiceClassExternalName, + ClusterServicePlanExternalName: clusterServicePlanExternalName, }, - ClusterServiceClassRef: &corev1.ObjectReference{}, - ClusterServicePlanRef: &corev1.ObjectReference{}, + ClusterServiceClassRef: &servicecatalog.ClusterObjectReference{}, + ClusterServicePlanRef: &servicecatalog.ClusterObjectReference{}, }, Status: *tc.old, } @@ -851,11 +850,11 @@ func TestValidateServiceInstanceStatusUpdate(t *testing.T) { }, Spec: servicecatalog.ServiceInstanceSpec{ PlanReference: servicecatalog.PlanReference{ - ExternalClusterServiceClassName: externalClusterServiceClassName, - ExternalClusterServicePlanName: externalClusterServicePlanName, + ClusterServiceClassExternalName: clusterServiceClassExternalName, + ClusterServicePlanExternalName: clusterServicePlanExternalName, }, - ClusterServiceClassRef: &corev1.ObjectReference{}, - ClusterServicePlanRef: &corev1.ObjectReference{}, + ClusterServiceClassRef: &servicecatalog.ClusterObjectReference{}, + ClusterServicePlanRef: &servicecatalog.ClusterObjectReference{}, }, Status: *tc.new, } @@ -901,7 +900,7 @@ func TestValidateServiceInstanceReferencesUpdate(t *testing.T) { old: validServiceInstance(), new: func() *servicecatalog.ServiceInstance { i := validServiceInstance() - i.Spec.ClusterServiceClassRef = &corev1.ObjectReference{ + i.Spec.ClusterServiceClassRef = &servicecatalog.ClusterObjectReference{ Name: "new-class-name", } return i @@ -913,7 +912,7 @@ func TestValidateServiceInstanceReferencesUpdate(t *testing.T) { old: validServiceInstance(), new: func() *servicecatalog.ServiceInstance { i := validServiceInstance() - i.Spec.ClusterServicePlanRef = &corev1.ObjectReference{ + i.Spec.ClusterServicePlanRef = &servicecatalog.ClusterObjectReference{ Name: "new-plan-name", } return i @@ -929,7 +928,7 @@ func TestValidateServiceInstanceReferencesUpdate(t *testing.T) { }(), new: func() *servicecatalog.ServiceInstance { i := validServiceInstance() - i.Spec.ClusterServicePlanRef = &corev1.ObjectReference{ + i.Spec.ClusterServicePlanRef = &servicecatalog.ClusterObjectReference{ Name: "new-plan-name", } return i @@ -966,7 +965,7 @@ func TestValidatePlanReference(t *testing.T) { name: "invalid -- empty struct", ref: servicecatalog.PlanReference{}, valid: false, - expectedError: "exactly one of externalClusterServicePlanName", + expectedError: "exactly one of clusterServicePlanExternalName", }, { name: "valid -- external", @@ -976,11 +975,11 @@ func TestValidatePlanReference(t *testing.T) { { name: "invalid -- external name, k8s plan", ref: servicecatalog.PlanReference{ - ExternalClusterServiceClassName: externalClusterServiceClassName, - ClusterServicePlanName: externalClusterServicePlanName, + ClusterServiceClassExternalName: clusterServiceClassExternalName, + ClusterServicePlanName: clusterServicePlanExternalName, }, valid: false, - expectedError: "must specify externalClusterServicePlanName", + expectedError: "must specify clusterServicePlanExternalName", }, { name: "valid -- k8s", @@ -991,7 +990,7 @@ func TestValidatePlanReference(t *testing.T) { name: "invalid -- valid k8s name, external plan", ref: servicecatalog.PlanReference{ ClusterServiceClassName: clusterServiceClassName, - ExternalClusterServicePlanName: externalClusterServicePlanName, + ClusterServicePlanExternalName: clusterServicePlanExternalName, }, valid: false, expectedError: "must specify clusterServicePlanName", @@ -1000,21 +999,21 @@ func TestValidatePlanReference(t *testing.T) { name: "invalid -- external and k8s name specified", ref: servicecatalog.PlanReference{ ClusterServiceClassName: clusterServiceClassName, - ExternalClusterServiceClassName: externalClusterServiceClassName, - ExternalClusterServicePlanName: externalClusterServicePlanName, + ClusterServiceClassExternalName: clusterServiceClassExternalName, + ClusterServicePlanExternalName: clusterServicePlanExternalName, }, valid: false, - expectedError: "exactly one of externalClusterServiceClassName", + expectedError: "exactly one of clusterServiceClassExternalName", }, { name: "invalid -- external and k8s plan specified", ref: servicecatalog.PlanReference{ - ExternalClusterServiceClassName: externalClusterServiceClassName, - ExternalClusterServicePlanName: externalClusterServicePlanName, + ClusterServiceClassExternalName: clusterServiceClassExternalName, + ClusterServicePlanExternalName: clusterServicePlanExternalName, ClusterServicePlanName: clusterServicePlanName, }, valid: false, - expectedError: "exactly one of externalClusterServicePlanName", + expectedError: "exactly one of clusterServicePlanExternalName", }, } for _, tc := range cases { @@ -1064,18 +1063,18 @@ func TestValidatePlanReferenceUpdate(t *testing.T) { name: "invalid -- changing external class name", old: validPlanReferenceExternal(), new: servicecatalog.PlanReference{ - ExternalClusterServiceClassName: clusterServiceClassName, - ExternalClusterServicePlanName: externalClusterServicePlanName, + ClusterServiceClassExternalName: clusterServiceClassName, + ClusterServicePlanExternalName: clusterServicePlanExternalName, }, valid: false, - expectedError: "externalClusterServiceClassName", + expectedError: "clusterServiceClassExternalName", }, { name: "valid -- changing external plan name", old: validPlanReferenceExternal(), new: servicecatalog.PlanReference{ - ExternalClusterServiceClassName: externalClusterServiceClassName, - ExternalClusterServicePlanName: clusterServicePlanName, + ClusterServiceClassExternalName: clusterServiceClassExternalName, + ClusterServicePlanExternalName: clusterServicePlanName, }, valid: true, }, @@ -1083,7 +1082,7 @@ func TestValidatePlanReferenceUpdate(t *testing.T) { name: "invalid -- changing k8s class name", old: validPlanReferenceK8S(), new: servicecatalog.PlanReference{ - ClusterServiceClassName: externalClusterServiceClassName, + ClusterServiceClassName: clusterServiceClassExternalName, ClusterServicePlanName: clusterServicePlanName, }, valid: false, @@ -1094,7 +1093,7 @@ func TestValidatePlanReferenceUpdate(t *testing.T) { old: validPlanReferenceK8S(), new: servicecatalog.PlanReference{ ClusterServiceClassName: clusterServiceClassName, - ClusterServicePlanName: externalClusterServicePlanName, + ClusterServicePlanName: clusterServicePlanExternalName, }, valid: true, }, diff --git a/pkg/apis/servicecatalog/validation/serviceplan_test.go b/pkg/apis/servicecatalog/validation/serviceplan_test.go index 27ef00c2d9db..33fd5562676a 100644 --- a/pkg/apis/servicecatalog/validation/serviceplan_test.go +++ b/pkg/apis/servicecatalog/validation/serviceplan_test.go @@ -19,7 +19,6 @@ package validation import ( "testing" - "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog" @@ -35,7 +34,7 @@ func validClusterServicePlan() *servicecatalog.ClusterServicePlan { ExternalName: "test-plan", ExternalID: "40d-0983-1b89", Description: "plan description", - ClusterServiceClassRef: v1.LocalObjectReference{ + ClusterServiceClassRef: servicecatalog.ClusterObjectReference{ Name: "test-service-class", }, }, diff --git a/pkg/apis/servicecatalog/zz_generated.deepcopy.go b/pkg/apis/servicecatalog/zz_generated.deepcopy.go index 5ace4742a27e..167eee197312 100644 --- a/pkg/apis/servicecatalog/zz_generated.deepcopy.go +++ b/pkg/apis/servicecatalog/zz_generated.deepcopy.go @@ -21,8 +21,7 @@ limitations under the License. package servicecatalog import ( - v1 "k8s.io/api/core/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" reflect "reflect" @@ -46,6 +45,10 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { in.(*BearerTokenAuthConfig).DeepCopyInto(out.(*BearerTokenAuthConfig)) return nil }, InType: reflect.TypeOf(&BearerTokenAuthConfig{})}, + conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { + in.(*ClusterObjectReference).DeepCopyInto(out.(*ClusterObjectReference)) + return nil + }, InType: reflect.TypeOf(&ClusterObjectReference{})}, conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { in.(*ClusterServiceBroker).DeepCopyInto(out.(*ClusterServiceBroker)) return nil @@ -94,6 +97,14 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { in.(*ClusterServicePlanStatus).DeepCopyInto(out.(*ClusterServicePlanStatus)) return nil }, InType: reflect.TypeOf(&ClusterServicePlanStatus{})}, + conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { + in.(*LocalObjectReference).DeepCopyInto(out.(*LocalObjectReference)) + return nil + }, InType: reflect.TypeOf(&LocalObjectReference{})}, + conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { + in.(*ObjectReference).DeepCopyInto(out.(*ObjectReference)) + return nil + }, InType: reflect.TypeOf(&ObjectReference{})}, conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { in.(*ParametersFromSource).DeepCopyInto(out.(*ParametersFromSource)) return nil @@ -177,7 +188,7 @@ func (in *BasicAuthConfig) DeepCopyInto(out *BasicAuthConfig) { if *in == nil { *out = nil } else { - *out = new(v1.ObjectReference) + *out = new(ObjectReference) **out = **in } } @@ -202,7 +213,7 @@ func (in *BearerTokenAuthConfig) DeepCopyInto(out *BearerTokenAuthConfig) { if *in == nil { *out = nil } else { - *out = new(v1.ObjectReference) + *out = new(ObjectReference) **out = **in } } @@ -219,6 +230,22 @@ func (in *BearerTokenAuthConfig) DeepCopy() *BearerTokenAuthConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterObjectReference) DeepCopyInto(out *ClusterObjectReference) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterObjectReference. +func (in *ClusterObjectReference) DeepCopy() *ClusterObjectReference { + if in == nil { + return nil + } + out := new(ClusterObjectReference) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterServiceBroker) DeepCopyInto(out *ClusterServiceBroker) { *out = *in @@ -304,7 +331,7 @@ func (in *ClusterServiceBrokerSpec) DeepCopyInto(out *ClusterServiceBrokerSpec) if *in == nil { *out = nil } else { - *out = new(meta_v1.Duration) + *out = new(v1.Duration) **out = **in } } @@ -336,7 +363,7 @@ func (in *ClusterServiceBrokerStatus) DeepCopyInto(out *ClusterServiceBrokerStat if *in == nil { *out = nil } else { - *out = new(meta_v1.Time) + *out = new(v1.Time) (*in).DeepCopyInto(*out) } } @@ -608,6 +635,38 @@ func (in *ClusterServicePlanStatus) DeepCopy() *ClusterServicePlanStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalObjectReference) DeepCopyInto(out *LocalObjectReference) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalObjectReference. +func (in *LocalObjectReference) DeepCopy() *LocalObjectReference { + if in == nil { + return nil + } + out := new(LocalObjectReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectReference) DeepCopyInto(out *ObjectReference) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectReference. +func (in *ObjectReference) DeepCopy() *ObjectReference { + if in == nil { + return nil + } + out := new(ObjectReference) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ParametersFromSource) DeepCopyInto(out *ParametersFromSource) { *out = *in @@ -836,7 +895,7 @@ func (in *ServiceBindingStatus) DeepCopyInto(out *ServiceBindingStatus) { if *in == nil { *out = nil } else { - *out = new(meta_v1.Time) + *out = new(v1.Time) (*in).DeepCopyInto(*out) } } @@ -892,15 +951,6 @@ func (in *ServiceBrokerAuthInfo) DeepCopyInto(out *ServiceBrokerAuthInfo) { (*in).DeepCopyInto(*out) } } - if in.BasicAuthSecret != nil { - in, out := &in.BasicAuthSecret, &out.BasicAuthSecret - if *in == nil { - *out = nil - } else { - *out = new(v1.ObjectReference) - **out = **in - } - } return } @@ -1054,7 +1104,7 @@ func (in *ServiceInstanceSpec) DeepCopyInto(out *ServiceInstanceSpec) { if *in == nil { *out = nil } else { - *out = new(v1.ObjectReference) + *out = new(ClusterObjectReference) **out = **in } } @@ -1063,7 +1113,7 @@ func (in *ServiceInstanceSpec) DeepCopyInto(out *ServiceInstanceSpec) { if *in == nil { *out = nil } else { - *out = new(v1.ObjectReference) + *out = new(ClusterObjectReference) **out = **in } } @@ -1138,7 +1188,7 @@ func (in *ServiceInstanceStatus) DeepCopyInto(out *ServiceInstanceStatus) { if *in == nil { *out = nil } else { - *out = new(meta_v1.Time) + *out = new(v1.Time) (*in).DeepCopyInto(*out) } } diff --git a/pkg/apiserver/etcd_config.go b/pkg/apiserver/etcd_config.go index 61a4cbeebd89..565c4b8659cf 100644 --- a/pkg/apiserver/etcd_config.go +++ b/pkg/apiserver/etcd_config.go @@ -88,6 +88,8 @@ func (c completedEtcdConfig) NewServer() (*ServiceCatalogAPIServer, error) { roFactory := etcdRESTOptionsFactory{ deleteCollectionWorkers: c.extraConfig.deleteCollectionWorkers, + // TODO https://github.com/kubernetes/kubernetes/issues/44507 + // we still need to enable it so finalizers are respected. enableGarbageCollection: true, storageFactory: c.extraConfig.storageFactory, storageDecorator: generic.UndecoratedStorage, diff --git a/pkg/apiserver/etcd_rest_options_factory.go b/pkg/apiserver/etcd_rest_options_factory.go index b6decc309df2..1fe5fc1536ab 100644 --- a/pkg/apiserver/etcd_rest_options_factory.go +++ b/pkg/apiserver/etcd_rest_options_factory.go @@ -45,6 +45,6 @@ func (f etcdRESTOptionsFactory) GetRESTOptions(resource schema.GroupResource) (g Decorator: f.storageDecorator, DeleteCollectionWorkers: f.deleteCollectionWorkers, EnableGarbageCollection: f.enableGarbageCollection, - ResourcePrefix: f.storageFactory.ResourcePrefix(resource), + ResourcePrefix: resource.Group + "/" + resource.Resource, }, nil } diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 688a2b677b30..741cf0fc76ba 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -27,7 +27,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" runtimeutil "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" @@ -41,6 +40,7 @@ import ( servicecatalogclientset "github.com/kubernetes-incubator/service-catalog/pkg/client/clientset_generated/clientset/typed/servicecatalog/v1beta1" informers "github.com/kubernetes-incubator/service-catalog/pkg/client/informers_generated/externalversions/servicecatalog/v1beta1" listers "github.com/kubernetes-incubator/service-catalog/pkg/client/listers_generated/servicecatalog/v1beta1" + pretty "github.com/kubernetes-incubator/service-catalog/pkg/pretty" ) const ( @@ -90,40 +90,40 @@ func NewController( pollingQueue: workqueue.NewNamedRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(pollingStartInterval, pollingMaxBackoffDuration), "poller"), } + controller.brokerLister = brokerInformer.Lister() brokerInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: controller.brokerAdd, UpdateFunc: controller.brokerUpdate, DeleteFunc: controller.brokerDelete, }) - controller.brokerLister = brokerInformer.Lister() + controller.serviceClassLister = clusterServiceClassInformer.Lister() clusterServiceClassInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: controller.serviceClassAdd, UpdateFunc: controller.serviceClassUpdate, DeleteFunc: controller.serviceClassDelete, }) - controller.serviceClassLister = clusterServiceClassInformer.Lister() + controller.servicePlanLister = clusterServicePlanInformer.Lister() clusterServicePlanInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: controller.servicePlanAdd, UpdateFunc: controller.servicePlanUpdate, DeleteFunc: controller.servicePlanDelete, }) - controller.servicePlanLister = clusterServicePlanInformer.Lister() + controller.instanceLister = instanceInformer.Lister() instanceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: controller.instanceAdd, UpdateFunc: controller.instanceUpdate, DeleteFunc: controller.instanceDelete, }) - controller.instanceLister = instanceInformer.Lister() + controller.bindingLister = bindingInformer.Lister() bindingInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: controller.bindingAdd, UpdateFunc: controller.bindingUpdate, DeleteFunc: controller.bindingDelete, }) - controller.bindingLister = bindingInformer.Lister() return controller, nil } @@ -247,16 +247,14 @@ func worker(queue workqueue.RateLimitingInterface, resourceType string, maxRetri // a brokerClient to use for that method given an ServiceInstance. // Sets ClusterServiceClassRef and/or ClusterServicePlanRef if they haven't been already set. func (c *controller) getClusterServiceClassPlanAndClusterServiceBroker(instance *v1beta1.ServiceInstance) (*v1beta1.ClusterServiceClass, *v1beta1.ClusterServicePlan, string, osb.Client, error) { + pcb := pretty.NewContextBuilder(pretty.ServiceInstance, instance.Namespace, instance.Name) serviceClass, err := c.serviceClassLister.Get(instance.Spec.ClusterServiceClassRef.Name) if err != nil { s := fmt.Sprintf( "References a non-existent ClusterServiceClass (K8S: %q ExternalName: %q)", - instance.Spec.ClusterServiceClassRef.Name, instance.Spec.ExternalClusterServiceClassName, - ) - glog.Infof( - `ServiceInstance "%v/%v": %s`, - instance.Namespace, instance.Name, s, + instance.Spec.ClusterServiceClassRef.Name, instance.Spec.ClusterServiceClassExternalName, ) + glog.Info(pcb.Message(s)) c.updateServiceInstanceCondition( instance, v1beta1.ServiceInstanceConditionReady, @@ -272,12 +270,9 @@ func (c *controller) getClusterServiceClassPlanAndClusterServiceBroker(instance if nil != err { s := fmt.Sprintf( "References a non-existent ClusterServicePlan (K8S: %q ExternalName: %q) on ClusterServiceClass (K8S: %q ExternalName: %q)", - instance.Spec.ClusterServicePlanName, instance.Spec.ExternalClusterServicePlanName, serviceClass.Name, serviceClass.Spec.ExternalName, - ) - glog.Warningf( - `ServiceInstance "%s/%s": %s`, - instance.Namespace, instance.Name, s, + instance.Spec.ClusterServicePlanName, instance.Spec.ClusterServicePlanExternalName, serviceClass.Name, serviceClass.Spec.ExternalName, ) + glog.Warning(pcb.Message(s)) c.updateServiceInstanceCondition( instance, v1beta1.ServiceInstanceConditionReady, @@ -292,10 +287,7 @@ func (c *controller) getClusterServiceClassPlanAndClusterServiceBroker(instance broker, err := c.brokerLister.Get(serviceClass.Spec.ClusterServiceBrokerName) if err != nil { s := fmt.Sprintf("References a non-existent broker %q", serviceClass.Spec.ClusterServiceBrokerName) - glog.Warningf( - `ServiceInstance "%s/%s": %s`, - instance.Namespace, instance.Name, s, - ) + glog.Warning(pcb.Message(s)) c.updateServiceInstanceCondition( instance, v1beta1.ServiceInstanceConditionReady, @@ -310,10 +302,7 @@ func (c *controller) getClusterServiceClassPlanAndClusterServiceBroker(instance authConfig, err := getAuthCredentialsFromClusterServiceBroker(c.kubeClient, broker) if err != nil { s := fmt.Sprintf("Error getting broker auth credentials for broker %q: %s", broker.Name, err) - glog.Infof( - `ServiceInstance "%v/%v": %s`, - instance.Namespace, instance.Name, s, - ) + glog.Info(pcb.Message(s)) c.updateServiceInstanceCondition( instance, v1beta1.ServiceInstanceConditionReady, @@ -327,7 +316,8 @@ func (c *controller) getClusterServiceClassPlanAndClusterServiceBroker(instance clientConfig := NewClientConfigurationForBroker(broker, authConfig) - glog.V(4).Infof("Creating client for ClusterServiceBroker %v, URL: %v", broker.Name, broker.Spec.URL) + s := fmt.Sprintf("Creating client for ClusterServiceBroker %v, URL: %v", broker.Name, broker.Spec.URL) + glog.V(4).Info(pcb.Message(s)) brokerClient, err := c.brokerClientCreateFunc(clientConfig) if err != nil { return nil, nil, "", nil, err @@ -341,16 +331,14 @@ func (c *controller) getClusterServiceClassPlanAndClusterServiceBroker(instance // a brokerclient to use for a given ServiceInstance. // Sets ClusterServiceClassRef and/or ClusterServicePlanRef if they haven't been already set. func (c *controller) getClusterServiceClassPlanAndClusterServiceBrokerForServiceBinding(instance *v1beta1.ServiceInstance, binding *v1beta1.ServiceBinding) (*v1beta1.ClusterServiceClass, *v1beta1.ClusterServicePlan, string, osb.Client, error) { + pcb := pretty.NewContextBuilder(pretty.ServiceInstance, instance.Namespace, instance.Name) serviceClass, err := c.serviceClassLister.Get(instance.Spec.ClusterServiceClassRef.Name) if err != nil { s := fmt.Sprintf( "References a non-existent ClusterServiceClass (K8S: %q ExternalName: %q)", - instance.Spec.ClusterServiceClassRef.Name, instance.Spec.ExternalClusterServiceClassName, - ) - glog.Warningf( - `ServiceBinding "%s/%s": %s`, - instance.Namespace, instance.Name, s, + instance.Spec.ClusterServiceClassRef.Name, instance.Spec.ClusterServiceClassExternalName, ) + glog.Warning(pcb.Message(s)) c.updateServiceBindingCondition( binding, v1beta1.ServiceBindingConditionReady, @@ -366,12 +354,9 @@ func (c *controller) getClusterServiceClassPlanAndClusterServiceBrokerForService if nil != err { s := fmt.Sprintf( "References a non-existent ClusterServicePlan (K8S: %q ExternalName: %q) on ClusterServiceClass (K8S: %q ExternalName: %q)", - instance.Spec.ClusterServicePlanName, instance.Spec.ExternalClusterServicePlanName, serviceClass.Name, serviceClass.Spec.ExternalName, - ) - glog.Warningf( - `ServiceBinding "%s/%s": %s`, - instance.Namespace, instance.Name, s, + instance.Spec.ClusterServicePlanName, instance.Spec.ClusterServicePlanExternalName, serviceClass.Name, serviceClass.Spec.ExternalName, ) + glog.Warning(pcb.Message(s)) c.updateServiceBindingCondition( binding, v1beta1.ServiceBindingConditionReady, @@ -386,10 +371,7 @@ func (c *controller) getClusterServiceClassPlanAndClusterServiceBrokerForService broker, err := c.brokerLister.Get(serviceClass.Spec.ClusterServiceBrokerName) if err != nil { s := fmt.Sprintf("References a non-existent ClusterServiceBroker %q", serviceClass.Spec.ClusterServiceBrokerName) - glog.Warningf( - `ServiceBinding "%s/%s": %s`, - instance.Namespace, instance.Name, s, - ) + glog.Warning(pcb.Message(s)) c.updateServiceBindingCondition( binding, v1beta1.ServiceBindingConditionReady, @@ -404,10 +386,7 @@ func (c *controller) getClusterServiceClassPlanAndClusterServiceBrokerForService authConfig, err := getAuthCredentialsFromClusterServiceBroker(c.kubeClient, broker) if err != nil { s := fmt.Sprintf("Error getting broker auth credentials for broker %q: %s", broker.Name, err) - glog.Warningf( - `ServiceBinding "%s/%s": %s`, - instance.Namespace, instance.Name, s, - ) + glog.Warning(pcb.Message(s)) c.updateServiceBindingCondition( binding, v1beta1.ServiceBindingConditionReady, @@ -442,7 +421,7 @@ func getAuthCredentialsFromClusterServiceBroker(client kubernetes.Interface, bro authInfo := broker.Spec.AuthInfo if authInfo.Basic != nil { secretRef := authInfo.Basic.SecretRef - secret, err := client.Core().Secrets(secretRef.Namespace).Get(secretRef.Name, metav1.GetOptions{}) + secret, err := client.CoreV1().Secrets(secretRef.Namespace).Get(secretRef.Name, metav1.GetOptions{}) if err != nil { return nil, err } @@ -455,7 +434,7 @@ func getAuthCredentialsFromClusterServiceBroker(client kubernetes.Interface, bro }, nil } else if authInfo.Bearer != nil { secretRef := authInfo.Bearer.SecretRef - secret, err := client.Core().Secrets(secretRef.Namespace).Get(secretRef.Name, metav1.GetOptions{}) + secret, err := client.CoreV1().Secrets(secretRef.Namespace).Get(secretRef.Name, metav1.GetOptions{}) if err != nil { return nil, err } @@ -466,19 +445,6 @@ func getAuthCredentialsFromClusterServiceBroker(client kubernetes.Interface, bro return &osb.AuthConfig{ BearerConfig: bearerConfig, }, nil - } else if authInfo.BasicAuthSecret != nil { - secretRef := authInfo.BasicAuthSecret - secret, err := client.Core().Secrets(secretRef.Namespace).Get(secretRef.Name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - basicAuthConfig, err := getBasicAuthConfig(secret) - if err != nil { - return nil, err - } - return &osb.AuthConfig{ - BasicAuthConfig: basicAuthConfig, - }, nil } return nil, fmt.Errorf("empty auth info or unsupported auth mode: %s", authInfo) } @@ -562,9 +528,9 @@ func convertClusterServicePlans(plans []osb.Plan, serviceClassID string) ([]*v1b Spec: v1beta1.ClusterServicePlanSpec{ ExternalName: plan.Name, ExternalID: plan.ID, - Free: (plan.Free != nil && *plan.Free), + Free: plan.Free != nil && *plan.Free, Description: plan.Description, - ClusterServiceClassRef: corev1.LocalObjectReference{Name: serviceClassID}, + ClusterServiceClassRef: v1beta1.ClusterObjectReference{Name: serviceClassID}, }, } servicePlans[i].SetName(plan.ID) @@ -634,44 +600,6 @@ func isServiceInstanceReady(instance *v1beta1.ServiceInstance) bool { return false } -// TODO (nilebox): The controllerRef methods below are merged into apimachinery and will be released in 1.8: -// https://github.com/kubernetes/kubernetes/pull/48319 -// Remove them after 1.8 is released and Service Catalog is migrated to it - -// IsControlledBy checks if the given object has a controller ownerReference set to the given owner -func IsControlledBy(obj metav1.Object, owner metav1.Object) bool { - ref := GetControllerOf(obj) - if ref == nil { - return false - } - return ref.UID == owner.GetUID() -} - -// GetControllerOf returns the controllerRef if controllee has a controller, -// otherwise returns nil. -func GetControllerOf(controllee metav1.Object) *metav1.OwnerReference { - for _, ref := range controllee.GetOwnerReferences() { - if ref.Controller != nil && *ref.Controller == true { - return &ref - } - } - return nil -} - -// NewControllerRef creates an OwnerReference pointing to the given owner. -func NewControllerRef(owner metav1.Object, gvk schema.GroupVersionKind) *metav1.OwnerReference { - blockOwnerDeletion := true - isController := true - return &metav1.OwnerReference{ - APIVersion: gvk.GroupVersion().String(), - Kind: gvk.Kind, - Name: owner.GetName(), - UID: owner.GetUID(), - BlockOwnerDeletion: &blockOwnerDeletion, - Controller: &isController, - } -} - // NewClientConfigurationForBroker creates a new ClientConfiguration for connecting // to the specified Broker func NewClientConfigurationForBroker(broker *v1beta1.ClusterServiceBroker, authConfig *osb.AuthConfig) *osb.ClientConfiguration { diff --git a/pkg/controller/controller_binding.go b/pkg/controller/controller_binding.go index 405a5abc9012..a810ec186c8c 100644 --- a/pkg/controller/controller_binding.go +++ b/pkg/controller/controller_binding.go @@ -121,19 +121,19 @@ func isServiceBindingFailed(binding *v1beta1.ServiceBinding) bool { return false } -// setAndUpdateOrphanMitigation is for setting the OrphanMitigationInProgress -// status to true, setting the proper condition statuses, and persisting the -// changes via updateServiceBindingStatus. -func (c *controller) setAndUpdateOrphanMitigation(binding *v1beta1.ServiceBinding, toUpdate *v1beta1.ServiceBinding, instance *v1beta1.ServiceInstance, serviceClass *v1beta1.ClusterServiceClass, brokerName string, errorStr string) error { +// setAndUpdateServiceBindingStartOrphanMitigation is for setting the +// OrphanMitigationInProgress status to true, setting the proper condition +// statuses, and persisting the changes via updateServiceBindingStatus. +func (c *controller) setAndUpdateServiceBindingStartOrphanMitigation(toUpdate *v1beta1.ServiceBinding) error { s := fmt.Sprintf( `%s "%s/%s": Starting orphan mitgation`, - typeSB, binding.Name, binding.Namespace, + typeSB, toUpdate.Name, toUpdate.Namespace, ) toUpdate.Status.OrphanMitigationInProgress = true toUpdate.Status.OperationStartTime = nil glog.V(5).Info(s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, @@ -141,7 +141,7 @@ func (c *controller) setAndUpdateOrphanMitigation(binding *v1beta1.ServiceBindin s, ) - c.recorder.Event(binding, corev1.EventTypeWarning, errorServiceBindingOrphanMitigation, s) + c.recorder.Event(toUpdate, corev1.EventTypeWarning, errorServiceBindingOrphanMitigation, s) if _, err := c.updateServiceBindingStatus(toUpdate); err != nil { return err } @@ -197,7 +197,7 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er typeSB, binding.Namespace, binding.Name, s, err, ) c.recorder.Event(binding, corev1.EventTypeWarning, errorNonexistentServiceInstanceReason, s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, @@ -217,7 +217,7 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er ) glog.Info(s) c.recorder.Event(binding, corev1.EventTypeWarning, errorWithOngoingAsyncOperation, s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, @@ -243,14 +243,14 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er if !isPlanBindable(serviceClass, servicePlan) { s := fmt.Sprintf( `References a non-bindable ClusterServiceClass (K8S: %q ExternalName: %q) and Plan (%q) combination`, - serviceClass.Name, serviceClass.Spec.ExternalName, instance.Spec.ExternalClusterServicePlanName, + serviceClass.Name, serviceClass.Spec.ExternalName, instance.Spec.ClusterServicePlanExternalName, ) glog.Warningf( `%s "%s/%s": %s`, typeSB, binding.Namespace, binding.Name, s, ) c.recorder.Event(binding, corev1.EventTypeWarning, errorNonbindableClusterServiceClassReason, s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, @@ -269,15 +269,15 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er typeSB, binding.Namespace, binding.Name, ) - ns, err := c.kubeClient.Core().Namespaces().Get(instance.Namespace, metav1.GetOptions{}) + ns, err := c.kubeClient.CoreV1().Namespaces().Get(instance.Namespace, metav1.GetOptions{}) if err != nil { s := fmt.Sprintf(`Failed to get namespace %q during binding: %s`, instance.Namespace, err) glog.Infof( - `%s "%s/%s": `, + `%s "%s/%s": %s`, typeSB, binding.Namespace, binding.Name, s, ) c.recorder.Eventf(binding, corev1.EventTypeWarning, errorFindingNamespaceServiceInstanceReason, s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, @@ -300,7 +300,7 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er typeSB, binding.Namespace, binding.Name, s, ) c.recorder.Eventf(binding, corev1.EventTypeWarning, errorServiceInstanceNotReadyReason, s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, @@ -331,7 +331,7 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er typeSB, binding.Namespace, binding.Name, s, ) c.recorder.Event(binding, corev1.EventTypeWarning, errorWithParameters, s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, @@ -352,7 +352,7 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er typeSB, binding.Namespace, binding.Name, s, ) c.recorder.Eventf(binding, corev1.EventTypeWarning, errorWithParameters, s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, @@ -372,7 +372,7 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er typeSB, binding.Namespace, binding.Name, s, ) c.recorder.Eventf(binding, corev1.EventTypeWarning, errorWithParameters, s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, @@ -415,7 +415,7 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er typeSB, binding.Namespace, binding.Name, s, ) c.recorder.Event(binding, corev1.EventTypeWarning, errorWithOriginatingIdentity, s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, @@ -442,28 +442,28 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er response, err := brokerClient.Bind(request) // orphan mitigation: looking for timeout if netErr, ok := err.(net.Error); ok && netErr.Timeout() { - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionFailed, v1beta1.ConditionTrue, errorBindCallReason, "Communication with the ServiceBroker timed out; Bind operation will not be retried: "+err.Error(), ) - return c.setAndUpdateOrphanMitigation(binding, toUpdate, instance, serviceClass, brokerName, netErr.Error()) + return c.setAndUpdateServiceBindingStartOrphanMitigation(toUpdate) } else if err != nil { if httpErr, ok := osb.IsHTTPError(err); ok { // orphan mitigation: looking for 2xx (excluding 200), 408, 5xx if httpErr.StatusCode > 200 && httpErr.StatusCode < 300 || httpErr.StatusCode == http.StatusRequestTimeout || httpErr.StatusCode >= 500 && httpErr.StatusCode < 600 { - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionFailed, v1beta1.ConditionTrue, errorBindCallReason, "ServiceBroker returned a failure; Bind operation will not be retried: "+err.Error(), ) - return c.setAndUpdateOrphanMitigation(binding, toUpdate, instance, serviceClass, brokerName, httpErr.Error()) + return c.setAndUpdateServiceBindingStartOrphanMitigation(toUpdate) } s := fmt.Sprintf( `Error creating ServiceBinding for ServiceInstance "%s/%s" of ClusterServiceClass (K8S: %q ExternalName: %q) at ClusterServiceBroker %q: %v`, @@ -474,14 +474,14 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er ) c.recorder.Event(binding, corev1.EventTypeWarning, errorBindCallReason, s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionFailed, v1beta1.ConditionTrue, "ServiceBindingReturnedFailure", s, ) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, @@ -503,7 +503,7 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er typeSB, binding.Name, binding.Namespace, s, ) c.recorder.Event(binding, corev1.EventTypeWarning, errorBindCallReason, s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, @@ -517,7 +517,7 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er typeSB, binding.Namespace, binding.Name, s, ) c.recorder.Event(binding, corev1.EventTypeWarning, errorReconciliationRetryTimeoutReason, s) - c.setServiceBindingCondition(toUpdate, + setServiceBindingCondition(toUpdate, v1beta1.ServiceBindingConditionFailed, v1beta1.ConditionTrue, errorReconciliationRetryTimeoutReason, @@ -553,7 +553,7 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er typeSB, binding.Namespace, binding.Name, s, ) c.recorder.Event(binding, corev1.EventTypeWarning, errorInjectingBindResultReason, s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, @@ -568,12 +568,12 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er ) glog.Info(s) c.recorder.Event(binding, corev1.EventTypeWarning, errorReconciliationRetryTimeoutReason, s) - c.setServiceBindingCondition(toUpdate, + setServiceBindingCondition(toUpdate, v1beta1.ServiceBindingConditionFailed, v1beta1.ConditionTrue, errorReconciliationRetryTimeoutReason, s) - return c.setAndUpdateOrphanMitigation(binding, toUpdate, instance, serviceClass, brokerName, "too much time has elapsed") + return c.setAndUpdateServiceBindingStartOrphanMitigation(toUpdate) } if _, err := c.updateServiceBindingStatus(toUpdate); err != nil { @@ -586,7 +586,7 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er c.clearServiceBindingCurrentOperation(toUpdate) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionTrue, @@ -620,7 +620,7 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er typeSB, binding.Namespace, binding.Name, s, ) c.recorder.Eventf(binding, corev1.EventTypeWarning, errorEjectingBindReason, "%v %v", errorEjectingBindMessage, s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionUnknown, @@ -649,7 +649,7 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er typeSB, binding.Namespace, binding.Name, s, ) c.recorder.Event(binding, corev1.EventTypeWarning, errorWithOriginatingIdentity, s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, @@ -688,14 +688,14 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er typeSB, binding.Namespace, binding.Name, s, ) c.recorder.Event(binding, corev1.EventTypeWarning, errorUnbindCallReason, s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionUnknown, errorUnbindCallReason, "Unbind call failed. "+s) if !toUpdate.Status.OrphanMitigationInProgress { - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionFailed, v1beta1.ConditionTrue, @@ -717,7 +717,7 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er typeSB, binding.Namespace, binding.Name, s, ) c.recorder.Event(binding, corev1.EventTypeWarning, errorUnbindCallReason, s) - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionUnknown, @@ -731,7 +731,7 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er typeSB, binding.Namespace, binding.Name, s, ) c.recorder.Event(binding, corev1.EventTypeWarning, errorReconciliationRetryTimeoutReason, s) - c.setServiceBindingCondition(toUpdate, + setServiceBindingCondition(toUpdate, v1beta1.ServiceBindingConditionFailed, v1beta1.ConditionTrue, errorReconciliationRetryTimeoutReason, @@ -750,7 +750,7 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er typeSB, binding.Namespace, binding.Name, s, ) c.recorder.Event(binding, corev1.EventTypeWarning, errorReconciliationRetryTimeoutReason, s) - c.setServiceBindingCondition(toUpdate, + setServiceBindingCondition(toUpdate, v1beta1.ServiceBindingConditionFailed, v1beta1.ConditionTrue, errorReconciliationRetryTimeoutReason, @@ -774,14 +774,14 @@ func (c *controller) reconcileServiceBinding(binding *v1beta1.ServiceBinding) er `%s "%s/%s": Orphan mitigation successful`, typeSB, binding.Namespace, binding.Name, ) - c.setServiceBindingCondition(toUpdate, + setServiceBindingCondition(toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, successOrphanMitigationReason, s) } else { s := "The binding was deleted successfully" - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, @@ -839,12 +839,12 @@ func (c *controller) injectServiceBinding(binding *v1beta1.ServiceBinding, crede } // Creating/updating the Secret - secretClient := c.kubeClient.Core().Secrets(binding.Namespace) + secretClient := c.kubeClient.CoreV1().Secrets(binding.Namespace) existingSecret, err := secretClient.Get(binding.Spec.SecretName, metav1.GetOptions{}) if err == nil { // Update existing secret - if !IsControlledBy(existingSecret, binding) { - controllerRef := GetControllerOf(existingSecret) + if !metav1.IsControlledBy(existingSecret, binding) { + controllerRef := metav1.GetControllerOf(existingSecret) return fmt.Errorf(`Secret "%s/%s" is not owned by ServiceBinding, controllerRef: %v`, binding.Namespace, existingSecret.Name, controllerRef) } existingSecret.Data = secretData @@ -869,7 +869,7 @@ func (c *controller) injectServiceBinding(binding *v1beta1.ServiceBinding, crede Name: binding.Spec.SecretName, Namespace: binding.Namespace, OwnerReferences: []metav1.OwnerReference{ - *NewControllerRef(binding, bindingControllerKind), + *metav1.NewControllerRef(binding, bindingControllerKind), }, }, Data: secretData, @@ -896,7 +896,7 @@ func (c *controller) ejectServiceBinding(binding *v1beta1.ServiceBinding) error `%s "%s/%s": Deleting Secret "%s/%s"`, typeSB, binding.Namespace, binding.Name, binding.Namespace, binding.Spec.SecretName, ) - err = c.kubeClient.Core().Secrets(binding.Namespace).Delete(binding.Spec.SecretName, &metav1.DeleteOptions{}) + err = c.kubeClient.CoreV1().Secrets(binding.Namespace).Delete(binding.Spec.SecretName, &metav1.DeleteOptions{}) if err != nil && !apierrors.IsNotFound(err) { return err } @@ -913,7 +913,7 @@ func (c *controller) ejectServiceBinding(binding *v1beta1.ServiceBinding) error // // Note: objects coming from informers should never be mutated; always pass a // deep copy as the binding parameter. -func (c *controller) setServiceBindingCondition(toUpdate *v1beta1.ServiceBinding, +func setServiceBindingCondition(toUpdate *v1beta1.ServiceBinding, conditionType v1beta1.ServiceBindingConditionType, status v1beta1.ConditionStatus, reason, message string) { @@ -1005,7 +1005,7 @@ func (c *controller) updateServiceBindingCondition( return err } - c.setServiceBindingCondition(toUpdate, conditionType, status, reason, message) + setServiceBindingCondition(toUpdate, conditionType, status, reason, message) glog.V(4).Infof( `%s "%s/%s": Updating %v condition to %v (Reason: %q, Message: %q)`, @@ -1056,7 +1056,7 @@ func (c *controller) recordStartOfServiceBindingOperation(toUpdate *v1beta1.Serv reason = unbindingInFlightReason message = unbindingInFlightMessage } - c.setServiceBindingCondition( + setServiceBindingCondition( toUpdate, v1beta1.ServiceBindingConditionReady, v1beta1.ConditionFalse, diff --git a/pkg/controller/controller_binding_test.go b/pkg/controller/controller_binding_test.go index 95f08d039ace..9c31fc7c1439 100644 --- a/pkg/controller/controller_binding_test.go +++ b/pkg/controller/controller_binding_test.go @@ -40,7 +40,6 @@ import ( "github.com/kubernetes-incubator/service-catalog/pkg/api" scfeatures "github.com/kubernetes-incubator/service-catalog/pkg/features" - "k8s.io/api/core/v1" clientgotesting "k8s.io/client-go/testing" ) @@ -55,7 +54,7 @@ func TestReconcileServiceBindingNonExistingServiceInstance(t *testing.T) { Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testNonExistentClusterServiceClassName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testNonExistentClusterServiceClassName}, ExternalID: testServiceBindingGUID, }, } @@ -84,8 +83,8 @@ func TestReconcileServiceBindingNonExistingServiceInstance(t *testing.T) { assertNumEvents(t, events, 1) expectedEvent := corev1.EventTypeWarning + " " + errorNonexistentServiceInstanceReason + " " + "References a non-existent ServiceInstance \"/nothere\"" - if e, a := expectedEvent, events[0]; e != a { - t.Fatalf("Received unexpected event: %v", a) + if err := checkEvents(events, []string{expectedEvent}); err != nil { + t.Fatal(err) } } @@ -100,8 +99,8 @@ func TestReconcileServiceBindingUnresolvedClusterServiceClassReference(t *testin ObjectMeta: metav1.ObjectMeta{Name: testServiceInstanceName, Namespace: testNamespace}, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServiceClassName: testNonExistentClusterServiceClassName, - ExternalClusterServicePlanName: testClusterServicePlanName, + ClusterServiceClassExternalName: testNonExistentClusterServiceClassName, + ClusterServicePlanExternalName: testClusterServicePlanName, }, ExternalID: testServiceInstanceGUID, }, @@ -116,7 +115,7 @@ func TestReconcileServiceBindingUnresolvedClusterServiceClassReference(t *testin Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, }, } @@ -126,7 +125,7 @@ func TestReconcileServiceBindingUnresolvedClusterServiceClassReference(t *testin t.Fatal("serviceclassref was nil and reconcile should return an error") } if !strings.Contains(err.Error(), "not been resolved yet") { - t.Fatalf("Did not get the expected error %q : got %q", "not been resolved yet", err) + t.Fatalf("Did not get the expected error: %s", expectedGot("not been resolved yet", err)) } brokerActions := fakeClusterServiceBrokerClient.Actions() @@ -148,11 +147,11 @@ func TestReconcileServiceBindingUnresolvedClusterServicePlanReference(t *testing ObjectMeta: metav1.ObjectMeta{Name: testServiceInstanceName, Namespace: testNamespace}, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServiceClassName: testNonExistentClusterServiceClassName, - ExternalClusterServicePlanName: testClusterServicePlanName, + ClusterServiceClassExternalName: testNonExistentClusterServiceClassName, + ClusterServicePlanExternalName: testClusterServicePlanName, }, ExternalID: testServiceInstanceGUID, - ClusterServiceClassRef: &v1.ObjectReference{Name: "Some Ref"}, + ClusterServiceClassRef: &v1beta1.ClusterObjectReference{Name: "Some Ref"}, }, } sharedInformers.ServiceInstances().Informer().GetStore().Add(instance) @@ -165,7 +164,7 @@ func TestReconcileServiceBindingUnresolvedClusterServicePlanReference(t *testing Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, }, } @@ -175,8 +174,8 @@ func TestReconcileServiceBindingUnresolvedClusterServicePlanReference(t *testing t.Fatal("serviceclass nothere was found and it should not be found") } - if !strings.Contains(err.Error(), "not been resolved yet") { - t.Fatalf("Did not get the expected error %q : got %q", "not been resolved yet", err) + if err := checkEventContains(err.Error(), "not been resolved yet"); err != nil { + t.Fatal(err) } brokerActions := fakeClusterServiceBrokerClient.Actions() @@ -198,12 +197,12 @@ func TestReconcileServiceBindingNonExistingClusterServiceClass(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: testServiceInstanceName, Namespace: testNamespace}, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServiceClassName: testNonExistentClusterServiceClassName, - ExternalClusterServicePlanName: testClusterServicePlanName, + ClusterServiceClassExternalName: testNonExistentClusterServiceClassName, + ClusterServicePlanExternalName: testClusterServicePlanName, }, ExternalID: testServiceInstanceGUID, - ClusterServiceClassRef: &v1.ObjectReference{Name: "nosuchclassid"}, - ClusterServicePlanRef: &v1.ObjectReference{Name: "nosuchplanid"}, + ClusterServiceClassRef: &v1beta1.ClusterObjectReference{Name: "nosuchclassid"}, + ClusterServicePlanRef: &v1beta1.ClusterObjectReference{Name: "nosuchplanid"}, }, } sharedInformers.ServiceInstances().Informer().GetStore().Add(instance) @@ -216,7 +215,7 @@ func TestReconcileServiceBindingNonExistingClusterServiceClass(t *testing.T) { Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, }, } @@ -243,8 +242,8 @@ func TestReconcileServiceBindingNonExistingClusterServiceClass(t *testing.T) { assertNumEvents(t, events, 1) expectedEvent := corev1.EventTypeWarning + " " + errorNonexistentClusterServiceClassMessage + " " + "References a non-existent ClusterServiceClass (K8S: \"nosuchclassid\" ExternalName: \"" + testNonExistentClusterServiceClassName + "\")" - if e, a := expectedEvent, events[0]; e != a { - t.Fatalf("Received unexpected event expected: %v got: %v", e, a) + if err := checkEvents(events, []string{expectedEvent}); err != nil { + t.Fatal(err) } } @@ -264,7 +263,7 @@ func TestReconcileServiceBindingWithSecretConflict(t *testing.T) { addGetNamespaceReaction(fakeKubeClient) // existing Secret with nil controllerRef - addGetSecretReaction(fakeKubeClient, &v1.Secret{ + addGetSecretReaction(fakeKubeClient, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: testServiceBindingName, Namespace: testNamespace}, }) @@ -280,7 +279,7 @@ func TestReconcileServiceBindingWithSecretConflict(t *testing.T) { Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, SecretName: testServiceBindingSecretName, }, @@ -328,18 +327,19 @@ func TestReconcileServiceBindingWithSecretConflict(t *testing.T) { // second action is a get on the secret action := kubeActions[1].(clientgotesting.GetAction) if e, a := "get", action.GetVerb(); e != a { - t.Fatalf("Unexpected verb on action; expected %v, got %v", e, a) + t.Fatalf("Unexpected verb on action; %s", expectedGot(e, a)) } if e, a := "secrets", action.GetResource().Resource; e != a { - t.Fatalf("Unexpected resource on action; expected %v, got %v", e, a) + t.Fatalf("Unexpected resource on action; %s", expectedGot(e, a)) } events := getRecordedEvents(testController) assertNumEvents(t, events, 1) expectedEvent := corev1.EventTypeWarning + " " + errorInjectingBindResultReason - if e, a := expectedEvent, events[0]; !strings.HasPrefix(a, e) { - t.Fatalf("Received unexpected event: %v", a) + + if err := checkEventPrefixes(events, []string{expectedEvent}); err != nil { + t.Fatal(err) } } @@ -372,7 +372,7 @@ func TestReconcileServiceBindingWithParameters(t *testing.T) { Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, SecretName: testServiceBindingSecretName, }, @@ -442,46 +442,46 @@ func TestReconcileServiceBindingWithParameters(t *testing.T) { // second action is a get on the secret action := kubeActions[2].(clientgotesting.CreateAction) if e, a := "create", action.GetVerb(); e != a { - t.Fatalf("Unexpected verb on action; expected %v, got %v", e, a) + t.Fatalf("Unexpected verb on action; %s", expectedGot(e, a)) } if e, a := "secrets", action.GetResource().Resource; e != a { - t.Fatalf("Unexpected resource on action; expected %v, got %v", e, a) + t.Fatalf("Unexpected resource on action; %s", expectedGot(e, a)) } - actionSecret, ok := action.GetObject().(*v1.Secret) + actionSecret, ok := action.GetObject().(*corev1.Secret) if !ok { - t.Fatal("couldn't convert secret into a v1.Secret") + t.Fatal("couldn't convert secret into a corev1.Secret") } - controllerRef := GetControllerOf(actionSecret) + controllerRef := metav1.GetControllerOf(actionSecret) if controllerRef == nil || controllerRef.UID != updatedServiceBinding.UID { t.Fatalf("Secret is not owned by the ServiceBinding: %v", controllerRef) } - if !IsControlledBy(actionSecret, updatedServiceBinding) { + if !metav1.IsControlledBy(actionSecret, updatedServiceBinding) { t.Fatal("Secret is not owned by the ServiceBinding") } if e, a := testServiceBindingSecretName, actionSecret.Name; e != a { - t.Fatalf("Unexpected name of secret; expected %v, got %v", e, a) + t.Fatalf("Unexpected name of secret; %s", expectedGot(e, a)) } value, ok := actionSecret.Data["a"] if !ok { t.Fatal("Didn't find secret key 'a' in created secret") } if e, a := "b", string(value); e != a { - t.Fatalf("Unexpected value of key 'a' in created secret; expected %v got %v", e, a) + t.Fatalf("Unexpected value of key 'a' in created secret; %s", expectedGot(e, a)) } value, ok = actionSecret.Data["c"] if !ok { t.Fatal("Didn't find secret key 'a' in created secret") } if e, a := "d", string(value); e != a { - t.Fatalf("Unexpected value of key 'c' in created secret; expected %v got %v", e, a) + t.Fatalf("Unexpected value of key 'c' in created secret; %s", expectedGot(e, a)) } events := getRecordedEvents(testController) assertNumEvents(t, events, 1) expectedEvent := corev1.EventTypeNormal + " " + successInjectedBindResultReason + " " + successInjectedBindResultMessage - if e, a := expectedEvent, events[0]; e != a { - t.Fatalf("Received unexpected event: %v", a) + if err := checkEvents(events, []string{expectedEvent}); err != nil { + t.Fatal(err) } } @@ -503,7 +503,7 @@ func TestReconcileServiceBindingNonbindableClusterServiceClass(t *testing.T) { Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, }, } @@ -528,8 +528,8 @@ func TestReconcileServiceBindingNonbindableClusterServiceClass(t *testing.T) { assertNumEvents(t, events, 1) expectedEvent := corev1.EventTypeWarning + " " + errorNonbindableClusterServiceClassReason + ` References a non-bindable ClusterServiceClass (K8S: "UNBINDABLE-SERVICE" ExternalName: "test-unbindable-serviceclass") and Plan ("test-unbindable-plan") combination` - if e, a := expectedEvent, events[0]; e != a { - t.Fatalf("Received unexpected event: %v", a) + if err := checkEvents(events, []string{expectedEvent}); err != nil { + t.Fatal(err) } } @@ -574,7 +574,7 @@ func TestReconcileServiceBindingNonbindableClusterServiceClassBindablePlan(t *te Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, SecretName: testServiceBindingSecretName, }, @@ -616,31 +616,31 @@ func TestReconcileServiceBindingNonbindableClusterServiceClassBindablePlan(t *te // second action is a get on the secret action := kubeActions[2].(clientgotesting.CreateAction) if e, a := "create", action.GetVerb(); e != a { - t.Fatalf("Unexpected verb on action; expected %v, got %v", e, a) + t.Fatalf("Unexpected verb on action; %s", expectedGot(e, a)) } if e, a := "secrets", action.GetResource().Resource; e != a { - t.Fatalf("Unexpected resource on action; expected %v, got %v", e, a) + t.Fatalf("Unexpected resource on action; %s", expectedGot(e, a)) } - actionSecret, ok := action.GetObject().(*v1.Secret) + actionSecret, ok := action.GetObject().(*corev1.Secret) if !ok { - t.Fatal("couldn't convert secret into a v1.Secret") + t.Fatal("couldn't convert secret into a corev1.Secret") } if e, a := testServiceBindingSecretName, actionSecret.Name; e != a { - t.Fatalf("Unexpected name of secret; expected %v, got %v", e, a) + t.Fatalf("Unexpected name of secret; %s", expectedGot(e, a)) } value, ok := actionSecret.Data["a"] if !ok { t.Fatal("Didn't find secret key 'a' in created secret") } if e, a := "b", string(value); e != a { - t.Fatalf("Unexpected value of key 'a' in created secret; expected %v got %v", e, a) + t.Fatalf("Unexpected value of key 'a' in created secret; %s", expectedGot(e, a)) } value, ok = actionSecret.Data["c"] if !ok { t.Fatal("Didn't find secret key 'a' in created secret") } if e, a := "d", string(value); e != a { - t.Fatalf("Unexpected value of key 'c' in created secret; expected %v got %v", e, a) + t.Fatalf("Unexpected value of key 'c' in created secret; %s", expectedGot(e, a)) } events := getRecordedEvents(testController) @@ -665,7 +665,7 @@ func TestReconcileServiceBindingBindableClusterServiceClassNonbindablePlan(t *te Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, }, } @@ -690,8 +690,8 @@ func TestReconcileServiceBindingBindableClusterServiceClassNonbindablePlan(t *te assertNumEvents(t, events, 1) expectedEvent := corev1.EventTypeWarning + " " + errorNonbindableClusterServiceClassReason + ` References a non-bindable ClusterServiceClass (K8S: "SCGUID" ExternalName: "test-serviceclass") and Plan ("test-unbindable-plan") combination` - if e, a := expectedEvent, events[0]; e != a { - t.Fatalf("Received unexpected event: %v", a) + if err := checkEvents(events, []string{expectedEvent}); err != nil { + t.Fatal(err) } } @@ -713,7 +713,7 @@ func TestReconcileServiceBindingFailsWithServiceInstanceAsyncOngoing(t *testing. Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, }, } @@ -723,8 +723,8 @@ func TestReconcileServiceBindingFailsWithServiceInstanceAsyncOngoing(t *testing. t.Fatalf("reconcileServiceBinding did not fail with async operation ongoing") } - if !strings.Contains(err.Error(), "Ongoing Asynchronous") { - t.Fatalf("Did not get the expected error %q : got %q", "Ongoing Asynchronous", err) + if err := checkEventContains(err.Error(), "Ongoing Asynchronous"); err != nil { + t.Fatal(err) } brokerActions := fakeClusterServiceBrokerClient.Actions() @@ -776,7 +776,7 @@ func TestReconcileServiceBindingServiceInstanceNotReady(t *testing.T) { Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, }, } @@ -812,7 +812,7 @@ func TestReconcileServiceBindingNamespaceError(t *testing.T) { fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, sharedInformers := newTestController(t, noFakeActions()) fakeKubeClient.AddReactor("get", "namespaces", func(action clientgotesting.Action) (bool, runtime.Object, error) { - return true, &v1.Namespace{}, errors.New("No namespace") + return true, &corev1.Namespace{}, errors.New("No namespace") }) sharedInformers.ClusterServiceBrokers().Informer().GetStore().Add(getTestClusterServiceBroker()) @@ -827,7 +827,7 @@ func TestReconcileServiceBindingNamespaceError(t *testing.T) { Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, }, } @@ -877,7 +877,7 @@ func TestReconcileServiceBindingDelete(t *testing.T) { Generation: 2, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, SecretName: testServiceBindingSecretName, }, @@ -1180,7 +1180,7 @@ func TestReconcileServiceBindingWithClusterServiceBrokerError(t *testing.T) { Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, SecretName: testServiceBindingSecretName, }, @@ -1239,7 +1239,7 @@ func TestReconcileServiceBindingWithClusterServiceBrokerHTTPError(t *testing.T) Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, SecretName: testServiceBindingSecretName, }, @@ -1605,7 +1605,7 @@ func TestReconcileUnbindingWithClusterServiceBrokerError(t *testing.T) { Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, SecretName: testServiceBindingSecretName, }, @@ -1668,7 +1668,7 @@ func TestReconcileUnbindingWithClusterServiceBrokerHTTPError(t *testing.T) { Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, SecretName: testServiceBindingSecretName, }, @@ -1932,7 +1932,7 @@ func TestReconcileBindingWithSecretConflictFailedAfterFinalRetry(t *testing.T) { addGetNamespaceReaction(fakeKubeClient) // existing Secret with nil controllerRef - addGetSecretReaction(fakeKubeClient, &v1.Secret{ + addGetSecretReaction(fakeKubeClient, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: testServiceBindingName, Namespace: testNamespace}, }) @@ -1949,7 +1949,7 @@ func TestReconcileBindingWithSecretConflictFailedAfterFinalRetry(t *testing.T) { Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, SecretName: testServiceBindingSecretName, }, @@ -2073,7 +2073,7 @@ func TestReconcileServiceBindingWithSecretParameters(t *testing.T) { addGetNamespaceReaction(fakeKubeClient) - paramSecret := &v1.Secret{ + paramSecret := &corev1.Secret{ Data: map[string][]byte{ "param-secret-key": []byte("{\"b\":\"2\"}"), }, @@ -2099,7 +2099,7 @@ func TestReconcileServiceBindingWithSecretParameters(t *testing.T) { Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, SecretName: testServiceBindingSecretName, }, @@ -2272,7 +2272,7 @@ func TestReconcileBindingWithSetOrphanMitigation(t *testing.T) { addGetNamespaceReaction(fakeKubeClient) // existing Secret with nil controllerRef - addGetSecretReaction(fakeKubeClient, &v1.Secret{ + addGetSecretReaction(fakeKubeClient, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: testServiceBindingName, Namespace: testNamespace}, }) @@ -2288,7 +2288,7 @@ func TestReconcileBindingWithSetOrphanMitigation(t *testing.T) { Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, SecretName: testServiceBindingSecretName, }, @@ -2352,7 +2352,7 @@ func TestReconcileBindingWithOrphanMitigationInProgress(t *testing.T) { addGetNamespaceReaction(fakeKubeClient) // existing Secret with nil controllerRef - addGetSecretReaction(fakeKubeClient, &v1.Secret{ + addGetSecretReaction(fakeKubeClient, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: testServiceBindingName, Namespace: testNamespace}, }) @@ -2369,7 +2369,7 @@ func TestReconcileBindingWithOrphanMitigationInProgress(t *testing.T) { Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, SecretName: testServiceBindingSecretName, }, @@ -2422,7 +2422,7 @@ func TestReconcileBindingWithOrphanMitigationReconciliationRetryTimeOut(t *testi addGetNamespaceReaction(fakeKubeClient) // existing Secret with nil controllerRef - addGetSecretReaction(fakeKubeClient, &v1.Secret{ + addGetSecretReaction(fakeKubeClient, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: testServiceBindingName, Namespace: testNamespace}, }) @@ -2439,7 +2439,7 @@ func TestReconcileBindingWithOrphanMitigationReconciliationRetryTimeOut(t *testi Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, SecretName: testServiceBindingSecretName, }, diff --git a/pkg/controller/controller_broker.go b/pkg/controller/controller_broker.go index 95f6a02bbf62..3135a56a8be3 100644 --- a/pkg/controller/controller_broker.go +++ b/pkg/controller/controller_broker.go @@ -25,6 +25,7 @@ import ( "github.com/kubernetes-incubator/service-catalog/pkg/api" "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + "github.com/kubernetes-incubator/service-catalog/pkg/pretty" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -33,8 +34,6 @@ import ( "k8s.io/client-go/tools/cache" ) -var typeCSB = "ClusterServiceBroker" - // the Message strings have a terminating period and space so they can // be easily combined with a follow on specific message. const ( @@ -95,6 +94,7 @@ func (c *controller) brokerDelete(obj interface{}) { // the controller's broker relist interval has not elapsed since the broker's // ready condition became true, or if the broker's RelistBehavior is set to Manual. func shouldReconcileClusterServiceBroker(broker *v1beta1.ClusterServiceBroker, now time.Time) bool { + pcb := pretty.NewContextBuilder(pretty.ClusterServiceBroker, "", broker.Name) if broker.Status.ReconciledGeneration != broker.Generation { // If the spec has changed, we should reconcile the broker. return true @@ -118,18 +118,12 @@ func shouldReconcileClusterServiceBroker(broker *v1beta1.ClusterServiceBroker, n // If a broker is configured with RelistBehaviorManual, it should // ignore the Duration and only relist based on spec changes - glog.V(10).Infof( - "ClusterServiceBroker %q: Not processing because RelistBehavior is set to Manual", - broker.Name, - ) + glog.V(10).Info(pcb.Message("Not processing because RelistBehavior is set to Manual")) return false } if broker.Spec.RelistDuration == nil { - glog.Errorf( - "ClusterServiceBroker %q: Unable to process because RelistBehavior is set to Duration with a nil RelistDuration value", - broker.Name, - ) + glog.Error(pcb.Message("Unable to process because RelistBehavior is set to Duration with a nil RelistDuration value")) return false } @@ -138,10 +132,7 @@ func shouldReconcileClusterServiceBroker(broker *v1beta1.ClusterServiceBroker, n duration := broker.Spec.RelistDuration.Duration intervalPassed := now.After(condition.LastTransitionTime.Add(duration)) if intervalPassed == false { - glog.V(10).Infof( - "ClusterServiceBroker %q: Not processing because RelistDuration has not elapsed since the broker became ready", - broker.Name, - ) + glog.V(10).Info(pcb.Message("Not processing because RelistDuration has not elapsed since the broker became ready")) } return intervalPassed } @@ -158,12 +149,13 @@ func shouldReconcileClusterServiceBroker(broker *v1beta1.ClusterServiceBroker, n func (c *controller) reconcileClusterServiceBrokerKey(key string) error { broker, err := c.brokerLister.Get(key) + pcb := pretty.NewContextBuilder(pretty.ClusterServiceBroker, "", key) if errors.IsNotFound(err) { - glog.Infof("ClusterServiceBroker %q: Not doing work because it has been deleted", key) + glog.Info(pcb.Message("Not doing work because it has been deleted")) return nil } if err != nil { - glog.Infof("ClusterServiceBroker %q: Unable to retrieve object from store: %v", key, err) + glog.Info(pcb.Messagef("Unable to retrieve object from store: %v", err)) return err } @@ -174,7 +166,8 @@ func (c *controller) reconcileClusterServiceBrokerKey(key string) error { // error is returned to indicate that the binding has not been fully // processed and should be resubmitted at a later time. func (c *controller) reconcileClusterServiceBroker(broker *v1beta1.ClusterServiceBroker) error { - glog.V(4).Infof("%s %q: processing", typeCSB, broker.Name) + pcb := pretty.NewContextBuilder(pretty.ClusterServiceBroker, "", broker.Name) + glog.V(4).Infof(pcb.Message("Processing")) // * If the broker's ready condition is true and the RelistBehavior has been // set to Manual, do not reconcile it. @@ -187,11 +180,8 @@ func (c *controller) reconcileClusterServiceBroker(broker *v1beta1.ClusterServic if broker.DeletionTimestamp == nil { // Add or update authConfig, err := getAuthCredentialsFromClusterServiceBroker(c.kubeClient, broker) if err != nil { - s := fmt.Sprintf( - "%s %q: Error getting broker auth credentials: %s", - typeCSB, broker.Name, err, - ) - glog.Info(s) + s := fmt.Sprintf("Error getting broker auth credentials: %s", err) + glog.Info(pcb.Message(s)) c.recorder.Event(broker, corev1.EventTypeWarning, errorAuthCredentialsReason, s) if err := c.updateClusterServiceBrokerCondition(broker, v1beta1.ServiceBrokerConditionReady, v1beta1.ConditionFalse, errorFetchingCatalogReason, errorFetchingCatalogMessage+s); err != nil { return err @@ -201,14 +191,11 @@ func (c *controller) reconcileClusterServiceBroker(broker *v1beta1.ClusterServic clientConfig := NewClientConfigurationForBroker(broker, authConfig) - glog.V(4).Infof( - "%s %q: creating client, URL: %v", - typeCSB, broker.Name, broker.Spec.URL, - ) + glog.V(4).Info(pcb.Messagef("Creating client, URL: %v", broker.Spec.URL)) brokerClient, err := c.brokerClientCreateFunc(clientConfig) if err != nil { s := fmt.Sprintf("Error creating client for broker %q: %s", broker.Name, err) - glog.Info(s) + glog.Info(pcb.Message(s)) c.recorder.Event(broker, corev1.EventTypeWarning, errorAuthCredentialsReason, s) if err := c.updateClusterServiceBrokerCondition(broker, v1beta1.ServiceBrokerConditionReady, v1beta1.ConditionFalse, errorFetchingCatalogReason, errorFetchingCatalogMessage+s); err != nil { return err @@ -216,20 +203,14 @@ func (c *controller) reconcileClusterServiceBroker(broker *v1beta1.ClusterServic return err } - glog.V(4).Infof( - "%s %q: processing adding/update event", - typeCSB, broker.Name, - ) + glog.V(4).Info(pcb.Message("Processing adding/update event")) // get the broker's catalog now := metav1.Now() brokerCatalog, err := brokerClient.GetCatalog() if err != nil { - s := fmt.Sprintf( - "%s %q: Error getting broker catalog: %s", - typeCSB, broker.Name, err, - ) - glog.Warning(s) + s := fmt.Sprintf("Error getting broker catalog: %s", err) + glog.Warning(pcb.Message(s)) c.recorder.Eventf(broker, corev1.EventTypeWarning, errorFetchingCatalogReason, s) if err := c.updateClusterServiceBrokerCondition(broker, v1beta1.ServiceBrokerConditionReady, v1beta1.ConditionFalse, errorFetchingCatalogReason, errorFetchingCatalogMessage+s); err != nil { return err @@ -240,19 +221,13 @@ func (c *controller) reconcileClusterServiceBroker(broker *v1beta1.ClusterServic toUpdate := clone.(*v1beta1.ClusterServiceBroker) toUpdate.Status.OperationStartTime = &now if _, err := c.serviceCatalogClient.ClusterServiceBrokers().UpdateStatus(toUpdate); err != nil { - glog.Errorf( - "%s %q: Error updating operation start time: %v", - typeCSB, broker.Name, err, - ) + glog.Error(pcb.Messagef("Error updating operation start time: %v", err)) return err } } } else if !time.Now().Before(broker.Status.OperationStartTime.Time.Add(c.reconciliationRetryDuration)) { - s := fmt.Sprintf( - "%s %q: stopping reconciliation retries because too much time has elapsed", - typeCSB, broker.Name, - ) - glog.Info(s) + s := "Stopping reconciliation retries because too much time has elapsed" + glog.Info(pcb.Message(s)) c.recorder.Event(broker, corev1.EventTypeWarning, errorReconciliationRetryTimeoutReason, s) clone, err := api.Scheme.DeepCopy(broker) if err == nil { @@ -271,10 +246,8 @@ func (c *controller) reconcileClusterServiceBroker(broker *v1beta1.ClusterServic } return err } - glog.V(5).Infof( - "%s %q: successfully fetched %v catalog entries", - typeCSB, broker.Name, len(brokerCatalog.Services), - ) + + glog.V(5).Info(pcb.Messagef("Successfully fetched %v catalog entries", len(brokerCatalog.Services))) // set the operation start time if not already set if broker.Status.OperationStartTime != nil { @@ -285,38 +258,29 @@ func (c *controller) reconcileClusterServiceBroker(broker *v1beta1.ClusterServic toUpdate := clone.(*v1beta1.ClusterServiceBroker) toUpdate.Status.OperationStartTime = nil if _, err := c.serviceCatalogClient.ClusterServiceBrokers().UpdateStatus(toUpdate); err != nil { - glog.Errorf( - "%s %q: error updating operation start time: %v", - typeCSB, broker.Name, err, - ) + glog.Error(pcb.Messagef("Error updating operation start time: %v", err)) return err } } // convert the broker's catalog payload into our API objects - glog.V(4).Infof( - "%s %q: converting catalog response into service-catalog API", - typeCSB, broker.Name, - ) + glog.V(4).Info(pcb.Message("Converting catalog response into service-catalog API")) payloadServiceClasses, payloadServicePlans, err := convertCatalog(brokerCatalog) if err != nil { s := fmt.Sprintf("Error converting catalog payload for broker %q to service-catalog API: %s", broker.Name, err) - glog.Warning(s) + glog.Warning(pcb.Message(s)) c.recorder.Eventf(broker, corev1.EventTypeWarning, errorSyncingCatalogReason, s) if err := c.updateClusterServiceBrokerCondition(broker, v1beta1.ServiceBrokerConditionReady, v1beta1.ConditionFalse, errorSyncingCatalogReason, errorSyncingCatalogMessage+s); err != nil { return err } return err } - glog.V(5).Infof( - "%s %q: successfully converted catalog payload from to service-catalog API", - typeCSB, broker.Name, - ) + glog.V(5).Info(pcb.Message("Successfully converted catalog payload from to service-catalog API")) // brokers must return at least one service; enforce this constraint if len(payloadServiceClasses) == 0 { s := fmt.Sprintf("Error getting catalog payload for broker %q; received zero services; at least one service is required", broker.Name) - glog.Warning(s) + glog.Warning(pcb.Message(s)) c.recorder.Eventf(broker, corev1.EventTypeWarning, errorSyncingCatalogReason, s) if err := c.updateClusterServiceBrokerCondition(broker, v1beta1.ServiceBrokerConditionReady, v1beta1.ConditionFalse, errorSyncingCatalogReason, errorSyncingCatalogMessage+s); err != nil { return err @@ -335,58 +299,46 @@ func (c *controller) reconcileClusterServiceBroker(broker *v1beta1.ClusterServic existingServiceClassMap := convertServiceClassListToMap(existingServiceClasses) existingServicePlanMap := convertServicePlanListToMap(existingServicePlans) - // reconcile the plans that were part of the broker's catalog payload - for _, payloadServicePlan := range payloadServicePlans { - existingServicePlan, _ := existingServicePlanMap[payloadServicePlan.Name] - delete(existingServicePlanMap, payloadServicePlan.Name) + // reconcile the serviceClasses that were part of the broker's catalog + // payload + for _, payloadServiceClass := range payloadServiceClasses { + existingServiceClass, _ := existingServiceClassMap[payloadServiceClass.Name] + delete(existingServiceClassMap, payloadServiceClass.Name) - glog.V(4).Infof( - "%s %q: reconciling ClusterServicePlan (K8S: %q ExternalName: %q)", - typeCSB, broker.Name, payloadServicePlan.Name, payloadServicePlan.Spec.ExternalName, - ) - if err := c.reconcileClusterServicePlanFromClusterServiceBrokerCatalog(broker, payloadServicePlan, existingServicePlan); err != nil { + glog.V(4).Info(pcb.Messagef("Reconciling %s", pretty.ClusterServiceClassName(payloadServiceClass))) + if err := c.reconcileClusterServiceClassFromClusterServiceBrokerCatalog(broker, payloadServiceClass, existingServiceClass); err != nil { s := fmt.Sprintf( - "Error reconciling ClusterServicePlan (K8S: %q ExternalName: %q): %s", - payloadServicePlan.Name, payloadServicePlan.Spec.ExternalName, err, - ) - glog.Warningf( - "%s %q: %s", - typeCSB, broker.Name, s, + "Error reconciling %s (broker %q): %s", + pretty.ClusterServiceClassName(payloadServiceClass), broker.Name, err, ) + glog.Warning(pcb.Message(s)) c.recorder.Eventf(broker, corev1.EventTypeWarning, errorSyncingCatalogReason, s) - c.updateClusterServiceBrokerCondition(broker, v1beta1.ServiceBrokerConditionReady, v1beta1.ConditionFalse, errorSyncingCatalogReason, - errorSyncingCatalogMessage+s) + if err := c.updateClusterServiceBrokerCondition(broker, v1beta1.ServiceBrokerConditionReady, v1beta1.ConditionFalse, errorSyncingCatalogReason, + errorSyncingCatalogMessage+s); err != nil { + return err + } return err } - glog.V(5).Infof( - "%s %q: Reconciled ClusterServicePlan (K8S: %q ExternalName: %q)", - typeCSB, broker.Name, payloadServicePlan.Name, payloadServicePlan.Spec.ExternalName, - ) + + glog.V(5).Info(pcb.Messagef("Reconciled %s", pretty.ClusterServiceClassName(payloadServiceClass))) } - // handle the servicePlans that were not in the broker's payload; - // mark these as deleted - for _, existingServicePlan := range existingServicePlanMap { - if existingServicePlan.Status.RemovedFromBrokerCatalog { + // handle the serviceClasses that were not in the broker's payload; + // mark these as having been removed from the broker's catalog + for _, existingServiceClass := range existingServiceClassMap { + if existingServiceClass.Status.RemovedFromBrokerCatalog { continue } - glog.V(4).Infof( - "%s %q: ClusterServicePlan (K8S: %q ExternalName: %q) has been removed from broker's catalog; marking", - typeCSB, broker.Name, existingServicePlan.Name, existingServicePlan.Spec.ExternalName, - ) - existingServicePlan.Status.RemovedFromBrokerCatalog = true - _, err := c.serviceCatalogClient.ClusterServicePlans().UpdateStatus(existingServicePlan) + + glog.V(4).Info(pcb.Messagef("%s has been removed from broker's catalog; marking", pretty.ClusterServiceClassName(existingServiceClass))) + existingServiceClass.Status.RemovedFromBrokerCatalog = true + _, err := c.serviceCatalogClient.ClusterServiceClasses().UpdateStatus(existingServiceClass) if err != nil { s := fmt.Sprintf( - "Error updating status of ClusterServicePlan (K8S: %q ExternalName: %q): %v", - existingServicePlan.Name, - existingServicePlan.Spec.ExternalName, - err, - ) - glog.Warningf( - "%s %q: %s", - typeCSB, broker.Name, s, + "Error updating status of %s: %v", + pretty.ClusterServiceClassName(existingServiceClass), err, ) + glog.Warning(pcb.Message(s)) c.recorder.Eventf(broker, corev1.EventTypeWarning, errorSyncingCatalogReason, s) if err := c.updateClusterServiceBrokerCondition(broker, v1beta1.ServiceBrokerConditionReady, v1beta1.ConditionFalse, errorSyncingCatalogReason, errorSyncingCatalogMessage+s); err != nil { @@ -396,63 +348,46 @@ func (c *controller) reconcileClusterServiceBroker(broker *v1beta1.ClusterServic } } - // reconcile the serviceClasses that were part of the broker's catalog - // payload - for _, payloadServiceClass := range payloadServiceClasses { - existingServiceClass, _ := existingServiceClassMap[payloadServiceClass.Name] - delete(existingServiceClassMap, payloadServiceClass.Name) + // reconcile the plans that were part of the broker's catalog payload + for _, payloadServicePlan := range payloadServicePlans { + existingServicePlan, _ := existingServicePlanMap[payloadServicePlan.Name] + delete(existingServicePlanMap, payloadServicePlan.Name) glog.V(4).Infof( - "%s %q: Reconciling ClusterServiceClass (K8S: %q ExternalName: %q)", - typeCSB, broker.Name, payloadServiceClass.Name, payloadServiceClass.Spec.ExternalName, + "ClusterServiceBroker %q: reconciling %s", + broker.Name, pretty.ClusterServicePlanName(payloadServicePlan), ) - if err := c.reconcileClusterServiceClassFromClusterServiceBrokerCatalog(broker, payloadServiceClass, existingServiceClass); err != nil { + if err := c.reconcileClusterServicePlanFromClusterServiceBrokerCatalog(broker, payloadServicePlan, existingServicePlan); err != nil { s := fmt.Sprintf( - "Error reconciling ClusterServiceClass (K8S: %q ExternalName: %q) (broker %q): %s", - payloadServiceClass.Name, payloadServiceClass.Spec.ExternalName, broker.Name, err, - ) - glog.Warningf( - `%s %q: %s`, - typeCSB, broker.Name, s, + "Error reconciling %s: %s", + pretty.ClusterServicePlanName(payloadServicePlan), err, ) + glog.Warning(pcb.Message(s)) c.recorder.Eventf(broker, corev1.EventTypeWarning, errorSyncingCatalogReason, s) - if err := c.updateClusterServiceBrokerCondition(broker, v1beta1.ServiceBrokerConditionReady, v1beta1.ConditionFalse, errorSyncingCatalogReason, - errorSyncingCatalogMessage+s); err != nil { - return err - } + c.updateClusterServiceBrokerCondition(broker, v1beta1.ServiceBrokerConditionReady, v1beta1.ConditionFalse, errorSyncingCatalogReason, + errorSyncingCatalogMessage+s) return err } + glog.V(5).Info(pcb.Messagef("Reconciled %s", pretty.ClusterServicePlanName(payloadServicePlan))) - glog.V(5).Infof( - "%s %q: Reconciled ClusterServiceClass (K8S: %q ExternalName: %q)", - typeCSB, broker.Name, payloadServiceClass.Name, payloadServiceClass.Spec.ExternalName, - ) } - // handle the serviceClasses that were not in the broker's payload; - // mark these as having been removed from the broker's catalog - for _, existingServiceClass := range existingServiceClassMap { - if existingServiceClass.Status.RemovedFromBrokerCatalog { + // handle the servicePlans that were not in the broker's payload; + // mark these as deleted + for _, existingServicePlan := range existingServicePlanMap { + if existingServicePlan.Status.RemovedFromBrokerCatalog { continue } - - glog.V(4).Infof( - "%s %q: ClusterServiceClass (K8S: %q ExternalName: %q) has been removed from broker's catalog; marking", - typeCSB, broker.Name, existingServiceClass.Name, existingServiceClass.Spec.ExternalName, - ) - existingServiceClass.Status.RemovedFromBrokerCatalog = true - _, err := c.serviceCatalogClient.ClusterServiceClasses().UpdateStatus(existingServiceClass) + glog.V(4).Info(pcb.Messagef("%s has been removed from broker's catalog; marking", pretty.ClusterServicePlanName(existingServicePlan))) + existingServicePlan.Status.RemovedFromBrokerCatalog = true + _, err := c.serviceCatalogClient.ClusterServicePlans().UpdateStatus(existingServicePlan) if err != nil { s := fmt.Sprintf( - "Error updating status of ClusterServiceClass (K8S: %q ExternalName: %q): %v", - existingServiceClass.Name, - existingServiceClass.Spec.ExternalName, + "Error updating status of %s: %v", + pretty.ClusterServicePlanName(existingServicePlan), err, ) - glog.Warningf( - "%s %q: %s", - typeCSB, broker.Name, s, - ) + glog.Warning(pcb.Message(s)) c.recorder.Eventf(broker, corev1.EventTypeWarning, errorSyncingCatalogReason, s) if err := c.updateClusterServiceBrokerCondition(broker, v1beta1.ServiceBrokerConditionReady, v1beta1.ConditionFalse, errorSyncingCatalogReason, errorSyncingCatalogMessage+s); err != nil { @@ -477,36 +412,21 @@ func (c *controller) reconcileClusterServiceBroker(broker *v1beta1.ClusterServic // and returned early. If we reach this point, we're dealing with an update // that's actually a soft delete-- i.e. we have some finalization to do. if finalizers := sets.NewString(broker.Finalizers...); finalizers.Has(v1beta1.FinalizerServiceCatalog) { - glog.V(4).Infof( - "%s %q: finalizing", - typeCSB, broker.Name, - ) + glog.V(4).Info(pcb.Message("Finalizing")) existingServiceClasses, existingServicePlans, err := c.getCurrentServiceClassesAndPlansForBroker(broker) if err != nil { return err } - glog.V(4).Infof( - "%s %q: found %d ClusterServiceClasses and %d ClusterServicePlans to delete", - typeCSB, broker.Name, len(existingServiceClasses), len(existingServicePlans), - ) + glog.V(4).Info(pcb.Messagef("Found %d ClusterServiceClasses and %d ClusterServicePlans to delete", len(existingServiceClasses), len(existingServicePlans))) for _, plan := range existingServicePlans { - glog.V(4).Infof( - "%s %q: deleting ClusterServicePlan (K8S: %q ExternalName: %q)", - typeCSB, broker.Name, plan.Name, plan.Spec.ExternalName, - ) + glog.V(4).Info(pcb.Messagef("Deleting %s", pretty.ClusterServicePlanName(&plan))) err := c.serviceCatalogClient.ClusterServicePlans().Delete(plan.Name, &metav1.DeleteOptions{}) if err != nil && !errors.IsNotFound(err) { - s := fmt.Sprintf( - "Error deleting ClusterServicePlan (K8S: %q ExternalName: %q): %s", - plan.Name, plan.Spec.ExternalName, err, - ) - glog.Warningf( - "%s %q: %s", - typeCSB, broker.Name, s, - ) + s := fmt.Sprintf("Error deleting %s: %s", pretty.ClusterServicePlanName(&plan), err) + glog.Warning(pcb.Message(s)) c.updateClusterServiceBrokerCondition( broker, v1beta1.ServiceBrokerConditionReady, @@ -520,20 +440,11 @@ func (c *controller) reconcileClusterServiceBroker(broker *v1beta1.ClusterServic } for _, svcClass := range existingServiceClasses { - glog.V(4).Infof( - "%s %q: deleting ClusterServiceClass (K8S: %q ExternalName: %q)", - typeCSB, broker.Name, svcClass.Name, svcClass.Spec.ExternalName, - ) + glog.V(4).Info(pcb.Messagef("Deleting %s", pretty.ClusterServiceClassName(&svcClass))) err = c.serviceCatalogClient.ClusterServiceClasses().Delete(svcClass.Name, &metav1.DeleteOptions{}) if err != nil && !errors.IsNotFound(err) { - s := fmt.Sprintf( - "Error deleting ClusterServiceClass (K8S: %q ExternalName: %q): %s", - svcClass.Name, svcClass.Spec.ExternalName, err, - ) - glog.Warningf( - "%s %q: %s", - typeCSB, broker.Name, s, - ) + s := fmt.Sprintf("Error deleting %s: %s", pretty.ClusterServiceClassName(&svcClass), err) + glog.Warning(pcb.Message(s)) c.recorder.Eventf(broker, corev1.EventTypeWarning, errorDeletingClusterServiceClassReason, "%v %v", errorDeletingClusterServiceClassMessage, s) if err := c.updateClusterServiceBrokerCondition( broker, @@ -562,7 +473,7 @@ func (c *controller) reconcileClusterServiceBroker(broker *v1beta1.ClusterServic c.updateClusterServiceBrokerFinalizers(broker, finalizers.List()) c.recorder.Eventf(broker, corev1.EventTypeNormal, successClusterServiceBrokerDeletedReason, successClusterServiceBrokerDeletedMessage, broker.Name) - glog.V(5).Infof("%s %q: Successfully deleted", typeCSB, broker.Name) + glog.V(5).Info(pcb.Message("Successfully deleted")) return nil } @@ -575,6 +486,7 @@ func (c *controller) reconcileClusterServiceBroker(broker *v1beta1.ClusterServic // catalog payload. The existingServiceClass parameter is the serviceClass // that already exists for the given broker with this serviceClass' k8s name. func (c *controller) reconcileClusterServiceClassFromClusterServiceBrokerCatalog(broker *v1beta1.ClusterServiceBroker, serviceClass, existingServiceClass *v1beta1.ClusterServiceClass) error { + pcb := pretty.NewContextBuilder(pretty.ClusterServiceBroker, "", broker.Name) serviceClass.Spec.ClusterServiceBrokerName = broker.Name if existingServiceClass == nil { @@ -590,24 +502,17 @@ func (c *controller) reconcileClusterServiceClassFromClusterServiceBrokerCatalog // not already passed one; the following if statement will almost // certainly evaluate to true. if otherServiceClass.Spec.ClusterServiceBrokerName != broker.Name { - errMsg := fmt.Sprintf( - "%s %q: ClusterServiceClass (K8S: %q ExternalName: %q) already exists for Broker %q", - typeCSB, broker.Name, serviceClass.Name, serviceClass.Spec.ExternalName, otherServiceClass.Spec.ClusterServiceBrokerName, + errMsg := fmt.Sprintf("%s already exists for Broker %q", + pretty.ClusterServiceClassName(serviceClass), otherServiceClass.Spec.ClusterServiceBrokerName, ) - glog.Error(errMsg) + glog.Error(pcb.Message(errMsg)) return fmt.Errorf(errMsg) } } - glog.V(5).Infof( - "%s %q: fresh ClusterServiceClass (K8S: %q ExternalName: %q); creating", - typeCSB, broker.Name, serviceClass.Name, serviceClass.Spec.ExternalName, - ) + glog.V(5).Info(pcb.Messagef("Fresh %s; creating", pretty.ClusterServiceClassName(serviceClass))) if _, err := c.serviceCatalogClient.ClusterServiceClasses().Create(serviceClass); err != nil { - glog.Errorf( - "%s %q: Error creating ClusterServiceClass (K8S: %q ExternalName: %q): %v", - typeCSB, broker.Name, serviceClass.Name, serviceClass.Spec.ExternalName, err, - ) + glog.Error(pcb.Messagef("Error creating %s: %v", pretty.ClusterServiceClassName(serviceClass), err)) return err } @@ -616,17 +521,14 @@ func (c *controller) reconcileClusterServiceClassFromClusterServiceBrokerCatalog if existingServiceClass.Spec.ExternalID != serviceClass.Spec.ExternalID { errMsg := fmt.Sprintf( - "%s %q: ClusterServiceClass (K8S: %q ExternalName: %q) already exists with OSB guid %q, received different guid %q", - typeCSB, broker.Name, serviceClass.Name, serviceClass.Spec.ExternalName, existingServiceClass.Name, serviceClass.Name, + "%s already exists with OSB guid %q, received different guid %q", + pretty.ClusterServiceClassName(serviceClass), existingServiceClass.Name, serviceClass.Name, ) - glog.Error(errMsg) + glog.Error(pcb.Message(errMsg)) return fmt.Errorf(errMsg) } - glog.V(5).Infof( - "%s %q: Found existing ClusterServiceClass (K8S: %q ExternalName: %q); updating", - typeCSB, broker.Name, serviceClass.Name, serviceClass.Spec.ExternalName, - ) + glog.V(5).Info(pcb.Messagef("Found existing %s; updating", pretty.ClusterServiceClassName(serviceClass))) // There was an existing service class -- project the update onto it and // update it. @@ -645,10 +547,7 @@ func (c *controller) reconcileClusterServiceClassFromClusterServiceBrokerCatalog toUpdate.Spec.ExternalMetadata = serviceClass.Spec.ExternalMetadata if _, err := c.serviceCatalogClient.ClusterServiceClasses().Update(toUpdate); err != nil { - glog.Errorf( - "%s %q: Error updating ClusterServiceClass (K8S: %q ExternalName: %q): %v", - typeCSB, broker.Name, serviceClass.Name, serviceClass.Spec.ExternalName, err, - ) + glog.Error(pcb.Messagef("Error updating %s: %v", pretty.ClusterServiceClassName(serviceClass), err)) return err } @@ -658,6 +557,7 @@ func (c *controller) reconcileClusterServiceClassFromClusterServiceBrokerCatalog // reconcileClusterServicePlanFromClusterServiceBrokerCatalog reconciles a // ServicePlan after the ServiceClass's catalog has been re-listed. func (c *controller) reconcileClusterServicePlanFromClusterServiceBrokerCatalog(broker *v1beta1.ClusterServiceBroker, servicePlan, existingServicePlan *v1beta1.ClusterServicePlan) error { + pcb := pretty.NewContextBuilder(pretty.ClusterServiceBroker, "", broker.Name) servicePlan.Spec.ClusterServiceBrokerName = broker.Name if existingServicePlan == nil { @@ -674,13 +574,10 @@ func (c *controller) reconcileClusterServicePlanFromClusterServiceBrokerCatalog( // certainly evaluate to true. if otherServicePlan.Spec.ClusterServiceBrokerName != broker.Name { errMsg := fmt.Sprintf( - "ClusterServicePlan (K8S: %q ExternalName: %q) already exists for Broker %q", - servicePlan.Name, servicePlan.Spec.ExternalName, otherServicePlan.Spec.ClusterServiceBrokerName, - ) - glog.Errorf( - `%s %q: %s`, - typeCSB, broker.Name, errMsg, + "%s already exists for Broker %q", + pretty.ClusterServicePlanName(servicePlan), otherServicePlan.Spec.ClusterServiceBrokerName, ) + glog.Error(pcb.Message(errMsg)) return fmt.Errorf(errMsg) } } @@ -688,10 +585,7 @@ func (c *controller) reconcileClusterServicePlanFromClusterServiceBrokerCatalog( // An error returned from a lister Get call means that the object does // not exist. Create a new ClusterServicePlan. if _, err := c.serviceCatalogClient.ClusterServicePlans().Create(servicePlan); err != nil { - glog.Errorf( - "%s %q: Error creating ClusterServicePlan (K8S: %q, ExternalName: %q): %v", - typeCSB, broker.Name, servicePlan.Name, servicePlan.Spec.ExternalName, err, - ) + glog.Error(pcb.Messagef("Error creating %s: %v", pretty.ClusterServicePlanName(servicePlan), err)) return err } @@ -700,20 +594,14 @@ func (c *controller) reconcileClusterServicePlanFromClusterServiceBrokerCatalog( if existingServicePlan.Spec.ExternalID != servicePlan.Spec.ExternalID { errMsg := fmt.Sprintf( - "ClusterServicePlan (K8S: %q ExternalName: %q) already exists with OSB guid %q, received different guid %q", - servicePlan.Name, servicePlan.Spec.ExternalName, existingServicePlan.Spec.ExternalID, servicePlan.Spec.ExternalID, - ) - glog.Error( - "%s %q: %s", - typeCSB, broker.Name, errMsg, + "%s already exists with OSB guid %q, received different guid %q", + pretty.ClusterServicePlanName(servicePlan), existingServicePlan.Spec.ExternalID, servicePlan.Spec.ExternalID, ) + glog.Error(pcb.Message(errMsg)) return fmt.Errorf(errMsg) } - glog.V(5).Infof( - "%s %q: Found existing ClusterServicePlan (K8S: %q ExternalName: %q); updating", - typeCSB, broker.Name, servicePlan.Name, servicePlan.Spec.ExternalName, - ) + glog.V(5).Info(pcb.Messagef("Found existing %s; updating", pretty.ClusterServicePlanName(servicePlan))) // There was an existing service plan -- project the update onto it and // update it. @@ -733,10 +621,7 @@ func (c *controller) reconcileClusterServicePlanFromClusterServiceBrokerCatalog( toUpdate.Spec.ServiceBindingCreateParameterSchema = servicePlan.Spec.ServiceBindingCreateParameterSchema if _, err := c.serviceCatalogClient.ClusterServicePlans().Update(toUpdate); err != nil { - glog.Errorf( - "%s %q: Error updating ClusterServicePlan (K8S: %q ExternalName: %q): %v", - typeCSB, broker.Name, servicePlan.Name, servicePlan.Spec.ExternalName, err, - ) + glog.Error(pcb.Messagef("Error updating %s: %v", pretty.ClusterServicePlanName(servicePlan), err)) return err } @@ -746,6 +631,7 @@ func (c *controller) reconcileClusterServicePlanFromClusterServiceBrokerCatalog( // updateClusterServiceBrokerCondition updates the ready condition for the given Broker // with the given status, reason, and message. func (c *controller) updateClusterServiceBrokerCondition(broker *v1beta1.ClusterServiceBroker, conditionType v1beta1.ServiceBrokerConditionType, status v1beta1.ConditionStatus, reason, message string) error { + pcb := pretty.NewContextBuilder(pretty.ClusterServiceBroker, "", broker.Name) clone, err := api.Scheme.DeepCopy(broker) if err != nil { return err @@ -761,20 +647,17 @@ func (c *controller) updateClusterServiceBrokerCondition(broker *v1beta1.Cluster t := time.Now() if len(broker.Status.Conditions) == 0 { - glog.Infof( - "%s %q: Setting lastTransitionTime for condition %q to %v", - typeCSB, broker.Name, conditionType, t, - ) + glog.Info(pcb.Messagef("Setting lastTransitionTime for condition %q to %v", conditionType, t)) newCondition.LastTransitionTime = metav1.NewTime(t) toUpdate.Status.Conditions = []v1beta1.ServiceBrokerCondition{newCondition} } else { for i, cond := range broker.Status.Conditions { if cond.Type == conditionType { if cond.Status != newCondition.Status { - glog.Infof( - "%s %q: Found status change for condition %q: %q -> %q; setting lastTransitionTime to %v", - typeCSB, broker.Name, conditionType, cond.Status, status, t, - ) + glog.Info(pcb.Messagef( + "Found status change for condition %q: %q -> %q; setting lastTransitionTime to %v", + conditionType, cond.Status, status, t, + )) newCondition.LastTransitionTime = metav1.NewTime(t) } else { newCondition.LastTransitionTime = cond.LastTransitionTime @@ -792,20 +675,12 @@ func (c *controller) updateClusterServiceBrokerCondition(broker *v1beta1.Cluster toUpdate.Status.ReconciledGeneration = toUpdate.Generation } - glog.V(4).Infof("%s %q: Updating ready condition to %v", - typeCSB, broker.Name, status, - ) + glog.V(4).Info(pcb.Messagef("Updating ready condition to %v", status)) _, err = c.serviceCatalogClient.ClusterServiceBrokers().UpdateStatus(toUpdate) if err != nil { - glog.Errorf( - "%s %q: Error updating ready condition: %v", - typeCSB, broker.Name, err, - ) + glog.Error(pcb.Messagef("Error updating ready condition: %v", err)) } else { - glog.V(5).Infof( - "%s %q: Updated ready condition to %v", - typeCSB, broker.Name, status, - ) + glog.V(5).Info(pcb.Messagef("Updated ready condition to %v", status)) } return err @@ -815,16 +690,14 @@ func (c *controller) updateClusterServiceBrokerCondition(broker *v1beta1.Cluster func (c *controller) updateClusterServiceBrokerFinalizers( broker *v1beta1.ClusterServiceBroker, finalizers []string) error { + pcb := pretty.NewContextBuilder(pretty.ClusterServiceBroker, "", broker.Name) // Get the latest version of the broker so that we can avoid conflicts // (since we have probably just updated the status of the broker and are // now removing the last finalizer). broker, err := c.serviceCatalogClient.ClusterServiceBrokers().Get(broker.Name, metav1.GetOptions{}) if err != nil { - glog.Errorf( - "%s %q: Error finalizing: %v", - typeCSB, broker.Name, err, - ) + glog.Error(pcb.Messagef("Error finalizing: %v", err)) } clone, err := api.Scheme.DeepCopy(broker) @@ -835,15 +708,12 @@ func (c *controller) updateClusterServiceBrokerFinalizers( toUpdate.Finalizers = finalizers - logContext := fmt.Sprintf( - "%s %q: updating finalizers to %v", - typeCSB, broker.Name, finalizers, - ) + logContext := fmt.Sprint(pcb.Messagef("Updating finalizers to %v", finalizers)) - glog.V(4).Infof("Updating %v", logContext) + glog.V(4).Info(pcb.Messagef("Updating %v", logContext)) _, err = c.serviceCatalogClient.ClusterServiceBrokers().UpdateStatus(toUpdate) if err != nil { - glog.Errorf("Error updating %v: %v", logContext, err) + glog.Error(pcb.Messagef("Error updating %v: %v", logContext, err)) } return err } diff --git a/pkg/controller/controller_broker_test.go b/pkg/controller/controller_broker_test.go index d5986278ef7c..64f3d419ba44 100644 --- a/pkg/controller/controller_broker_test.go +++ b/pkg/controller/controller_broker_test.go @@ -26,6 +26,7 @@ import ( fakeosb "github.com/pmorie/go-open-service-broker-client/v2/fake" "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + "github.com/kubernetes-incubator/service-catalog/test/fake" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" @@ -36,7 +37,6 @@ import ( "strings" "github.com/kubernetes-incubator/service-catalog/pkg/api" - "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1" clientgotesting "k8s.io/client-go/testing" ) @@ -232,9 +232,9 @@ func TestReconcileClusterServiceBrokerExistingServiceClassAndServicePlan(t *test assertNumberOfActions(t, actions, 6) assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions) - assertCreate(t, actions[2], testClusterServicePlan) - assertCreate(t, actions[3], testClusterServicePlanNonbindable) - assertUpdate(t, actions[4], testClusterServiceClass) + assertUpdate(t, actions[2], testClusterServiceClass) + assertCreate(t, actions[3], testClusterServicePlan) + assertCreate(t, actions[4], testClusterServicePlanNonbindable) // 4 update action for broker status subresource updatedClusterServiceBroker := assertUpdateStatus(t, actions[5], getTestClusterServiceBroker()) @@ -281,10 +281,10 @@ func TestReconcileClusterServiceBrokerRemovedClusterServiceClass(t *testing.T) { assertNumberOfActions(t, actions, 7) assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions) - assertCreate(t, actions[2], testClusterServicePlan) - assertCreate(t, actions[3], testClusterServicePlanNonbindable) - assertUpdate(t, actions[4], testClusterServiceClass) - assertUpdateStatus(t, actions[5], testRemovedClusterServiceClass) + assertUpdate(t, actions[2], testClusterServiceClass) + assertUpdateStatus(t, actions[3], testRemovedClusterServiceClass) + assertCreate(t, actions[4], testClusterServicePlan) + assertCreate(t, actions[5], testClusterServicePlanNonbindable) updatedClusterServiceBroker := assertUpdateStatus(t, actions[6], getTestClusterServiceBroker()) assertClusterServiceBrokerReadyTrue(t, updatedClusterServiceBroker) @@ -336,10 +336,10 @@ func TestReconcileClusterServiceBrokerRemovedClusterServicePlan(t *testing.T) { assertNumberOfActions(t, actions, 7) assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions) - assertCreate(t, actions[2], testClusterServicePlan) - assertCreate(t, actions[3], testClusterServicePlanNonbindable) - assertUpdateStatus(t, actions[4], testRemovedClusterServicePlan) - assertUpdate(t, actions[5], testClusterServiceClass) + assertUpdate(t, actions[2], testClusterServiceClass) + assertCreate(t, actions[3], testClusterServicePlan) + assertCreate(t, actions[4], testClusterServicePlanNonbindable) + assertUpdateStatus(t, actions[5], testRemovedClusterServicePlan) updatedClusterServiceBroker := assertUpdateStatus(t, actions[6], getTestClusterServiceBroker()) assertClusterServiceBrokerReadyTrue(t, updatedClusterServiceBroker) @@ -358,15 +358,54 @@ func TestReconcileClusterServiceBrokerExistingClusterServiceClassDifferentBroker testClusterServiceClass := getTestClusterServiceClass() testClusterServiceClass.Spec.ClusterServiceBrokerName = "notTheSame" + sharedInformers.ClusterServiceClasses().Informer().GetStore().Add(testClusterServiceClass) + + if err := testController.reconcileClusterServiceBroker(getTestClusterServiceBroker()); err == nil { + t.Fatal("The same service class should not belong to two different brokers.") + } + + brokerActions := fakeClusterServiceBrokerClient.Actions() + assertNumberOfClusterServiceBrokerActions(t, brokerActions, 1) + assertGetCatalog(t, brokerActions[0]) + + actions := fakeCatalogClient.Actions() + assertNumberOfActions(t, actions, 3) + + listRestrictions := clientgotesting.ListRestrictions{ + Labels: labels.Everything(), + Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", "test-broker"), + } + assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) + assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions) + updatedClusterServiceBroker := assertUpdateStatus(t, actions[2], getTestClusterServiceBroker()) + assertClusterServiceBrokerReadyFalse(t, updatedClusterServiceBroker) + + // verify no kube resources created + kubeActions := fakeKubeClient.Actions() + assertNumberOfActions(t, kubeActions, 0) + + events := getRecordedEvents(testController) + assertNumEvents(t, events, 1) + + expectedEvent := corev1.EventTypeWarning + " " + errorSyncingCatalogReason + ` Error reconciling ClusterServiceClass (K8S: "SCGUID" ExternalName: "test-serviceclass") (broker "test-broker"): ClusterServiceClass (K8S: "SCGUID" ExternalName: "test-serviceclass") already exists for Broker "notTheSame"` + if e, a := expectedEvent, events[0]; e != a { + t.Fatalf("Received unexpected event; expected\n%v, got\n%v", e, a) + } +} + +// TestReconcileClusterServiceBrokerExistingClusterServicePlanDifferentClass simulates catalog +// refresh where broker lists a service plan which matches an existing, already +// cataloged service plan but the plan points to a different ClusterServiceClass. Results in an error. +func TestReconcileClusterServiceBrokerExistingClusterServicePlanDifferentClass(t *testing.T) { + fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, sharedInformers := newTestController(t, getTestCatalogConfig()) + testClusterServicePlan := getTestClusterServicePlan() testClusterServicePlan.Spec.ClusterServiceBrokerName = "notTheSame" + testClusterServicePlan.Spec.ClusterServiceClassRef = v1beta1.ClusterObjectReference{ + Name: "notTheSameClass", + } - testClusterServicePlanNonbindable := getTestClusterServicePlanNonbindable() - testClusterServicePlanNonbindable.Spec.ClusterServiceBrokerName = "notTheSame" - - sharedInformers.ClusterServiceClasses().Informer().GetStore().Add(testClusterServiceClass) sharedInformers.ClusterServicePlans().Informer().GetStore().Add(testClusterServicePlan) - sharedInformers.ClusterServicePlans().Informer().GetStore().Add(testClusterServicePlanNonbindable) if err := testController.reconcileClusterServiceBroker(getTestClusterServiceBroker()); err == nil { t.Fatal("The same service class should not belong to two different brokers.") @@ -377,7 +416,7 @@ func TestReconcileClusterServiceBrokerExistingClusterServiceClassDifferentBroker assertGetCatalog(t, brokerActions[0]) actions := fakeCatalogClient.Actions() - assertNumberOfActions(t, actions, 3) + assertNumberOfActions(t, actions, 4) listRestrictions := clientgotesting.ListRestrictions{ Labels: labels.Everything(), @@ -385,7 +424,8 @@ func TestReconcileClusterServiceBrokerExistingClusterServiceClassDifferentBroker } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions) - updatedClusterServiceBroker := assertUpdateStatus(t, actions[2], getTestClusterServiceBroker()) + assertCreate(t, actions[2], getTestClusterServiceClass()) + updatedClusterServiceBroker := assertUpdateStatus(t, actions[3], getTestClusterServiceBroker()) assertClusterServiceBrokerReadyFalse(t, updatedClusterServiceBroker) // verify no kube resources created @@ -512,7 +552,7 @@ func TestReconcileClusterServiceBrokerErrorFetchingCatalog(t *testing.T) { events := getRecordedEvents(testController) assertNumEvents(t, events, 1) - expectedEvent := corev1.EventTypeWarning + " " + errorFetchingCatalogReason + " " + `ClusterServiceBroker "test-broker": Error getting broker catalog: ooops` + expectedEvent := corev1.EventTypeWarning + " " + errorFetchingCatalogReason + " " + "Error getting broker catalog: ooops" if e, a := expectedEvent, events[0]; e != a { t.Fatalf("Received unexpected event: %v", a) } @@ -558,7 +598,7 @@ func TestReconcileClusterServiceBrokerZeroServices(t *testing.T) { func TestReconcileClusterServiceBrokerWithAuth(t *testing.T) { basicAuthInfo := &v1beta1.ServiceBrokerAuthInfo{ Basic: &v1beta1.BasicAuthConfig{ - SecretRef: &v1.ObjectReference{ + SecretRef: &v1beta1.ObjectReference{ Namespace: "test-ns", Name: "auth-secret", }, @@ -566,19 +606,19 @@ func TestReconcileClusterServiceBrokerWithAuth(t *testing.T) { } bearerAuthInfo := &v1beta1.ServiceBrokerAuthInfo{ Bearer: &v1beta1.BearerTokenAuthConfig{ - SecretRef: &v1.ObjectReference{ + SecretRef: &v1beta1.ObjectReference{ Namespace: "test-ns", Name: "auth-secret", }, }, } - basicAuthSecret := &v1.Secret{ + basicAuthSecret := &corev1.Secret{ Data: map[string][]byte{ v1beta1.BasicAuthUsernameKey: []byte("foo"), v1beta1.BasicAuthPasswordKey: []byte("bar"), }, } - bearerAuthSecret := &v1.Secret{ + bearerAuthSecret := &corev1.Secret{ Data: map[string][]byte{ v1beta1.BearerTokenKey: []byte("token"), }, @@ -594,7 +634,7 @@ func TestReconcileClusterServiceBrokerWithAuth(t *testing.T) { cases := []struct { name string authInfo *v1beta1.ServiceBrokerAuthInfo - secret *v1.Secret + secret *corev1.Secret shouldSucceed bool }{ { @@ -643,7 +683,7 @@ func TestReconcileClusterServiceBrokerWithAuth(t *testing.T) { } } -func testReconcileClusterServiceBrokerWithAuth(t *testing.T, authInfo *v1beta1.ServiceBrokerAuthInfo, secret *v1.Secret, shouldSucceed bool) { +func testReconcileClusterServiceBrokerWithAuth(t *testing.T, authInfo *v1beta1.ServiceBrokerAuthInfo, secret *corev1.Secret, shouldSucceed bool) { fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, _ := newTestController(t, fakeosb.FakeClientConfiguration{}) broker := getTestClusterServiceBrokerWithAuth(authInfo) @@ -709,10 +749,10 @@ func testReconcileClusterServiceBrokerWithAuth(t *testing.T, authInfo *v1beta1.S if shouldSucceed { expectedEvent = corev1.EventTypeNormal + " " + successFetchedCatalogReason + " " + successFetchedCatalogMessage } else { - expectedEvent = corev1.EventTypeWarning + " " + errorAuthCredentialsReason + " " + `ClusterServiceBroker "test-broker": Error getting broker auth credentials` + expectedEvent = corev1.EventTypeWarning + " " + errorAuthCredentialsReason + " " + `Error getting broker auth credentials` } if e, a := expectedEvent, events[0]; !strings.HasPrefix(a, e) { - t.Fatalf("Received unexpected event: %v", a) + t.Fatalf("Received unexpected event,\nexpected:\t%s \ngot:\t%s", e, a) } } @@ -737,7 +777,7 @@ func TestReconcileClusterServiceBrokerWithReconcileError(t *testing.T) { assertGetCatalog(t, brokerActions[0]) actions := fakeCatalogClient.Actions() - assertNumberOfActions(t, actions, 6) + assertNumberOfActions(t, actions, 4) listRestrictions := clientgotesting.ListRestrictions{ Labels: labels.Everything(), @@ -745,12 +785,10 @@ func TestReconcileClusterServiceBrokerWithReconcileError(t *testing.T) { } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions) - assertCreate(t, actions[2], getTestClusterServicePlan()) - assertCreate(t, actions[3], getTestClusterServicePlanNonbindable()) // the two plans in the catalog as two separate actions - createSCAction := actions[4].(clientgotesting.CreateAction) + createSCAction := actions[2].(clientgotesting.CreateAction) createdSC, ok := createSCAction.GetObject().(*v1beta1.ClusterServiceClass) if !ok { t.Fatalf("couldn't convert to a ClusterServiceClass: %+v", createSCAction.GetObject()) @@ -758,7 +796,7 @@ func TestReconcileClusterServiceBrokerWithReconcileError(t *testing.T) { if e, a := getTestClusterServiceClass(), 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[5], broker) + updatedClusterServiceBroker := assertUpdateStatus(t, actions[3], broker) assertClusterServiceBrokerReadyFalse(t, updatedClusterServiceBroker) kubeActions := fakeKubeClient.Actions() @@ -807,9 +845,9 @@ func TestReconcileClusterServiceBrokerSuccessOnFinalRetry(t *testing.T) { assertList(t, actions[1], &v1beta1.ClusterServiceClass{}, listRestrictions) assertList(t, actions[2], &v1beta1.ClusterServicePlan{}, listRestrictions) - assertCreate(t, actions[3], getTestClusterServicePlan()) - assertCreate(t, actions[4], getTestClusterServicePlanNonbindable()) - assertCreate(t, actions[5], testClusterServiceClass) + assertCreate(t, actions[3], testClusterServiceClass) + assertCreate(t, actions[4], getTestClusterServicePlan()) + assertCreate(t, actions[5], getTestClusterServicePlanNonbindable()) updatedClusterServiceBroker = assertUpdateStatus(t, actions[6], getTestClusterServiceBroker()) assertClusterServiceBrokerReadyTrue(t, updatedClusterServiceBroker) @@ -904,9 +942,9 @@ func TestReconcileClusterServiceBrokerWithStatusUpdateError(t *testing.T) { assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions) - assertCreate(t, actions[2], getTestClusterServicePlan()) - assertCreate(t, actions[3], getTestClusterServicePlanNonbindable()) - assertCreate(t, actions[4], testClusterServiceClass) + assertCreate(t, actions[2], testClusterServiceClass) + assertCreate(t, actions[3], getTestClusterServicePlan()) + assertCreate(t, actions[4], getTestClusterServicePlanNonbindable()) // 4 update action for broker status subresource updatedClusterServiceBroker := assertUpdateStatus(t, actions[5], getTestClusterServiceBroker()) @@ -1051,3 +1089,102 @@ func TestUpdateServiceBrokerCondition(t *testing.T) { } } } + +func TestReconcileClusterServicePlanFromClusterServiceBrokerCatalog(t *testing.T) { + updatedPlan := func() *v1beta1.ClusterServicePlan { + p := getTestClusterServicePlan() + p.Spec.Description = "new-description" + p.Spec.ExternalName = "new-value" + p.Spec.Free = false + p.Spec.ExternalMetadata = &runtime.RawExtension{Raw: []byte(`{"field1": "value1"}`)} + p.Spec.ServiceInstanceCreateParameterSchema = &runtime.RawExtension{Raw: []byte(`{"field1": "value1"}`)} + p.Spec.ServiceInstanceUpdateParameterSchema = &runtime.RawExtension{Raw: []byte(`{"field1": "value1"}`)} + p.Spec.ServiceBindingCreateParameterSchema = &runtime.RawExtension{Raw: []byte(`{"field1": "value1"}`)} + + return p + } + + cases := []struct { + name string + newServicePlan *v1beta1.ClusterServicePlan + existingServicePlan *v1beta1.ClusterServicePlan + listerServicePlan *v1beta1.ClusterServicePlan + shouldError bool + errText *string + catalogClientPrepFunc func(*fake.Clientset) + catalogActionsCheckFunc func(t *testing.T, name string, actions []clientgotesting.Action) + }{ + { + name: "new plan", + newServicePlan: getTestClusterServicePlan(), + shouldError: false, + catalogActionsCheckFunc: func(t *testing.T, name string, actions []clientgotesting.Action) { + expectNumberOfActions(t, name, actions, 1) + expectCreate(t, name, actions[0], getTestClusterServicePlan()) + }, + }, + { + name: "exists, but for a different broker", + newServicePlan: getTestClusterServicePlan(), + existingServicePlan: getTestClusterServicePlan(), + listerServicePlan: func() *v1beta1.ClusterServicePlan { + p := getTestClusterServicePlan() + p.Spec.ClusterServiceBrokerName = "something-else" + return p + }(), + shouldError: true, + errText: strPtr(`ClusterServiceBroker "test-broker": ClusterServicePlan "test-plan" already exists for Broker "something-else"`), + }, + { + name: "plan update", + newServicePlan: updatedPlan(), + existingServicePlan: getTestClusterServicePlan(), + shouldError: false, + catalogActionsCheckFunc: func(t *testing.T, name string, actions []clientgotesting.Action) { + expectNumberOfActions(t, name, actions, 1) + expectUpdate(t, name, actions[0], updatedPlan()) + }, + }, + { + name: "plan update - failure", + newServicePlan: updatedPlan(), + existingServicePlan: getTestClusterServicePlan(), + catalogClientPrepFunc: func(client *fake.Clientset) { + client.AddReactor("update", "clusterserviceplans", func(action clientgotesting.Action) (bool, runtime.Object, error) { + return true, nil, errors.New("oops") + }) + }, + shouldError: true, + errText: strPtr("oops"), + }, + } + + broker := getTestClusterServiceBroker() + + for _, tc := range cases { + _, fakeCatalogClient, _, testController, sharedInformers := newTestController(t, noFakeActions()) + if tc.catalogClientPrepFunc != nil { + tc.catalogClientPrepFunc(fakeCatalogClient) + } + + if tc.listerServicePlan != nil { + sharedInformers.ClusterServicePlans().Informer().GetStore().Add(tc.listerServicePlan) + } + + err := testController.reconcileClusterServicePlanFromClusterServiceBrokerCatalog(broker, tc.newServicePlan, tc.existingServicePlan) + if err != nil { + if !tc.shouldError { + t.Errorf("%v: unexpected error from method under test: %v", tc.name, err) + continue + } else if tc.errText != nil && *tc.errText != err.Error() { + t.Errorf("%v: unexpected error text from method under test; expected %v, got %v", tc.name, tc.errText, err.Error()) + continue + } + } + + if tc.catalogActionsCheckFunc != nil { + actions := fakeCatalogClient.Actions() + tc.catalogActionsCheckFunc(t, tc.name, actions) + } + } +} diff --git a/pkg/controller/controller_events_test.go b/pkg/controller/controller_events_test.go index 0d949c0646c2..14207192d3a0 100644 --- a/pkg/controller/controller_events_test.go +++ b/pkg/controller/controller_events_test.go @@ -18,12 +18,20 @@ package controller import ( "fmt" + "strings" ) -func checkEvents(actual, expected []string) error { +func checkEventCounts(actual, expected []string) error { if len(actual) != len(expected) { return fmt.Errorf("expected %d events, got %d", len(expected), len(actual)) } + return nil +} + +func checkEvents(actual, expected []string) error { + if err := checkEventCounts(actual, expected); err != nil { + return err + } for i, actualEvt := range actual { if expectedEvt := expected[i]; actualEvt != expectedEvt { return fmt.Errorf("event %d: expected '%s', got '%s'", i, expectedEvt, actualEvt) @@ -31,3 +39,28 @@ func checkEvents(actual, expected []string) error { } return nil } + +func checkEventPrefixes(actual, expected []string) error { + if err := checkEventCounts(actual, expected); err != nil { + return err + } + for i, e := range expected { + a := actual[i] + if !strings.HasPrefix(a, e) { + return fmt.Errorf("received unexpected event prefix:\n %s", expectedGot(e, a)) + } + } + return nil +} + +func checkEventContains(actual, expected string) error { + if !strings.Contains(actual, expected) { + return fmt.Errorf("received unexpected event (contains):\n %s", expectedGot(expected, actual)) + } + + return nil +} + +func expectedGot(a ...interface{}) string { + return fmt.Sprintf("\nexpected:\n\t '%v',\ngot:\n\t '%v'", a...) +} diff --git a/pkg/controller/controller_instance.go b/pkg/controller/controller_instance.go index 12e708b8c70d..5c30bc5ca035 100644 --- a/pkg/controller/controller_instance.go +++ b/pkg/controller/controller_instance.go @@ -66,6 +66,10 @@ const ( errorNonexistentClusterServiceClassMessage string = "ReferencesNonexistentServiceClass" errorNonexistentClusterServicePlanReason string = "ReferencesNonexistentServicePlan" errorNonexistentClusterServiceBrokerReason string = "ReferencesNonexistentBroker" + errorDeletedClusterServiceClassReason string = "ReferencesDeletedServiceClass" + errorDeletedClusterServiceClassMessage string = "ReferencesDeletedServiceClass" + errorDeletedClusterServicePlanReason string = "ReferencesDeletedServicePlan" + errorDeletedClusterServicePlanMessage string = "ReferencesDeletedServicePlan" errorFindingNamespaceServiceInstanceReason string = "ErrorFindingNamespaceForInstance" errorOrphanMitigationFailedReason string = "OrphanMitigationFailed" @@ -370,8 +374,8 @@ func (c *controller) reconcileServiceInstanceDelete(instance *v1beta1.ServiceIns if err != nil { if httpErr, ok := osb.IsHTTPError(err); ok { s := fmt.Sprintf( - "Deprovision call failed; received error response from broker: Status Code: %d, Error Message: %v, Description: %v", - httpErr.StatusCode, httpErr.ErrorMessage, httpErr.Description, + "Deprovision call failed; received error response from broker: %v", + httpErr.Error(), ) glog.Warningf( `%s "%s/%s": %s`, @@ -626,7 +630,18 @@ func (c *controller) reconcileServiceInstance(instance *v1beta1.ServiceInstance) return err } - ns, err := c.kubeClient.Core().Namespaces().Get(instance.Namespace, metav1.GetOptions{}) + // Check if the ServiceClass or ServicePlan has been deleted and do not allow + // creation of new ServiceInstances or plan upgrades. It's little complicated + // since we do want to allow parameter changes on an instance whose plan or class + // has been removed from the broker's catalog. + // If changes are not allowed, the method will set the appropriate status / record + // events, so we can just return here on failure. + err = c.checkForRemovedClassAndPlan(instance, serviceClass, servicePlan) + if err != nil { + return err + } + + ns, err := c.kubeClient.CoreV1().Namespaces().Get(instance.Namespace, metav1.GetOptions{}) if err != nil { s := fmt.Sprintf("Failed to get namespace %q during instance create: %s", instance.Namespace, err) glog.Infof( @@ -725,7 +740,7 @@ func (c *controller) reconcileServiceInstance(instance *v1beta1.ServiceInstance) } toUpdate.Status.InProgressProperties = &v1beta1.ServiceInstancePropertiesState{ - ExternalClusterServicePlanName: servicePlan.Spec.ExternalName, + ClusterServicePlanExternalName: servicePlan.Spec.ExternalName, Parameters: rawParametersWithRedaction, ParametersChecksum: parametersChecksum, UserInfo: instance.Spec.UserInfo, @@ -799,7 +814,7 @@ func (c *controller) reconcileServiceInstance(instance *v1beta1.ServiceInstance) } // Only send the plan ID if the plan name has changed from what the Broker has if toUpdate.Status.ExternalProperties == nil || - toUpdate.Status.InProgressProperties.ExternalClusterServicePlanName != toUpdate.Status.ExternalProperties.ExternalClusterServicePlanName { + toUpdate.Status.InProgressProperties.ClusterServicePlanExternalName != toUpdate.Status.ExternalProperties.ClusterServicePlanExternalName { planID := servicePlan.Spec.ExternalID updateRequest.PlanID = &planID } @@ -1076,7 +1091,7 @@ func (c *controller) pollServiceInstance(instance *v1beta1.ServiceInstance) erro typeSI, instance.Namespace, instance.Name, ) - serviceClass, servicePlan, brokerName, brokerClient, err := c.getClusterServiceClassPlanAndClusterServiceBroker(instance) + serviceClass, servicePlan, _, brokerClient, err := c.getClusterServiceClassPlanAndClusterServiceBroker(instance) if err != nil { return err } @@ -1193,41 +1208,31 @@ func (c *controller) pollServiceInstance(instance *v1beta1.ServiceInstance) erro } toUpdate := clone.(*v1beta1.ServiceInstance) + var ( + reason string + message string + ) + switch { + case mitigatingOrphan: + reason = successOrphanMitigationReason + message = successOrphanMitigationMessage + default: + reason = successDeprovisionReason + message = successDeprovisionMessage + } + c.clearServiceInstanceCurrentOperation(toUpdate) toUpdate.Status.ExternalProperties = nil - if mitigatingOrphan { - glog.V(5).Infof( - `%s "%s/%s": %s`, - typeSI, - instance.Namespace, - instance.Name, - successOrphanMitigationMessage, - ) - c.recorder.Event(instance, corev1.EventTypeNormal, successOrphanMitigationReason, successOrphanMitigationMessage) - - setServiceInstanceCondition( - toUpdate, - v1beta1.ServiceInstanceConditionReady, - v1beta1.ConditionFalse, - successOrphanMitigationReason, - successOrphanMitigationMessage, - ) - } else { - glog.V(5).Infof( - `%s "%s/%s": Successfully deprovisioned ServiceInstance of ClusterServiceClass (K8S: %q ExternalName: %q) at ClusterServiceBroker %q`, - typeSI, instance.Namespace, instance.Name, serviceClass.Name, serviceClass.Spec.ExternalName, brokerName, - ) - c.recorder.Event(instance, corev1.EventTypeNormal, successDeprovisionReason, successDeprovisionMessage) - - setServiceInstanceCondition( - toUpdate, - v1beta1.ServiceInstanceConditionReady, - v1beta1.ConditionFalse, - successDeprovisionReason, - successDeprovisionMessage, - ) + setServiceInstanceCondition( + toUpdate, + v1beta1.ServiceInstanceConditionReady, + v1beta1.ConditionFalse, + reason, + message, + ) + if !mitigatingOrphan { // Clear the finalizer if finalizers := sets.NewString(toUpdate.Finalizers...); finalizers.Has(v1beta1.FinalizerServiceCatalog) { finalizers.Delete(v1beta1.FinalizerServiceCatalog) @@ -1239,6 +1244,13 @@ func (c *controller) pollServiceInstance(instance *v1beta1.ServiceInstance) erro return err } + glog.V(5).Infof( + `%s "%s/%s": %s`, + typeSI, instance.Namespace, instance.Name, message, + ) + + c.recorder.Event(instance, corev1.EventTypeNormal, reason, message) + return c.finishPollingServiceInstance(instance) } @@ -1251,10 +1263,7 @@ func (c *controller) pollServiceInstance(instance *v1beta1.ServiceInstance) erro // the instance. errText := "" if httpErr, ok := osb.IsHTTPError(err); ok { - errText = fmt.Sprintf( - "Status code: %d; ErrorMessage: %q; description: %q", - httpErr.StatusCode, httpErr.ErrorMessage, httpErr.Description, - ) + errText = httpErr.Error() } else { errText = err.Error() } @@ -1310,7 +1319,7 @@ func (c *controller) pollServiceInstance(instance *v1beta1.ServiceInstance) erro } glog.V(4).Infof( - `%s "%s/%s": Poll returned %q : %q`, + `%s "%s/%s": Poll returned %q : Response description: %v`, typeSI, instance.Namespace, instance.Name, response.State, response.Description, ) @@ -1484,65 +1493,54 @@ func (c *controller) pollServiceInstance(instance *v1beta1.ServiceInstance) erro return err } case osb.StateFailed: - description := "" + description := "(no description provided)" if response.Description != nil { description = *response.Description } - actionText := "" - switch { - case mitigatingOrphan: - actionText = "mitigating orphan" - case deleting: - actionText = "deprovisioning" - case provisioning: - actionText = "provisioning" - default: - actionText = "updating" - } - s := fmt.Sprintf(`Error %s: %q`, actionText, description) - c.recorder.Event(instance, corev1.EventTypeWarning, errorDeprovisionCalledReason, s) - glog.V(5).Infof( - `%s "%s/%s": %s`, - typeSI, instance.Namespace, instance.Name, s, - ) - - clone, err := api.Scheme.DeepCopy(instance) - if err != nil { - return err - } - toUpdate := clone.(*v1beta1.ServiceInstance) - c.clearServiceInstanceCurrentOperation(toUpdate) - var ( readyCond v1beta1.ConditionStatus reason string - msg string + message string ) switch { case mitigatingOrphan: readyCond = v1beta1.ConditionUnknown reason = errorOrphanMitigationFailedReason - msg = "Orphan mitigation failed: " + s + message = "Orphan mitigation failed: " + description case deleting: readyCond = v1beta1.ConditionUnknown reason = errorDeprovisionCalledReason - msg = "Deprovision call failed: " + s + message = "Deprovision call failed: " + description case provisioning: readyCond = v1beta1.ConditionFalse reason = errorProvisionCallFailedReason - msg = "Provision call failed: " + s + message = "Provision call failed: " + description default: readyCond = v1beta1.ConditionFalse reason = errorUpdateInstanceCallFailedReason - msg = "Update call failed: " + s + message = "Update call failed: " + description } + c.recorder.Event(instance, corev1.EventTypeWarning, reason, message) + glog.V(5).Infof( + `%s "%s/%s": %s`, + typeSI, instance.Namespace, instance.Name, message, + ) + + clone, err := api.Scheme.DeepCopy(instance) + if err != nil { + return err + } + toUpdate := clone.(*v1beta1.ServiceInstance) + + c.clearServiceInstanceCurrentOperation(toUpdate) + setServiceInstanceCondition( toUpdate, v1beta1.ServiceInstanceConditionReady, readyCond, reason, - msg, + message, ) if !mitigatingOrphan { @@ -1551,7 +1549,7 @@ func (c *controller) pollServiceInstance(instance *v1beta1.ServiceInstance) erro v1beta1.ServiceInstanceConditionFailed, v1beta1.ConditionTrue, reason, - msg, + message, ) } @@ -1559,10 +1557,7 @@ func (c *controller) pollServiceInstance(instance *v1beta1.ServiceInstance) erro return err } - err = c.finishPollingServiceInstance(instance) - if err != nil { - return err - } + return c.finishPollingServiceInstance(instance) default: glog.Warningf( `%s "%s/%s": Got invalid state in LastOperationResponse: %q`, @@ -1653,30 +1648,26 @@ func (c *controller) resolveReferences(instance *v1beta1.ServiceInstance) (*v1be // Event, and sets the InstanceCondition with the appropriate error message. func (c *controller) resolveClusterServiceClassRef(instance *v1beta1.ServiceInstance) (*v1beta1.ServiceInstance, *v1beta1.ClusterServiceClass, error) { var sc *v1beta1.ClusterServiceClass - if instance.Spec.ExternalClusterServiceClassName != "" { + if instance.Spec.ClusterServiceClassExternalName != "" { glog.V(4).Infof( `%s "%s/%s": looking up a ClusterServiceClass from externalName: %q`, - typeSI, instance.Namespace, instance.Name, instance.Spec.ExternalClusterServiceClassName, + typeSI, instance.Namespace, instance.Name, instance.Spec.ClusterServiceClassExternalName, ) - listOpts := metav1.ListOptions{FieldSelector: "spec.externalName==" + instance.Spec.ExternalClusterServiceClassName} + listOpts := metav1.ListOptions{FieldSelector: "spec.externalName==" + instance.Spec.ClusterServiceClassExternalName} serviceClasses, err := c.serviceCatalogClient.ClusterServiceClasses().List(listOpts) if err == nil && len(serviceClasses.Items) == 1 { sc = &serviceClasses.Items[0] - instance.Spec.ClusterServiceClassRef = &corev1.ObjectReference{ - Kind: sc.Kind, - Name: sc.Name, - UID: sc.UID, - APIVersion: sc.APIVersion, - ResourceVersion: sc.ResourceVersion, + instance.Spec.ClusterServiceClassRef = &v1beta1.ClusterObjectReference{ + Name: sc.Name, } glog.V(4).Infof( `%s "%s/%s": resolved ClusterServiceClass with externalName %q to K8S ClusterServiceClass %q`, - typeSI, instance.Namespace, instance.Name, instance.Spec.ExternalClusterServiceClassName, sc.Name, + typeSI, instance.Namespace, instance.Name, instance.Spec.ClusterServiceClassExternalName, sc.Name, ) } else { s := fmt.Sprintf( "References a non-existent ClusterServiceClass (ExternalName: %q) or there is more than one (found: %d)", - instance.Spec.ExternalClusterServiceClassName, len(serviceClasses.Items), + instance.Spec.ClusterServiceClassExternalName, len(serviceClasses.Items), ) glog.Warningf( `%s "%s/%s": %s`, @@ -1701,12 +1692,8 @@ func (c *controller) resolveClusterServiceClassRef(instance *v1beta1.ServiceInst var err error sc, err = c.serviceClassLister.Get(instance.Spec.ClusterServiceClassName) if err == nil { - instance.Spec.ClusterServiceClassRef = &corev1.ObjectReference{ - Kind: sc.Kind, - Name: sc.Name, - UID: sc.UID, - APIVersion: sc.APIVersion, - ResourceVersion: sc.ResourceVersion, + instance.Spec.ClusterServiceClassRef = &v1beta1.ClusterObjectReference{ + Name: sc.Name, } glog.V(4).Infof( `%s "%s/%s": resolved ClusterServiceClass with K8S name %q to ClusterServiceClass with external Name %q`, @@ -1733,7 +1720,7 @@ func (c *controller) resolveClusterServiceClassRef(instance *v1beta1.ServiceInst } } else { // ServiceInstance is in invalid state, should not ever happen. check - return nil, nil, fmt.Errorf("ServiceInstance is in inconsistent state, neither ExternalClusterServiceClassName nor ClusterServiceClassName is set: %+v", instance.Spec) + return nil, nil, fmt.Errorf("ServiceInstance is in inconsistent state, neither ClusterServiceClassExternalName nor ClusterServiceClassName is set: %+v", instance.Spec) } return instance, sc, nil } @@ -1743,9 +1730,9 @@ func (c *controller) resolveClusterServiceClassRef(instance *v1beta1.ServiceInst // If ClusterServicePlan can not be resolved, returns an error, records an // Event, and sets the InstanceCondition with the appropriate error message. func (c *controller) resolveClusterServicePlanRef(instance *v1beta1.ServiceInstance, brokerName string) (*v1beta1.ServiceInstance, error) { - if instance.Spec.ExternalClusterServicePlanName != "" { + if instance.Spec.ClusterServicePlanExternalName != "" { fieldSet := fields.Set{ - "spec.externalName": instance.Spec.ExternalClusterServicePlanName, + "spec.externalName": instance.Spec.ClusterServicePlanExternalName, "spec.clusterServiceClassRef.name": instance.Spec.ClusterServiceClassRef.Name, "spec.clusterServiceBrokerName": brokerName, } @@ -1754,21 +1741,17 @@ func (c *controller) resolveClusterServicePlanRef(instance *v1beta1.ServiceInsta servicePlans, err := c.serviceCatalogClient.ClusterServicePlans().List(listOpts) if err == nil && len(servicePlans.Items) == 1 { sp := &servicePlans.Items[0] - instance.Spec.ClusterServicePlanRef = &corev1.ObjectReference{ - Kind: sp.Kind, - Name: sp.Name, - UID: sp.UID, - APIVersion: sp.APIVersion, - ResourceVersion: sp.ResourceVersion, + instance.Spec.ClusterServicePlanRef = &v1beta1.ClusterObjectReference{ + Name: sp.Name, } glog.V(4).Infof( `%s "%s/%s": resolved ClusterServicePlan (ExternalName: %q) to ClusterServicePlan (K8S: %q)`, - typeSI, instance.Namespace, instance.Name, instance.Spec.ExternalClusterServicePlanName, sp.Name, + typeSI, instance.Namespace, instance.Name, instance.Spec.ClusterServicePlanExternalName, sp.Name, ) } else { s := fmt.Sprintf( "References a non-existent ClusterServicePlan (K8S: %q ExternalName: %q) on ClusterServiceClass (K8S: %q ExternalName: %q) or there is more than one (found: %d)", - instance.Spec.ClusterServicePlanName, instance.Spec.ExternalClusterServicePlanName, instance.Spec.ClusterServiceClassRef.Name, instance.Spec.ExternalClusterServiceClassName, len(servicePlans.Items), + instance.Spec.ClusterServicePlanName, instance.Spec.ClusterServicePlanExternalName, instance.Spec.ClusterServiceClassRef.Name, instance.Spec.ClusterServiceClassExternalName, len(servicePlans.Items), ) glog.Warningf( `%s "%s/%s": %s`, @@ -1787,12 +1770,8 @@ func (c *controller) resolveClusterServicePlanRef(instance *v1beta1.ServiceInsta } else if instance.Spec.ClusterServicePlanName != "" { sp, err := c.servicePlanLister.Get(instance.Spec.ClusterServicePlanName) if err == nil { - instance.Spec.ClusterServicePlanRef = &corev1.ObjectReference{ - Kind: sp.Kind, - Name: sp.Name, - UID: sp.UID, - APIVersion: sp.APIVersion, - ResourceVersion: sp.ResourceVersion, + instance.Spec.ClusterServicePlanRef = &v1beta1.ClusterObjectReference{ + Name: sp.Name, } glog.V(4).Infof( `%s "%s/%s": resolved ClusterServicePlan with K8S name %q to ClusterServicePlan with external name %q`, @@ -1804,7 +1783,7 @@ func (c *controller) resolveClusterServicePlanRef(instance *v1beta1.ServiceInsta instance.Spec.ClusterServicePlanName, instance.Spec.ClusterServiceClassName, ) glog.Warningf( - `%s "%s/%s": `, + `%s "%s/%s": %s`, typeSI, instance.Namespace, instance.Name, s, ) c.updateServiceInstanceCondition( @@ -1819,7 +1798,7 @@ func (c *controller) resolveClusterServicePlanRef(instance *v1beta1.ServiceInsta } } else { // ServiceInstance is in invalid state, should not ever happen. check - return nil, fmt.Errorf("ServiceInstance is in inconsistent state, neither ExternalClusterServicePlanName nor ClusterServicePlanName is set: %+v", instance.Spec) + return nil, fmt.Errorf("ServiceInstance is in inconsistent state, neither ClusterServicePlanExternalName nor ClusterServicePlanName is set: %+v", instance.Spec) } return instance, nil } @@ -1890,7 +1869,7 @@ func setServiceInstanceConditionInternal(toUpdate *v1beta1.ServiceInstance, glog.V(3).Infof( `%s "%s/%s": Setting lastTransitionTime, condition %q to %v`, - toUpdate.Namespace, toUpdate.Name, conditionType, t, + typeSI, toUpdate.Namespace, toUpdate.Name, conditionType, t, ) newCondition.LastTransitionTime = t toUpdate.Status.Conditions = append(toUpdate.Status.Conditions, newCondition) @@ -2046,6 +2025,74 @@ func (c *controller) setServiceInstanceStartOrphanMitigation(toUpdate *v1beta1.S ) } +// checkForRemovedClassAndPlan looks at serviceClass and servicePlan and +// if either has been deleted, will block a new instance creation. If +// +func (c *controller) checkForRemovedClassAndPlan(instance *v1beta1.ServiceInstance, serviceClass *v1beta1.ClusterServiceClass, servicePlan *v1beta1.ClusterServicePlan) error { + classDeleted := serviceClass.Status.RemovedFromBrokerCatalog + planDeleted := servicePlan.Status.RemovedFromBrokerCatalog + + if !classDeleted && !planDeleted { + // Neither has been deleted, life's good. + return nil + } + + isProvisioning := false + if instance.Status.ReconciledGeneration == 0 { + isProvisioning = true + } + + // Regardless of what's been deleted, you can always update + // parameters (ie, not change plans) + if !isProvisioning && instance.Status.ExternalProperties != nil && + servicePlan.Spec.ExternalName == instance.Status.ExternalProperties.ClusterServicePlanExternalName { + // Service Instance has already been provisioned and we're only + // updating parameters, so let it through. + return nil + } + + // At this point we know that plan is being changed + if planDeleted { + s := fmt.Sprintf("Service Plan %q (K8S name: %q) has been deleted, can not provision.", servicePlan.Spec.ExternalName, servicePlan.Name) + glog.Warningf( + `%s "%s/%s": %s`, + typeSI, instance.Namespace, instance.Name, s, + ) + c.recorder.Event(instance, corev1.EventTypeWarning, errorDeletedClusterServicePlanReason, s) + + setServiceInstanceCondition( + instance, + v1beta1.ServiceInstanceConditionReady, + v1beta1.ConditionFalse, + errorDeletedClusterServicePlanReason, + s, + ) + if _, err := c.updateServiceInstanceStatus(instance); err != nil { + return err + } + return fmt.Errorf(s) + } + + s := fmt.Sprintf("Service Class %q (K8S name: %q) has been deleted, can not provision.", serviceClass.Spec.ExternalName, serviceClass.Name) + glog.Warningf( + `%s "%s/%s": %s`, + typeSI, instance.Namespace, instance.Name, s, + ) + c.recorder.Event(instance, corev1.EventTypeWarning, errorDeletedClusterServiceClassReason, s) + + setServiceInstanceCondition( + instance, + v1beta1.ServiceInstanceConditionReady, + v1beta1.ConditionFalse, + errorDeletedClusterServiceClassReason, + s, + ) + if _, err := c.updateServiceInstanceStatus(instance); err != nil { + return err + } + return fmt.Errorf(s) +} + // shouldStartOrphanMitigation returns whether an error with the given status // code indicates that orphan migitation should start. func shouldStartOrphanMitigation(statusCode int) bool { diff --git a/pkg/controller/controller_instance_test.go b/pkg/controller/controller_instance_test.go index e89fe9bf99b1..39a85fc915a5 100644 --- a/pkg/controller/controller_instance_test.go +++ b/pkg/controller/controller_instance_test.go @@ -61,8 +61,8 @@ func TestReconcileServiceInstanceNonExistentClusterServiceClass(t *testing.T) { }, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServiceClassName: "nothere", - ExternalClusterServicePlanName: "nothere", + ClusterServiceClassExternalName: "nothere", + ClusterServicePlanExternalName: "nothere", }, ExternalID: testServiceInstanceGUID, }, @@ -80,7 +80,7 @@ func TestReconcileServiceInstanceNonExistentClusterServiceClass(t *testing.T) { listRestrictions := clientgotesting.ListRestrictions{ Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.externalName", instance.Spec.ExternalClusterServiceClassName), + Fields: fields.OneTermEqualSelector("spec.externalName", instance.Spec.ClusterServiceClassExternalName), } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) @@ -179,7 +179,7 @@ func TestReconcileServiceInstanceWithAuthError(t *testing.T) { broker := getTestClusterServiceBroker() broker.Spec.AuthInfo = &v1beta1.ServiceBrokerAuthInfo{ Basic: &v1beta1.BasicAuthConfig{ - SecretRef: &corev1.ObjectReference{ + SecretRef: &v1beta1.ObjectReference{ Namespace: "does_not_exist", Name: "auth-name", }, @@ -243,10 +243,10 @@ func TestReconcileServiceInstanceNonExistentClusterServicePlan(t *testing.T) { }, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServiceClassName: testClusterServiceClassName, - ExternalClusterServicePlanName: "nothere", + ClusterServiceClassExternalName: testClusterServiceClassName, + ClusterServicePlanExternalName: "nothere", }, - ClusterServiceClassRef: &corev1.ObjectReference{ + ClusterServiceClassRef: &v1beta1.ClusterObjectReference{ Name: testClusterServiceClassGUID, }, ExternalID: testServiceInstanceGUID, @@ -306,7 +306,7 @@ func TestReconcileServiceInstanceNonExistentClusterServicePlanK8SName(t *testing ClusterServiceClassName: testClusterServiceClassGUID, ClusterServicePlanName: "nothereplan", }, - ClusterServiceClassRef: &corev1.ObjectReference{ + ClusterServiceClassRef: &v1beta1.ClusterObjectReference{ Name: testClusterServiceClassGUID, }, ExternalID: testServiceInstanceGUID, @@ -491,7 +491,7 @@ func TestReconcileServiceInstanceResolvesReferences(t *testing.T) { listRestrictions := clientgotesting.ListRestrictions{ Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.externalName", instance.Spec.ExternalClusterServiceClassName), + Fields: fields.OneTermEqualSelector("spec.externalName", instance.Spec.ClusterServiceClassExternalName), } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) @@ -555,7 +555,7 @@ func TestReconcileServiceInstanceResolvesReferencesClusterServiceClassRefAlready sharedInformers.ClusterServicePlans().Informer().GetStore().Add(sp) instance := getTestServiceInstance() - instance.Spec.ClusterServiceClassRef = &corev1.ObjectReference{ + instance.Spec.ClusterServiceClassRef = &v1beta1.ClusterObjectReference{ Name: testClusterServiceClassGUID, } @@ -893,6 +893,102 @@ func TestReconcileServiceInstance(t *testing.T) { } } +// TestReconcileServiceInstanceFailsWithDeletedPlan tests that a ServiceInstance is not +// created if the ServicePlan specified is marked as RemovedFromCatalog. +func TestReconcileServiceInstanceFailsWithDeletedPlan(t *testing.T) { + fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, sharedInformers := newTestController(t, noFakeActions()) + + addGetNamespaceReaction(fakeKubeClient) + + sharedInformers.ClusterServiceBrokers().Informer().GetStore().Add(getTestClusterServiceBroker()) + sharedInformers.ClusterServiceClasses().Informer().GetStore().Add(getTestClusterServiceClass()) + sp := getTestClusterServicePlan() + sp.Status.RemovedFromBrokerCatalog = true + sharedInformers.ClusterServicePlans().Informer().GetStore().Add(sp) + + instance := getTestServiceInstanceWithRefs() + + if err := testController.reconcileServiceInstance(instance); err == nil { + t.Fatalf("This should fail") + } + + brokerActions := fakeClusterServiceBrokerClient.Actions() + assertNumberOfClusterServiceBrokerActions(t, brokerActions, 0) + + instanceKey := testNamespace + "/" + testServiceInstanceName + + // Since synchronous operation, must not make it into the polling queue. + if testController.pollingQueue.NumRequeues(instanceKey) != 0 { + t.Fatalf("Expected polling queue to not have any record of test instance") + } + + actions := fakeCatalogClient.Actions() + assertNumberOfActions(t, actions, 1) + + // verify no kube actions + kubeActions := fakeKubeClient.Actions() + assertNumberOfActions(t, kubeActions, 0) + + updatedServiceInstance := assertUpdateStatus(t, actions[0], instance) + assertServiceInstanceReadyFalse(t, updatedServiceInstance, errorDeletedClusterServicePlanReason) + + events := getRecordedEvents(testController) + assertNumEvents(t, events, 1) + + expectedEvent := corev1.EventTypeWarning + " " + errorDeletedClusterServicePlanReason + " Service Plan \"test-plan\" (K8S name: \"PGUID\") has been deleted, can not provision." + if e, a := expectedEvent, events[0]; e != a { + t.Fatalf("Received unexpected event: %v\nExpected: %v", a, e) + } +} + +// TestReconcileServiceInstanceFailsWithDeletedClass tests that a ServiceInstance is not +// created if the ServiceClass specified is marked as RemovedFromCatalog. +func TestReconcileServiceInstanceFailsWithDeletedClass(t *testing.T) { + fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, sharedInformers := newTestController(t, noFakeActions()) + + addGetNamespaceReaction(fakeKubeClient) + + sharedInformers.ClusterServiceBrokers().Informer().GetStore().Add(getTestClusterServiceBroker()) + sc := getTestClusterServiceClass() + sc.Status.RemovedFromBrokerCatalog = true + sharedInformers.ClusterServiceClasses().Informer().GetStore().Add(sc) + sharedInformers.ClusterServicePlans().Informer().GetStore().Add(getTestClusterServicePlan()) + + instance := getTestServiceInstanceWithRefs() + + if err := testController.reconcileServiceInstance(instance); err == nil { + t.Fatalf("This should have failed") + } + + brokerActions := fakeClusterServiceBrokerClient.Actions() + assertNumberOfClusterServiceBrokerActions(t, brokerActions, 0) + + instanceKey := testNamespace + "/" + testServiceInstanceName + + // Since synchronous operation, must not make it into the polling queue. + if testController.pollingQueue.NumRequeues(instanceKey) != 0 { + t.Fatalf("Expected polling queue to not have any record of test instance") + } + + actions := fakeCatalogClient.Actions() + assertNumberOfActions(t, actions, 1) + + // verify no kube actions + kubeActions := fakeKubeClient.Actions() + assertNumberOfActions(t, kubeActions, 0) + + updatedServiceInstance := assertUpdateStatus(t, actions[0], instance) + assertServiceInstanceReadyFalse(t, updatedServiceInstance, errorDeletedClusterServiceClassReason) + + events := getRecordedEvents(testController) + assertNumEvents(t, events, 1) + + expectedEvent := corev1.EventTypeWarning + " " + errorDeletedClusterServiceClassReason + " Service Class \"test-serviceclass\" (K8S name: \"SCGUID\") has been deleted, can not provision." + if e, a := expectedEvent, events[0]; e != a { + t.Fatalf("Received unexpected event: %v\nExpected: %v", a, e) + } +} + // TestReconcileServiceInstance tests synchronously provisioning a new service func TestReconcileServiceInstanceSuccessWithK8SNames(t *testing.T) { fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, sharedInformers := newTestController(t, fakeosb.FakeClientConfiguration{ @@ -1945,7 +2041,7 @@ func TestPollServiceInstanceFailureDeprovisioningWithOperation(t *testing.T) { events := getRecordedEvents(testController) assertNumEvents(t, events, 1) - expectedEvent := corev1.EventTypeWarning + " " + errorDeprovisionCalledReason + " " + "Error deprovisioning: \"\"" + expectedEvent := corev1.EventTypeWarning + " " + errorDeprovisionCalledReason + " " + "Deprovision call failed: (no description provided)" if e, a := expectedEvent, events[0]; e != a { t.Fatalf("Received unexpected event: %v\nExpected: %v", a, e) } @@ -2065,7 +2161,7 @@ func TestPollServiceInstanceClusterServiceBrokerError(t *testing.T) { events := getRecordedEvents(testController) assertNumEvents(t, events, 1) - expectedEvent := corev1.EventTypeWarning + " " + errorPollingLastOperationReason + " " + "Error polling last operation: Status code: 403; ErrorMessage: %!q(*string=); description: %!q(*string=)" + expectedEvent := corev1.EventTypeWarning + " " + errorPollingLastOperationReason + " " + "Error polling last operation: Status: 403; ErrorMessage: ; Description: ; ResponseError: " if e, a := expectedEvent, events[0]; e != a { t.Fatalf("Received unexpected event: %v\nExpected: %v", a, e) } @@ -3408,7 +3504,7 @@ func TestResolveReferencesNoClusterServiceClass(t *testing.T) { listRestrictions := clientgotesting.ListRestrictions{ Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.externalName", instance.Spec.ExternalClusterServiceClassName), + Fields: fields.OneTermEqualSelector("spec.externalName", instance.Spec.ClusterServiceClassExternalName), } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) @@ -3477,7 +3573,7 @@ func TestReconcileServiceInstanceUpdateParameters(t *testing.T) { } instance.Status.ExternalProperties = &v1beta1.ServiceInstancePropertiesState{ - ExternalClusterServicePlanName: testClusterServicePlanName, + ClusterServicePlanExternalName: testClusterServicePlanName, Parameters: oldParametersRaw, ParametersChecksum: oldParametersChecksum, } @@ -3597,7 +3693,7 @@ func TestResolveReferencesNoClusterServicePlan(t *testing.T) { listRestrictions := clientgotesting.ListRestrictions{ Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.externalName", instance.Spec.ExternalClusterServiceClassName), + Fields: fields.OneTermEqualSelector("spec.externalName", instance.Spec.ClusterServiceClassExternalName), } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) @@ -3675,7 +3771,7 @@ func TestReconcileServiceInstanceUpdatePlan(t *testing.T) { } instance.Status.ExternalProperties = &v1beta1.ServiceInstancePropertiesState{ - ExternalClusterServicePlanName: "old-plan-name", + ClusterServicePlanExternalName: "old-plan-name", Parameters: oldParametersRaw, ParametersChecksum: oldParametersChecksum, } @@ -3756,13 +3852,7 @@ func TestReconcileServiceInstanceWithUpdateCallFailure(t *testing.T) { sharedInformers.ClusterServiceClasses().Informer().GetStore().Add(getTestClusterServiceClass()) sharedInformers.ClusterServicePlans().Informer().GetStore().Add(getTestClusterServicePlan()) - instance := getTestServiceInstanceWithRefs() - instance.Generation = 2 - instance.Status.ReconciledGeneration = 1 - - instance.Status.ExternalProperties = &v1beta1.ServiceInstancePropertiesState{ - ExternalClusterServicePlanName: "old-plan-name", - } + instance := getTestServiceInstanceUpdatingPlan() if err := testController.reconcileServiceInstance(instance); err == nil { t.Fatalf("Should not be able to make the ServiceInstance.") @@ -3823,13 +3913,7 @@ func TestReconcileServiceInstanceWithUpdateFailure(t *testing.T) { sharedInformers.ClusterServiceClasses().Informer().GetStore().Add(getTestClusterServiceClass()) sharedInformers.ClusterServicePlans().Informer().GetStore().Add(getTestClusterServicePlan()) - instance := getTestServiceInstanceWithRefs() - instance.Generation = 2 - instance.Status.ReconciledGeneration = 1 - - instance.Status.ExternalProperties = &v1beta1.ServiceInstancePropertiesState{ - ExternalClusterServicePlanName: "old-plan-name", - } + instance := getTestServiceInstanceUpdatingPlan() if err := testController.reconcileServiceInstance(instance); err != nil { t.Fatalf("unexpected error: %v", err) @@ -3916,7 +4000,7 @@ func TestResolveReferencesWorks(t *testing.T) { listRestrictions := clientgotesting.ListRestrictions{ Labels: labels.Everything(), - Fields: fields.OneTermEqualSelector("spec.externalName", instance.Spec.ExternalClusterServiceClassName), + Fields: fields.OneTermEqualSelector("spec.externalName", instance.Spec.ClusterServiceClassExternalName), } assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions) @@ -3973,7 +4057,7 @@ func TestResolveReferencesForPlanChange(t *testing.T) { return true, &v1beta1.ClusterServicePlanList{Items: spItems}, nil }) - instance.Spec.ExternalClusterServicePlanName = newPlanName + instance.Spec.ClusterServicePlanExternalName = newPlanName instance.Spec.ClusterServicePlanRef = nil updatedInstance, err := testController.resolveReferences(instance) @@ -4097,7 +4181,7 @@ func TestReconcileServiceInstanceUpdateAsynchronous(t *testing.T) { instance.Status.ReconciledGeneration = 1 instance.Status.ExternalProperties = &v1beta1.ServiceInstancePropertiesState{ - ExternalClusterServicePlanName: "old-plan-name", + ClusterServicePlanExternalName: "old-plan-name", } instanceKey := testNamespace + "/" + testServiceInstanceName @@ -4305,3 +4389,103 @@ func TestPollServiceInstanceAsyncFailureUpdating(t *testing.T) { updatedServiceInstance := assertUpdateStatus(t, actions[0], instance) assertServiceInstanceRequestFailingErrorNoOrphanMitigation(t, updatedServiceInstance, v1beta1.ServiceInstanceOperationUpdate, errorUpdateInstanceCallFailedReason, errorUpdateInstanceCallFailedReason, instance) } + +func TestCheckClassAndPlanForDeletion(t *testing.T) { + cases := []struct { + name string + instance *v1beta1.ServiceInstance + class *v1beta1.ClusterServiceClass + plan *v1beta1.ClusterServicePlan + success bool + expectedReason string + expectedErrors []string + }{ + { + name: "non-deleted plan and class works", + instance: getTestServiceInstance(), + class: getTestClusterServiceClass(), + plan: getTestClusterServicePlan(), + success: true, + }, + { + name: "deleted plan fails", + instance: getTestServiceInstance(), + class: getTestClusterServiceClass(), + plan: getTestMarkedAsRemovedClusterServicePlan(), + success: false, + expectedReason: errorDeletedClusterServicePlanReason, + expectedErrors: []string{"Service Plan", "has been deleted"}, + }, + { + name: "deleted class fails", + instance: getTestServiceInstance(), + class: getTestMarkedAsRemovedClusterServiceClass(), + plan: getTestClusterServicePlan(), + success: false, + expectedReason: errorDeletedClusterServiceClassReason, + expectedErrors: []string{"Service Class", "has been deleted"}, + }, + { + name: "deleted plan and class fails", + instance: getTestServiceInstance(), + class: getTestClusterServiceClass(), + plan: getTestMarkedAsRemovedClusterServicePlan(), + success: false, + expectedReason: errorDeletedClusterServicePlanReason, + expectedErrors: []string{"Service Plan", "has been deleted"}, + }, + { + name: "Updating plan fails", + instance: getTestServiceInstanceUpdatingPlan(), + class: getTestClusterServiceClass(), + plan: getTestMarkedAsRemovedClusterServicePlan(), + success: false, + expectedReason: errorDeletedClusterServicePlanReason, + expectedErrors: []string{"Service Plan", "has been deleted"}, + }, + { + name: "Updating parameters works", + instance: getTestServiceInstanceUpdatingParametersOfDeletedPlan(), + class: getTestClusterServiceClass(), + plan: getTestMarkedAsRemovedClusterServicePlan(), + success: true, + }, + } + + for _, tc := range cases { + fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, _ := newTestController(t, noFakeActions()) + + err := testController.checkForRemovedClassAndPlan(tc.instance, tc.class, tc.plan) + if err != nil { + if tc.success { + t.Errorf("%q: Unexpected error %v", tc.name, err) + } + for _, exp := range tc.expectedErrors { + if e, a := exp, err.Error(); !strings.Contains(a, e) { + t.Errorf("%q: Did not find expected error %q : got %q", tc.name, e, a) + } + } + } else if !tc.success { + t.Errorf("%q: Did not get a failure when expected one", tc.name) + } + + // no kube or broker actions ever + assertNumberOfActions(t, fakeKubeClient.Actions(), 0) + brokerActions := fakeClusterServiceBrokerClient.Actions() + assertNumberOfClusterServiceBrokerActions(t, brokerActions, 0) + + // If things succeeded, make sure no actions on the catalog client + // and if things fail, make sure instance status is updated and + // an event is generated + actions := fakeCatalogClient.Actions() + if tc.success { + assertNumberOfActions(t, actions, 0) + } else { + assertNumberOfActions(t, actions, 1) + assertUpdateStatus(t, actions[0], tc.instance) + assertServiceInstanceReadyFalse(t, tc.instance, tc.expectedReason) + events := getRecordedEvents(testController) + assertNumEvents(t, events, 1) + } + } +} diff --git a/pkg/controller/controller_serviceclass.go b/pkg/controller/controller_serviceclass.go index 45e456c8e288..e9114368da6e 100644 --- a/pkg/controller/controller_serviceclass.go +++ b/pkg/controller/controller_serviceclass.go @@ -19,6 +19,10 @@ package controller import ( "github.com/golang/glog" "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + + "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" ) @@ -33,30 +37,65 @@ func (c *controller) serviceClassAdd(obj interface{}) { c.serviceClassQueue.Add(key) } +func (c *controller) serviceClassUpdate(oldObj, newObj interface{}) { + c.serviceClassAdd(newObj) +} + +func (c *controller) serviceClassDelete(obj interface{}) { + serviceClass, ok := obj.(*v1beta1.ClusterServiceClass) + if serviceClass == nil || !ok { + return + } + + glog.V(4).Infof("Received delete event for ServiceClass %v; no further processing will occur", serviceClass.Name) +} + // reconcileServiceClassKey reconciles a ServiceClass due to controller resync // or an event on the ServiceClass. Note that this is NOT the main // reconciliation loop for ServiceClass. ServiceClasses are primarily // reconciled in a separate flow when a ClusterServiceBroker is reconciled. func (c *controller) reconcileClusterServiceClassKey(key string) error { - // currently, this is a no-op. In the future, we'll maintain status - // information here. - return nil + plan, err := c.serviceClassLister.Get(key) + if errors.IsNotFound(err) { + glog.Infof("ClusterServiceClass %q: Not doing work because it has been deleted", key) + return nil + } + if err != nil { + glog.Infof("ClusterServiceClass %q: Unable to retrieve object from store: %v", key, err) + return err + } + + return c.reconcileClusterServiceClass(plan) } func (c *controller) reconcileClusterServiceClass(serviceClass *v1beta1.ClusterServiceClass) error { - glog.V(4).Infof("Processing ServiceClass %v", serviceClass.Name) - return nil -} + glog.Infof("ClusterServiceClass %q (ExternalName: %q): processing", serviceClass.Name, serviceClass.Spec.ExternalName) -func (c *controller) serviceClassUpdate(oldObj, newObj interface{}) { - c.serviceClassAdd(newObj) + if !serviceClass.Status.RemovedFromBrokerCatalog { + return nil + } + + glog.Infof("ClusterServiceClass %q (ExternalName: %q): has been removed from broker catalog; determining whether there are instances remaining", serviceClass.Name, serviceClass.Spec.ExternalName) + + serviceInstances, err := c.findServiceInstancesOnClusterServiceClass(serviceClass) + if err != nil { + return err + } + + if len(serviceInstances.Items) != 0 { + return nil + } + + glog.Infof("ClusterServiceClass %q (ExternalName: %q): has been removed from broker catalog and has zero instances remaining; deleting", serviceClass.Name, serviceClass.Spec.ExternalName) + return c.serviceCatalogClient.ClusterServiceClasses().Delete(serviceClass.Name, &metav1.DeleteOptions{}) } -func (c *controller) serviceClassDelete(obj interface{}) { - serviceClass, ok := obj.(*v1beta1.ClusterServiceClass) - if serviceClass == nil || !ok { - return +func (c *controller) findServiceInstancesOnClusterServiceClass(serviceClass *v1beta1.ClusterServiceClass) (*v1beta1.ServiceInstanceList, error) { + fieldSet := fields.Set{ + "spec.clusterServiceClassRef.name": serviceClass.Name, } + fieldSelector := fields.SelectorFromSet(fieldSet).String() + listOpts := metav1.ListOptions{FieldSelector: fieldSelector} - glog.V(4).Infof("Received delete event for ServiceClass %v; no further processing will occur", serviceClass.Name) + return c.serviceCatalogClient.ServiceInstances(metav1.NamespaceAll).List(listOpts) } diff --git a/pkg/controller/controller_serviceclass_test.go b/pkg/controller/controller_serviceclass_test.go new file mode 100644 index 000000000000..4565e7fca7d5 --- /dev/null +++ b/pkg/controller/controller_serviceclass_test.go @@ -0,0 +1,137 @@ +/* +Copyright 2017 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 controller + +import ( + "errors" + "testing" + + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + "github.com/kubernetes-incubator/service-catalog/test/fake" + "k8s.io/apimachinery/pkg/runtime" + + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" + clientgotesting "k8s.io/client-go/testing" +) + +func TestReconcileClusterServiceClassRemovedFromCatalog(t *testing.T) { + getRemovedServiceClass := func() *v1beta1.ClusterServiceClass { + p := getTestClusterServiceClass() + p.Status.RemovedFromBrokerCatalog = true + return p + } + + cases := []struct { + name string + serviceClass *v1beta1.ClusterServiceClass + instances []v1beta1.ServiceInstance + catalogClientPrepFunc func(*fake.Clientset) + shouldError bool + errText *string + catalogActionsCheckFunc func(t *testing.T, name string, actions []clientgotesting.Action) + }{ + { + name: "not removed from catalog", + serviceClass: getTestClusterServiceClass(), + shouldError: false, + }, + { + name: "removed from catalog, instances left", + serviceClass: getRemovedServiceClass(), + instances: []v1beta1.ServiceInstance{*getTestServiceInstance()}, + shouldError: false, + catalogActionsCheckFunc: func(t *testing.T, name string, actions []clientgotesting.Action) { + listRestrictions := clientgotesting.ListRestrictions{ + Labels: labels.Everything(), + Fields: fields.OneTermEqualSelector("spec.clusterServiceClassRef.name", "SCGUID"), + } + + expectNumberOfActions(t, name, actions, 1) + assertList(t, actions[0], &v1beta1.ServiceInstance{}, listRestrictions) + }, + }, + { + name: "removed from catalog, no instances left", + serviceClass: getRemovedServiceClass(), + instances: nil, + shouldError: false, + catalogActionsCheckFunc: func(t *testing.T, name string, actions []clientgotesting.Action) { + listRestrictions := clientgotesting.ListRestrictions{ + Labels: labels.Everything(), + Fields: fields.OneTermEqualSelector("spec.clusterServiceClassRef.name", "SCGUID"), + } + + expectNumberOfActions(t, name, actions, 2) + assertList(t, actions[0], &v1beta1.ServiceInstance{}, listRestrictions) + assertDelete(t, actions[1], getRemovedServiceClass()) + }, + }, + { + name: "removed from catalog, no instances left, delete fails", serviceClass: getRemovedServiceClass(), + instances: nil, + shouldError: true, + catalogClientPrepFunc: func(client *fake.Clientset) { + client.AddReactor("delete", "clusterserviceclasses", func(action clientgotesting.Action) (bool, runtime.Object, error) { + return true, nil, errors.New("oops") + }) + }, + errText: strPtr("oops"), + catalogActionsCheckFunc: func(t *testing.T, name string, actions []clientgotesting.Action) { + listRestrictions := clientgotesting.ListRestrictions{ + Labels: labels.Everything(), + Fields: fields.OneTermEqualSelector("spec.clusterServiceClassRef.name", "SCGUID"), + } + + expectNumberOfActions(t, name, actions, 2) + assertList(t, actions[0], &v1beta1.ServiceInstance{}, listRestrictions) + assertDelete(t, actions[1], getRemovedServiceClass()) + }, + }, + } + + for _, tc := range cases { + _, fakeCatalogClient, _, testController, _ := newTestController(t, noFakeActions()) + + fakeCatalogClient.AddReactor("list", "serviceinstances", func(action clientgotesting.Action) (bool, runtime.Object, error) { + return true, &v1beta1.ServiceInstanceList{Items: tc.instances}, nil + }) + + if tc.catalogClientPrepFunc != nil { + tc.catalogClientPrepFunc(fakeCatalogClient) + } + + err := testController.reconcileClusterServiceClass(tc.serviceClass) + if err != nil { + if !tc.shouldError { + t.Errorf("%v: unexpected error from method under test: %v", tc.name, err) + continue + } else if tc.errText != nil && *tc.errText != err.Error() { + t.Errorf("%v: unexpected error text from method under test; expected %v, got %v", tc.name, tc.errText, err.Error()) + continue + } + } + + actions := fakeCatalogClient.Actions() + + if tc.catalogActionsCheckFunc != nil { + tc.catalogActionsCheckFunc(t, tc.name, actions) + } else { + expectNumberOfActions(t, tc.name, actions, 0) + } + } +} diff --git a/pkg/controller/controller_serviceplan.go b/pkg/controller/controller_serviceplan.go index ba451728e623..e0f97b1a77c4 100644 --- a/pkg/controller/controller_serviceplan.go +++ b/pkg/controller/controller_serviceplan.go @@ -19,6 +19,10 @@ package controller import ( "github.com/golang/glog" "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + + "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" ) @@ -33,26 +37,66 @@ func (c *controller) servicePlanAdd(obj interface{}) { c.servicePlanQueue.Add(key) } +func (c *controller) servicePlanUpdate(oldObj, newObj interface{}) { + c.servicePlanAdd(newObj) +} + +func (c *controller) servicePlanDelete(obj interface{}) { + servicePlan, ok := obj.(*v1beta1.ClusterServicePlan) + if servicePlan == nil || !ok { + return + } + + glog.V(4).Infof("ClusterServicePlan: Received delete event for %v; no further processing will occur", servicePlan.Name) +} + // reconcileClusterServicePlanKey reconciles a ClusterServicePlan due to resync // or an event on the ClusterServicePlan. Note that this is NOT the main // reconciliation loop for ClusterServicePlans. ClusterServicePlans are // primarily reconciled in a separate flow when a ClusterServiceBroker is // reconciled. func (c *controller) reconcileClusterServicePlanKey(key string) error { - // currently, this is a no-op. In the future, we'll maintain status - // information here. - return nil + plan, err := c.servicePlanLister.Get(key) + if errors.IsNotFound(err) { + glog.Infof("ClusterServicePlan %q: Not doing work because it has been deleted", key) + return nil + } + if err != nil { + glog.Infof("ClusterServicePlan %q: Unable to retrieve object from store: %v", key, err) + return err + } + + return c.reconcileClusterServicePlan(plan) } -func (c *controller) servicePlanUpdate(oldObj, newObj interface{}) { - c.servicePlanAdd(newObj) +func (c *controller) reconcileClusterServicePlan(servicePlan *v1beta1.ClusterServicePlan) error { + glog.Infof("ClusterServicePlan %q (ExternalName: %q): processing", servicePlan.Name, servicePlan.Spec.ExternalName) + + if !servicePlan.Status.RemovedFromBrokerCatalog { + return nil + } + + glog.Infof("ClusterServicePlan %q (ExternalName: %q): has been removed from broker catalog; determining whether there are instances remaining", servicePlan.Name, servicePlan.Spec.ExternalName) + + serviceInstances, err := c.findServiceInstancesOnClusterServicePlan(servicePlan) + if err != nil { + return err + } + + if len(serviceInstances.Items) != 0 { + return nil + } + + glog.Infof("ClusterServicePlan %q (ExternalName: %q): has been removed from broker catalog and has zero instances remaining; deleting", servicePlan.Name, servicePlan.Spec.ExternalName) + return c.serviceCatalogClient.ClusterServicePlans().Delete(servicePlan.Name, &metav1.DeleteOptions{}) } -func (c *controller) servicePlanDelete(obj interface{}) { - servicePlan, ok := obj.(*v1beta1.ClusterServicePlan) - if servicePlan == nil || !ok { - return +func (c *controller) findServiceInstancesOnClusterServicePlan(servicePlan *v1beta1.ClusterServicePlan) (*v1beta1.ServiceInstanceList, error) { + fieldSet := fields.Set{ + "spec.clusterServicePlanRef.name": servicePlan.Name, } + fieldSelector := fields.SelectorFromSet(fieldSet).String() + listOpts := metav1.ListOptions{FieldSelector: fieldSelector} - glog.V(4).Infof("ClusterServicePlan: Received delete event for %v; no further processing will occur", servicePlan.Name) + return c.serviceCatalogClient.ServiceInstances(metav1.NamespaceAll).List(listOpts) } diff --git a/pkg/controller/controller_serviceplan_test.go b/pkg/controller/controller_serviceplan_test.go new file mode 100644 index 000000000000..03fbca25496a --- /dev/null +++ b/pkg/controller/controller_serviceplan_test.go @@ -0,0 +1,137 @@ +/* +Copyright 2017 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 controller + +import ( + "errors" + "testing" + + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + "github.com/kubernetes-incubator/service-catalog/test/fake" + "k8s.io/apimachinery/pkg/runtime" + + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" + clientgotesting "k8s.io/client-go/testing" +) + +func TestReconcileClusterServicePlanRemovedFromCatalog(t *testing.T) { + getRemovedPlan := func() *v1beta1.ClusterServicePlan { + p := getTestClusterServicePlan() + p.Status.RemovedFromBrokerCatalog = true + return p + } + + cases := []struct { + name string + plan *v1beta1.ClusterServicePlan + instances []v1beta1.ServiceInstance + catalogClientPrepFunc func(*fake.Clientset) + shouldError bool + errText *string + catalogActionsCheckFunc func(t *testing.T, name string, actions []clientgotesting.Action) + }{ + { + name: "not removed from catalog", + plan: getTestClusterServicePlan(), + shouldError: false, + }, + { + name: "removed from catalog, instances left", + plan: getRemovedPlan(), + instances: []v1beta1.ServiceInstance{*getTestServiceInstance()}, + shouldError: false, + catalogActionsCheckFunc: func(t *testing.T, name string, actions []clientgotesting.Action) { + listRestrictions := clientgotesting.ListRestrictions{ + Labels: labels.Everything(), + Fields: fields.OneTermEqualSelector("spec.clusterServicePlanRef.name", "PGUID"), + } + + expectNumberOfActions(t, name, actions, 1) + assertList(t, actions[0], &v1beta1.ServiceInstance{}, listRestrictions) + }, + }, + { + name: "removed from catalog, no instances left", + plan: getRemovedPlan(), + instances: nil, + shouldError: false, + catalogActionsCheckFunc: func(t *testing.T, name string, actions []clientgotesting.Action) { + listRestrictions := clientgotesting.ListRestrictions{ + Labels: labels.Everything(), + Fields: fields.OneTermEqualSelector("spec.clusterServicePlanRef.name", "PGUID"), + } + + expectNumberOfActions(t, name, actions, 2) + assertList(t, actions[0], &v1beta1.ServiceInstance{}, listRestrictions) + assertDelete(t, actions[1], getRemovedPlan()) + }, + }, + { + name: "removed from catalog, no instances left, delete fails", plan: getRemovedPlan(), + instances: nil, + shouldError: true, + catalogClientPrepFunc: func(client *fake.Clientset) { + client.AddReactor("delete", "clusterserviceplans", func(action clientgotesting.Action) (bool, runtime.Object, error) { + return true, nil, errors.New("oops") + }) + }, + errText: strPtr("oops"), + catalogActionsCheckFunc: func(t *testing.T, name string, actions []clientgotesting.Action) { + listRestrictions := clientgotesting.ListRestrictions{ + Labels: labels.Everything(), + Fields: fields.OneTermEqualSelector("spec.clusterServicePlanRef.name", "PGUID"), + } + + expectNumberOfActions(t, name, actions, 2) + assertList(t, actions[0], &v1beta1.ServiceInstance{}, listRestrictions) + assertDelete(t, actions[1], getRemovedPlan()) + }, + }, + } + + for _, tc := range cases { + _, fakeCatalogClient, _, testController, _ := newTestController(t, noFakeActions()) + + fakeCatalogClient.AddReactor("list", "serviceinstances", func(action clientgotesting.Action) (bool, runtime.Object, error) { + return true, &v1beta1.ServiceInstanceList{Items: tc.instances}, nil + }) + + if tc.catalogClientPrepFunc != nil { + tc.catalogClientPrepFunc(fakeCatalogClient) + } + + err := testController.reconcileClusterServicePlan(tc.plan) + if err != nil { + if !tc.shouldError { + t.Errorf("%v: unexpected error from method under test: %v", tc.name, err) + continue + } else if tc.errText != nil && *tc.errText != err.Error() { + t.Errorf("%v: unexpected error text from method under test; expected %v, got %v", tc.name, tc.errText, err.Error()) + continue + } + } + + actions := fakeCatalogClient.Actions() + + if tc.catalogActionsCheckFunc != nil { + tc.catalogActionsCheckFunc(t, tc.name, actions) + } else { + expectNumberOfActions(t, tc.name, actions, 0) + } + } +} diff --git a/pkg/controller/controller_test.go b/pkg/controller/controller_test.go index 8f44d312c129..012523a2f755 100644 --- a/pkg/controller/controller_test.go +++ b/pkg/controller/controller_test.go @@ -40,7 +40,7 @@ import ( "k8s.io/apimachinery/pkg/util/diff" "github.com/kubernetes-incubator/service-catalog/test/fake" - "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/util/sets" clientgofake "k8s.io/client-go/kubernetes/fake" @@ -399,6 +399,20 @@ func getTestClusterServiceClass() *v1beta1.ClusterServiceClass { } } +func getTestMarkedAsRemovedClusterServiceClass() *v1beta1.ClusterServiceClass { + return &v1beta1.ClusterServiceClass{ + ObjectMeta: metav1.ObjectMeta{Name: testRemovedClusterServiceClassGUID}, + Spec: v1beta1.ClusterServiceClassSpec{ + ClusterServiceBrokerName: testClusterServiceBrokerName, + Description: "a test service that has been marked as removed", + ExternalName: testRemovedClusterServiceClassName, + ExternalID: testRemovedClusterServiceClassGUID, + Bindable: true, + }, + Status: v1beta1.ClusterServiceClassStatus{RemovedFromBrokerCatalog: true}, + } +} + func getTestRemovedClusterServiceClass() *v1beta1.ClusterServiceClass { return &v1beta1.ClusterServiceClass{ ObjectMeta: metav1.ObjectMeta{Name: testRemovedClusterServiceClassGUID}, @@ -420,10 +434,27 @@ func getTestClusterServicePlan() *v1beta1.ClusterServicePlan { ExternalID: testClusterServicePlanGUID, ExternalName: testClusterServicePlanName, Bindable: truePtr(), - ClusterServiceClassRef: v1.LocalObjectReference{ + ClusterServiceClassRef: v1beta1.ClusterObjectReference{ Name: testClusterServiceClassGUID, }, }, + Status: v1beta1.ClusterServicePlanStatus{}, + } +} + +func getTestMarkedAsRemovedClusterServicePlan() *v1beta1.ClusterServicePlan { + return &v1beta1.ClusterServicePlan{ + ObjectMeta: metav1.ObjectMeta{Name: testRemovedClusterServicePlanGUID}, + Spec: v1beta1.ClusterServicePlanSpec{ + ClusterServiceBrokerName: testClusterServiceBrokerName, + ExternalID: testRemovedClusterServicePlanGUID, + ExternalName: testRemovedClusterServicePlanName, + Bindable: truePtr(), + ClusterServiceClassRef: v1beta1.ClusterObjectReference{ + Name: testClusterServiceClassGUID, + }, + }, + Status: v1beta1.ClusterServicePlanStatus{RemovedFromBrokerCatalog: true}, } } @@ -435,7 +466,7 @@ func getTestRemovedClusterServicePlan() *v1beta1.ClusterServicePlan { ExternalID: testRemovedClusterServicePlanGUID, ExternalName: testRemovedClusterServicePlanName, Bindable: truePtr(), - ClusterServiceClassRef: v1.LocalObjectReference{ + ClusterServiceClassRef: v1beta1.ClusterObjectReference{ Name: testClusterServiceClassGUID, }, }, @@ -449,7 +480,7 @@ func getTestClusterServicePlanNonbindable() *v1beta1.ClusterServicePlan { ExternalName: testNonbindableClusterServicePlanName, ExternalID: testNonbindableClusterServicePlanGUID, Bindable: falsePtr(), - ClusterServiceClassRef: v1.LocalObjectReference{ + ClusterServiceClassRef: v1beta1.ClusterObjectReference{ Name: testClusterServiceClassGUID, }, }, @@ -502,21 +533,21 @@ func getTestCatalog() *osb.CatalogResponse { // instance referencing the result of getTestClusterServiceClass() // and getTestClusterServicePlan() // This version sets: -// ExternalClusterServiceClassName and ExternalClusterServicePlanName as well +// ClusterServiceClassExternalName and ClusterServicePlanExternalName as well // as ClusterServiceClassRef and ClusterServicePlanRef which means that the // ClusterServiceClass and ClusterServicePlan are fetched using // Service[Class|Plan]Lister.get(spec.Service[Class|Plan]Ref.Name) func getTestServiceInstanceWithRefs() *v1beta1.ServiceInstance { sc := getTestServiceInstance() - sc.Spec.ClusterServiceClassRef = &v1.ObjectReference{Name: testClusterServiceClassGUID} - sc.Spec.ClusterServicePlanRef = &v1.ObjectReference{Name: testClusterServicePlanGUID} + sc.Spec.ClusterServiceClassRef = &v1beta1.ClusterObjectReference{Name: testClusterServiceClassGUID} + sc.Spec.ClusterServicePlanRef = &v1beta1.ClusterObjectReference{Name: testClusterServicePlanGUID} return sc } // instance referencing the result of getTestClusterServiceClass() // and getTestClusterServicePlan() // This version sets: -// ExternalClusterServiceClassName and ExternalClusterServicePlanName, so depending on the +// ClusterServiceClassExternalName and ClusterServicePlanExternalName, so depending on the // test, you may need to add reactors that deal with List due to the need // to resolve Names to IDs for both ClusterServiceClass and ClusterServicePlan func getTestServiceInstance() *v1beta1.ServiceInstance { @@ -528,8 +559,8 @@ func getTestServiceInstance() *v1beta1.ServiceInstance { }, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServiceClassName: testClusterServiceClassName, - ExternalClusterServicePlanName: testClusterServicePlanName, + ClusterServiceClassExternalName: testClusterServiceClassName, + ClusterServicePlanExternalName: testClusterServicePlanName, }, ExternalID: testServiceInstanceGUID, }, @@ -560,10 +591,10 @@ func getTestServiceInstanceK8SNames() *v1beta1.ServiceInstance { // an instance referencing the result of getTestNonbindableClusterServiceClass, on the non-bindable plan. func getTestNonbindableServiceInstance() *v1beta1.ServiceInstance { i := getTestServiceInstance() - i.Spec.ExternalClusterServiceClassName = testNonbindableClusterServiceClassName - i.Spec.ExternalClusterServicePlanName = testNonbindableClusterServicePlanName - i.Spec.ClusterServiceClassRef = &v1.ObjectReference{Name: testNonbindableClusterServiceClassGUID} - i.Spec.ClusterServicePlanRef = &v1.ObjectReference{Name: testNonbindableClusterServicePlanGUID} + i.Spec.ClusterServiceClassExternalName = testNonbindableClusterServiceClassName + i.Spec.ClusterServicePlanExternalName = testNonbindableClusterServicePlanName + i.Spec.ClusterServiceClassRef = &v1beta1.ClusterObjectReference{Name: testNonbindableClusterServiceClassGUID} + i.Spec.ClusterServicePlanRef = &v1beta1.ClusterObjectReference{Name: testNonbindableClusterServicePlanGUID} return i } @@ -571,16 +602,16 @@ func getTestNonbindableServiceInstance() *v1beta1.ServiceInstance { // an instance referencing the result of getTestNonbindableClusterServiceClass, on the bindable plan. func getTestServiceInstanceNonbindableServiceBindablePlan() *v1beta1.ServiceInstance { i := getTestNonbindableServiceInstance() - i.Spec.ExternalClusterServicePlanName = testClusterServicePlanName - i.Spec.ClusterServicePlanRef = &v1.ObjectReference{Name: testClusterServicePlanGUID} + i.Spec.ClusterServicePlanExternalName = testClusterServicePlanName + i.Spec.ClusterServicePlanRef = &v1beta1.ClusterObjectReference{Name: testClusterServicePlanGUID} return i } func getTestServiceInstanceBindableServiceNonbindablePlan() *v1beta1.ServiceInstance { i := getTestServiceInstanceWithRefs() - i.Spec.ExternalClusterServicePlanName = testNonbindableClusterServicePlanName - i.Spec.ClusterServicePlanRef = &v1.ObjectReference{Name: testNonbindableClusterServicePlanGUID} + i.Spec.ClusterServicePlanExternalName = testNonbindableClusterServicePlanName + i.Spec.ClusterServicePlanRef = &v1beta1.ClusterObjectReference{Name: testNonbindableClusterServicePlanGUID} return i } @@ -610,6 +641,60 @@ func getTestServiceInstanceWithFailedStatus() *v1beta1.ServiceInstance { return instance } +func getTestServiceInstanceUpdatingPlan() *v1beta1.ServiceInstance { + instance := getTestServiceInstanceWithRefs() + instance.Generation = 2 + instance.Status = v1beta1.ServiceInstanceStatus{ + Conditions: []v1beta1.ServiceInstanceCondition{{ + Type: v1beta1.ServiceInstanceConditionReady, + Status: v1beta1.ConditionTrue, + }}, + ExternalProperties: &v1beta1.ServiceInstancePropertiesState{ + ClusterServicePlanExternalName: "old-plan-name", + }, + // It's been provisioned successfully. + ReconciledGeneration: 1, + } + + return instance +} + +func getTestServiceInstanceUpdatingParameters() *v1beta1.ServiceInstance { + instance := getTestServiceInstanceWithRefs() + instance.Generation = 2 + instance.Status = v1beta1.ServiceInstanceStatus{ + Conditions: []v1beta1.ServiceInstanceCondition{{ + Type: v1beta1.ServiceInstanceConditionReady, + Status: v1beta1.ConditionTrue, + }}, + ExternalProperties: &v1beta1.ServiceInstancePropertiesState{ + ClusterServicePlanExternalName: testClusterServicePlanName, + }, + // It's been provisioned successfully. + ReconciledGeneration: 1, + } + + return instance +} + +func getTestServiceInstanceUpdatingParametersOfDeletedPlan() *v1beta1.ServiceInstance { + instance := getTestServiceInstanceWithRefs() + instance.Generation = 2 + instance.Status = v1beta1.ServiceInstanceStatus{ + Conditions: []v1beta1.ServiceInstanceCondition{{ + Type: v1beta1.ServiceInstanceConditionReady, + Status: v1beta1.ConditionTrue, + }}, + ExternalProperties: &v1beta1.ServiceInstancePropertiesState{ + ClusterServicePlanExternalName: testRemovedClusterServicePlanName, + }, + // It's been provisioned successfully. + ReconciledGeneration: 1, + } + + return instance +} + // getTestServiceInstanceAsync returns an instance in async mode func getTestServiceInstanceAsyncProvisioning(operation string) *v1beta1.ServiceInstance { instance := getTestServiceInstanceWithRefs() @@ -626,7 +711,7 @@ func getTestServiceInstanceAsyncProvisioning(operation string) *v1beta1.ServiceI OperationStartTime: &operationStartTime, CurrentOperation: v1beta1.ServiceInstanceOperationProvision, InProgressProperties: &v1beta1.ServiceInstancePropertiesState{ - ExternalClusterServicePlanName: testClusterServicePlanName, + ClusterServicePlanExternalName: testClusterServicePlanName, }, } if operation != "" { @@ -655,10 +740,10 @@ func getTestServiceInstanceAsyncUpdating(operation string) *v1beta1.ServiceInsta OperationStartTime: &operationStartTime, CurrentOperation: v1beta1.ServiceInstanceOperationUpdate, InProgressProperties: &v1beta1.ServiceInstancePropertiesState{ - ExternalClusterServicePlanName: testClusterServicePlanName, + ClusterServicePlanExternalName: testClusterServicePlanName, }, ExternalProperties: &v1beta1.ServiceInstancePropertiesState{ - ExternalClusterServicePlanName: "old-plan-name", + ClusterServicePlanExternalName: "old-plan-name", }, } if operation != "" { @@ -685,7 +770,7 @@ func getTestServiceInstanceAsyncDeprovisioning(operation string) *v1beta1.Servic CurrentOperation: v1beta1.ServiceInstanceOperationDeprovision, ReconciledGeneration: 1, ExternalProperties: &v1beta1.ServiceInstancePropertiesState{ - ExternalClusterServicePlanName: testClusterServicePlanName, + ClusterServicePlanExternalName: testClusterServicePlanName, }, } if operation != "" { @@ -713,7 +798,7 @@ func getTestServiceBinding() *v1beta1.ServiceBinding { Generation: 1, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{Name: testServiceInstanceName}, + ServiceInstanceRef: v1beta1.LocalObjectReference{Name: testServiceInstanceName}, ExternalID: testServiceBindingGUID, }, } @@ -1076,7 +1161,7 @@ func TestCatalogConversionClusterServicePlanBindable(t *testing.T) { ExternalID: "s1_plan1_id", ExternalName: "bindable-bindable", Bindable: nil, - ClusterServiceClassRef: v1.LocalObjectReference{ + ClusterServiceClassRef: v1beta1.ClusterObjectReference{ Name: "bindable-id", }, }, @@ -1089,7 +1174,7 @@ func TestCatalogConversionClusterServicePlanBindable(t *testing.T) { ExternalName: "bindable-unbindable", ExternalID: "s1_plan2_id", Bindable: falsePtr(), - ClusterServiceClassRef: v1.LocalObjectReference{ + ClusterServiceClassRef: v1beta1.ClusterObjectReference{ Name: "bindable-id", }, }, @@ -1102,7 +1187,7 @@ func TestCatalogConversionClusterServicePlanBindable(t *testing.T) { ExternalName: "unbindable-unbindable", ExternalID: "s2_plan1_id", Bindable: nil, - ClusterServiceClassRef: v1.LocalObjectReference{ + ClusterServiceClassRef: v1beta1.ClusterObjectReference{ Name: "unbindable-id", }, }, @@ -1115,7 +1200,7 @@ func TestCatalogConversionClusterServicePlanBindable(t *testing.T) { ExternalName: "unbindable-bindable", ExternalID: "s2_plan2_id", Bindable: truePtr(), - ClusterServiceClassRef: v1.LocalObjectReference{ + ClusterServiceClassRef: v1beta1.ClusterObjectReference{ Name: "unbindable-id", }, }, @@ -1371,6 +1456,14 @@ func assertUpdateReference(t *testing.T, action clientgotesting.Action, obj inte return assertActionFor(t, action, "update", "reference", obj) } +func expectCreate(t *testing.T, name string, action clientgotesting.Action, obj interface{}) (runtime.Object, bool) { + return testActionFor(t, name, errorf, action, "create", "" /* subresource */, obj) +} + +func expectUpdate(t *testing.T, name string, action clientgotesting.Action, obj interface{}) (runtime.Object, bool) { + return testActionFor(t, name, errorf, action, "update", "" /* subresource */, obj) +} + func expectUpdateStatus(t *testing.T, name string, action clientgotesting.Action, obj interface{}) (runtime.Object, bool) { return testActionFor(t, name, errorf, action, "update", "status", obj) } @@ -1991,7 +2084,7 @@ func assertServiceInstancePropertiesStatePlanName(t *testing.T, propsLabel strin if actualProps == nil { fatalf(t, "expected %v properties to not be nil", propsLabel) } - if e, a := expectedPlanName, actualProps.ExternalClusterServicePlanName; e != a { + if e, a := expectedPlanName, actualProps.ClusterServicePlanExternalName; e != a { fatalf(t, "unexpected %v properties external service plan name: expected %v, actual %v", propsLabel, e, a) } } @@ -2522,7 +2615,7 @@ func getTestCatalogConfig() fakeosb.FakeClientConfiguration { func addGetNamespaceReaction(fakeKubeClient *clientgofake.Clientset) { fakeKubeClient.AddReactor("get", "namespaces", func(action clientgotesting.Action) (bool, runtime.Object, error) { - return true, &v1.Namespace{ + return true, &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ UID: types.UID(testNamespaceGUID), }, @@ -2536,7 +2629,7 @@ func addGetSecretNotFoundReaction(fakeKubeClient *clientgofake.Clientset) { }) } -func addGetSecretReaction(fakeKubeClient *clientgofake.Clientset, secret *v1.Secret) { +func addGetSecretReaction(fakeKubeClient *clientgofake.Clientset, secret *corev1.Secret) { fakeKubeClient.AddReactor("get", "secrets", func(action clientgotesting.Action) (bool, runtime.Object, error) { return true, secret, nil }) diff --git a/pkg/controller/parameters_test.go b/pkg/controller/parameters_test.go index 202ea5685fe6..3b7a98e2b88f 100644 --- a/pkg/controller/parameters_test.go +++ b/pkg/controller/parameters_test.go @@ -21,14 +21,14 @@ import ( "testing" "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" - "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/diff" clientgofake "k8s.io/client-go/kubernetes/fake" ) func TestBuildParameters(t *testing.T) { - secret := &v1.Secret{ + secret := &corev1.Secret{ Data: map[string][]byte{ "json-key": []byte("{ \"json\": true }"), "string-key": []byte("textFromSecret"), @@ -39,7 +39,7 @@ func TestBuildParameters(t *testing.T) { name string parametersFrom []v1beta1.ParametersFromSource parameters *runtime.RawExtension - secret *v1.Secret + secret *corev1.Secret expectedParameters map[string]interface{} expectedParametersWithSecretsRedacted map[string]interface{} shouldSucceed bool @@ -147,7 +147,7 @@ func TestBuildParameters(t *testing.T) { } } -func testBuildParameters(t *testing.T, parametersFrom []v1beta1.ParametersFromSource, parameters *runtime.RawExtension, secret *v1.Secret, expected map[string]interface{}, expectedWithSecretsRdacted map[string]interface{}, shouldSucceed bool) { +func testBuildParameters(t *testing.T, parametersFrom []v1beta1.ParametersFromSource, parameters *runtime.RawExtension, secret *corev1.Secret, expected map[string]interface{}, expectedWithSecretsRdacted map[string]interface{}, shouldSucceed bool) { // create a fake kube client fakeKubeClient := &clientgofake.Clientset{} if secret != nil { diff --git a/pkg/hyperkube/README.md b/pkg/hyperkube/README.md new file mode 100644 index 000000000000..44b8518ac1d4 --- /dev/null +++ b/pkg/hyperkube/README.md @@ -0,0 +1,15 @@ +This package contains files copied from the main [kubernetes/kubernetes repo](https://github.com/kubernetes/kubernetes). +The files are taken from cmd/hyperkube. + +Version: 1.8 + +The following additional changes have been made to the files. +- The package name from k8s.io/kubernetes/cmd/hyperkube has been changed from +main to hyperkube. +- The use of stretchr/testify in k8s.io/kubernetes/cmd/hyperkube/hyperkube_test.go +has been replaced with similar assert calls to functions in service-catalog/test/util/assertions.go. +- In k8s.io/kubernetes/cmd/hyperkube/hyperkube.go, the code to print the +version has been replaced with version code from service-catalog. +- In k8s.io/kubernetes/cmd/hyperkube/server.go, made exportable the name field +of the Server type, renaming the field to ServerName to avoid conflict with +the Name function. \ No newline at end of file diff --git a/pkg/hyperkube/hyperkube.go b/pkg/hyperkube/hyperkube.go new file mode 100644 index 000000000000..d0757d1d8b44 --- /dev/null +++ b/pkg/hyperkube/hyperkube.go @@ -0,0 +1,256 @@ +/* +Copyright 2017 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 hyperkube + +import ( + "errors" + "flag" + "fmt" + "io" + "io/ioutil" + "os" + "path" + + "github.com/spf13/pflag" + + utiltemplate "github.com/kubernetes-incubator/service-catalog/pkg/kubernetes/pkg/util/template" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apiserver/pkg/server" + utilflag "k8s.io/apiserver/pkg/util/flag" + "k8s.io/apiserver/pkg/util/logs" + + "github.com/kubernetes-incubator/service-catalog/pkg" +) + +// HyperKube represents a single binary that can morph/manage into multiple +// servers. +type HyperKube struct { + Name string // The executable name, used for help and soft-link invocation + Long string // A long description of the binary. It will be word-wrapped before output. + + servers []Server + baseFlags *pflag.FlagSet + out io.Writer + helpFlagVal bool + printVersionFlagVal bool + makeSymlinksFlagVal bool +} + +// AddServer adds a server to the HyperKube object. +func (hk *HyperKube) AddServer(s *Server) { + hk.servers = append(hk.servers, *s) + hk.servers[len(hk.servers)-1].hk = hk +} + +// FindServer will find a specific server named name. +func (hk *HyperKube) FindServer(name string) (*Server, error) { + for _, s := range hk.servers { + if s.Name() == name || s.AlternativeName == name { + return &s, nil + } + } + return nil, fmt.Errorf("Server not found: %s", name) +} + +// Servers returns a list of all of the registered servers +func (hk *HyperKube) Servers() []Server { + return hk.servers +} + +// Flags returns a flagset for "global" flags. +func (hk *HyperKube) Flags() *pflag.FlagSet { + if hk.baseFlags == nil { + hk.baseFlags = pflag.NewFlagSet(hk.Name, pflag.ContinueOnError) + hk.baseFlags.SetOutput(ioutil.Discard) + hk.baseFlags.SetNormalizeFunc(utilflag.WordSepNormalizeFunc) + hk.baseFlags.BoolVarP(&hk.helpFlagVal, "help", "h", false, "help for "+hk.Name) + hk.baseFlags.BoolVar(&hk.printVersionFlagVal, "version", false, "Print version information and quit") + hk.baseFlags.BoolVar(&hk.makeSymlinksFlagVal, "make-symlinks", false, "create a symlink for each server in current directory") + hk.baseFlags.MarkHidden("make-symlinks") // hide this flag from appearing in servers' usage output + + // These will add all of the "global" flags (defined with both the + // flag and pflag packages) to the new flag set we have. + hk.baseFlags.AddGoFlagSet(flag.CommandLine) + hk.baseFlags.AddFlagSet(pflag.CommandLine) + + } + return hk.baseFlags +} + +// Out returns the io.Writer that is used for all usage/error information +func (hk *HyperKube) Out() io.Writer { + if hk.out == nil { + hk.out = os.Stderr + } + return hk.out +} + +// SetOut sets the output writer for all usage/error information +func (hk *HyperKube) SetOut(w io.Writer) { + hk.out = w +} + +// Print is a convenience method to Print to the defined output +func (hk *HyperKube) Print(i ...interface{}) { + fmt.Fprint(hk.Out(), i...) +} + +// Println is a convenience method to Println to the defined output +func (hk *HyperKube) Println(i ...interface{}) { + fmt.Fprintln(hk.Out(), i...) +} + +// Printf is a convenience method to Printf to the defined output +func (hk *HyperKube) Printf(format string, i ...interface{}) { + fmt.Fprintf(hk.Out(), format, i...) +} + +// Run the server. This will pick the appropriate server and run it. +func (hk *HyperKube) Run(args []string, stopCh <-chan struct{}) error { + // If we are called directly, parse all flags up to the first real + // argument. That should be the server to run. + command := args[0] + serverName := path.Base(command) + args = args[1:] + if serverName == hk.Name { + + baseFlags := hk.Flags() + baseFlags.SetInterspersed(false) // Only parse flags up to the next real command + err := baseFlags.Parse(args) + if err != nil || hk.helpFlagVal { + if err != nil { + hk.Println("Error:", err) + } + hk.Usage() + return err + } + + if hk.makeSymlinksFlagVal { + return hk.MakeSymlinks(command) + } + + if hk.printVersionFlagVal { + pkg.PrintAndExit() + } + + args = baseFlags.Args() + if len(args) > 0 && len(args[0]) > 0 { + serverName = args[0] + args = args[1:] + } else { + err = errors.New("no server specified") + hk.Printf("Error: %v\n\n", err) + hk.Usage() + return err + } + } + + s, err := hk.FindServer(serverName) + if err != nil { + hk.Printf("Error: %v\n\n", err) + hk.Usage() + return err + } + + s.Flags().AddFlagSet(hk.Flags()) + err = s.Flags().Parse(args) + if err != nil || hk.helpFlagVal { + if err != nil { + hk.Printf("Error: %v\n\n", err) + } + s.Usage() + return err + } + + if hk.printVersionFlagVal { + pkg.PrintAndExit() + } + + logs.InitLogs() + defer logs.FlushLogs() + + if !s.RespectsStopCh { + // For commands that do not respect the stopCh, we run them in a go + // routine and leave them running when stopCh is closed. + errCh := make(chan error) + go func() { + errCh <- s.Run(s, s.Flags().Args(), wait.NeverStop) + }() + select { + case <-stopCh: + return errors.New("interrupted") // This error text is ignored. + case err = <-errCh: + // fall-through + } + } else { + err = s.Run(s, s.Flags().Args(), stopCh) + } + if err != nil { + hk.Println("Error:", err) + } + + return err +} + +// RunToExit will run the hyperkube and then call os.Exit with an appropriate exit code. +func (hk *HyperKube) RunToExit(args []string) { + stopCh := server.SetupSignalHandler() + if err := hk.Run(args, stopCh); err != nil { + os.Exit(1) + } +} + +// Usage will write out a summary for all servers that this binary supports. +func (hk *HyperKube) Usage() { + tt := `{{if .Long}}{{.Long | trim | wrap ""}} +{{end}}Usage + + {{.Name}} [flags] + +Servers +{{range .Servers}} + {{.Name}} +{{.Long | trim | wrap " "}}{{end}} +Call '{{.Name}} --make-symlinks' to create symlinks for each server in the local directory. +Call '{{.Name}} --help' for help on a specific server. +` + utiltemplate.ExecuteTemplate(hk.Out(), tt, hk) +} + +// MakeSymlinks will create a symlink for each registered hyperkube server in the local directory. +func (hk *HyperKube) MakeSymlinks(command string) error { + wd, err := os.Getwd() + if err != nil { + return err + } + + var errs bool + for _, s := range hk.servers { + link := path.Join(wd, s.Name()) + + err := os.Symlink(command, link) + if err != nil { + errs = true + hk.Println(err) + } + } + + if errs { + return errors.New("error creating one or more symlinks") + } + return nil +} diff --git a/pkg/hyperkube/hyperkube_test.go b/pkg/hyperkube/hyperkube_test.go new file mode 100644 index 000000000000..dc6202aa2f22 --- /dev/null +++ b/pkg/hyperkube/hyperkube_test.go @@ -0,0 +1,322 @@ +/* +Copyright 2017 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 hyperkube + +import ( + "bytes" + "errors" + "fmt" + "strings" + "testing" + "time" + + "github.com/spf13/cobra" + + "k8s.io/apimachinery/pkg/util/wait" + + "github.com/kubernetes-incubator/service-catalog/test/util" +) + +type result struct { + err error + output string +} + +func testServer(n string) *Server { + return &Server{ + SimpleUsage: n, + Long: fmt.Sprintf("A simple server named %s", n), + Run: func(s *Server, args []string, stopCh <-chan struct{}) error { + s.hk.Printf("%s Run\n", s.Name()) + return nil + }, + } +} +func testServerError(n string) *Server { + return &Server{ + SimpleUsage: n, + Long: fmt.Sprintf("A simple server named %s that returns an error", n), + Run: func(s *Server, args []string, stopCh <-chan struct{}) error { + s.hk.Printf("%s Run\n", s.Name()) + return errors.New("server returning error") + }, + } +} +func testStopChRespectingServer(n string) *Server { + return &Server{ + SimpleUsage: n, + Long: fmt.Sprintf("A simple server named %s", n), + Run: func(s *Server, args []string, stopCh <-chan struct{}) error { + s.hk.Printf("%s Run\n", s.Name()) + <-stopCh + return nil + }, + RespectsStopCh: true, + } +} +func testStopChIgnoringServer(n string) *Server { + return &Server{ + SimpleUsage: n, + Long: fmt.Sprintf("A simple server named %s", n), + Run: func(s *Server, args []string, stopCh <-chan struct{}) error { + <-wait.NeverStop // this leaks obviously, but we don't care about one go routine more or less in test + return nil + }, + RespectsStopCh: false, + } +} +func testStopChRespectingServerWithError(n string) *Server { + return &Server{ + SimpleUsage: n, + Long: fmt.Sprintf("A simple server named %s", n), + Run: func(s *Server, args []string, stopCh <-chan struct{}) error { + s.hk.Printf("%s Run\n", s.Name()) + <-stopCh + return errors.New("server returning error") + }, + RespectsStopCh: true, + } +} + +const defaultCobraMessage = "default message from cobra command" +const defaultCobraSubMessage = "default sub-message from cobra command" +const cobraMessageDesc = "message to print" +const cobraSubMessageDesc = "sub-message to print" + +func testCobraCommand(n string) *Server { + + var cobraServer *Server + var msg string + cmd := &cobra.Command{ + Use: n, + Long: n, + Short: n, + Run: func(cmd *cobra.Command, args []string) { + cobraServer.hk.Printf("msg: %s\n", msg) + }, + } + cmd.PersistentFlags().StringVar(&msg, "msg", defaultCobraMessage, cobraMessageDesc) + + var subMsg string + subCmdName := "subcommand" + subCmd := &cobra.Command{ + Use: subCmdName, + Long: subCmdName, + Short: subCmdName, + Run: func(cmd *cobra.Command, args []string) { + cobraServer.hk.Printf("submsg: %s", subMsg) + }, + } + subCmd.PersistentFlags().StringVar(&subMsg, "submsg", defaultCobraSubMessage, cobraSubMessageDesc) + + cmd.AddCommand(subCmd) + + localFlags := cmd.LocalFlags() + localFlags.SetInterspersed(false) + s := &Server{ + SimpleUsage: n, + Long: fmt.Sprintf("A server named %s which uses a cobra command", n), + Run: func(s *Server, args []string, stopCh <-chan struct{}) error { + cobraServer = s + cmd.SetOutput(s.hk.Out()) + cmd.SetArgs(args) + return cmd.Execute() + }, + flags: localFlags, + } + + return s +} +func runFull(t *testing.T, args string, stopCh <-chan struct{}) *result { + buf := new(bytes.Buffer) + hk := HyperKube{ + Name: "hyperkube", + Long: "hyperkube is an all-in-one server binary.", + } + hk.SetOut(buf) + + hk.AddServer(testServer("test1")) + hk.AddServer(testServer("test2")) + hk.AddServer(testServer("test3")) + hk.AddServer(testServerError("test-error")) + hk.AddServer(testStopChIgnoringServer("test-stop-ch-ignoring")) + hk.AddServer(testStopChRespectingServer("test-stop-ch-respecting")) + hk.AddServer(testStopChRespectingServerWithError("test-error-stop-ch-respecting")) + hk.AddServer(testCobraCommand("test-cobra-command")) + + a := strings.Split(args, " ") + t.Logf("Running full with args: %q", a) + err := hk.Run(a, stopCh) + + r := &result{err, buf.String()} + t.Logf("Result err: %v, output: %q", r.err, r.output) + + return r +} + +func TestRun(t *testing.T) { + x := runFull(t, "hyperkube test1", wait.NeverStop) + util.AssertContains(t, x.output, "test1 Run") + util.AssertNoError(t, x.err) +} + +func TestLinkRun(t *testing.T) { + x := runFull(t, "test1", wait.NeverStop) + util.AssertContains(t, x.output, "test1 Run") + util.AssertNoError(t, x.err) +} + +func TestTopNoArgs(t *testing.T) { + x := runFull(t, "hyperkube", wait.NeverStop) + util.AssertEqualError(t, x.err, "no server specified") +} + +func TestBadServer(t *testing.T) { + x := runFull(t, "hyperkube bad-server", wait.NeverStop) + util.AssertEqualError(t, x.err, "Server not found: bad-server") + util.AssertContains(t, x.output, "Usage") +} + +func TestTopHelp(t *testing.T) { + x := runFull(t, "hyperkube --help", wait.NeverStop) + util.AssertNoError(t, x.err) + util.AssertContains(t, x.output, "all-in-one") + util.AssertContains(t, x.output, "A simple server named test1") +} + +func TestTopFlags(t *testing.T) { + x := runFull(t, "hyperkube --help test1", wait.NeverStop) + util.AssertNoError(t, x.err) + util.AssertContains(t, x.output, "all-in-one") + util.AssertContains(t, x.output, "A simple server named test1") + util.AssertNotContains(t, x.output, "test1 Run") +} + +func TestTopFlagsBad(t *testing.T) { + x := runFull(t, "hyperkube --bad-flag", wait.NeverStop) + util.AssertEqualError(t, x.err, "unknown flag: --bad-flag") + util.AssertContains(t, x.output, "all-in-one") + util.AssertContains(t, x.output, "A simple server named test1") +} + +func TestServerHelp(t *testing.T) { + x := runFull(t, "hyperkube test1 --help", wait.NeverStop) + util.AssertNoError(t, x.err) + util.AssertContains(t, x.output, "A simple server named test1") + util.AssertContains(t, x.output, "-h, --help") + util.AssertContains(t, x.output, "help for hyperkube") + util.AssertNotContains(t, x.output, "test1 Run") +} + +func TestServerFlagsBad(t *testing.T) { + x := runFull(t, "hyperkube test1 --bad-flag", wait.NeverStop) + util.AssertEqualError(t, x.err, "unknown flag: --bad-flag") + util.AssertContains(t, x.output, "A simple server named test1") + util.AssertContains(t, x.output, "-h, --help") + util.AssertContains(t, x.output, "help for hyperkube") + util.AssertNotContains(t, x.output, "test1 Run") +} + +func TestServerError(t *testing.T) { + x := runFull(t, "hyperkube test-error", wait.NeverStop) + util.AssertContains(t, x.output, "test-error Run") + util.AssertEqualError(t, x.err, "server returning error") +} + +func TestStopChIgnoringServer(t *testing.T) { + stopCh := make(chan struct{}) + returnedCh := make(chan struct{}) + var x *result + go func() { + defer close(returnedCh) + x = runFull(t, "hyperkube test-stop-ch-ignoring", stopCh) + }() + close(stopCh) + select { + case <-time.After(wait.ForeverTestTimeout): + t.Fatalf("%q never returned after stopCh was closed", "hyperkube test-stop-ch-ignoring") + case <-returnedCh: + } + // we cannot be sure that the server had a chance to output anything + // util.Assert.Contains(t, x.output, "test-error-stop-ch-ignoring Run") + util.AssertEqualError(t, x.err, "interrupted") +} + +func TestStopChRespectingServer(t *testing.T) { + stopCh := make(chan struct{}) + returnedCh := make(chan struct{}) + var x *result + go func() { + defer close(returnedCh) + x = runFull(t, "hyperkube test-stop-ch-respecting", stopCh) + }() + close(stopCh) + select { + case <-time.After(wait.ForeverTestTimeout): + t.Fatalf("%q never returned after stopCh was closed", "hyperkube test-stop-ch-respecting") + case <-returnedCh: + } + util.AssertContains(t, x.output, "test-stop-ch-respecting Run") + util.AssertNil(t, x.err) +} + +func TestStopChRespectingServerWithError(t *testing.T) { + stopCh := make(chan struct{}) + returnedCh := make(chan struct{}) + var x *result + go func() { + defer close(returnedCh) + x = runFull(t, "hyperkube test-error-stop-ch-respecting", stopCh) + }() + close(stopCh) + select { + case <-time.After(wait.ForeverTestTimeout): + t.Fatalf("%q never returned after stopCh was closed", "hyperkube test-error-stop-ch-respecting") + case <-returnedCh: + } + util.AssertContains(t, x.output, "test-error-stop-ch-respecting Run") + util.AssertEqualError(t, x.err, "server returning error") +} + +func TestCobraCommandHelp(t *testing.T) { + x := runFull(t, "hyperkube test-cobra-command --help", wait.NeverStop) + util.AssertNoError(t, x.err) + util.AssertContains(t, x.output, "A server named test-cobra-command which uses a cobra command") + util.AssertContains(t, x.output, cobraMessageDesc) +} +func TestCobraCommandDefaultMessage(t *testing.T) { + x := runFull(t, "hyperkube test-cobra-command", wait.NeverStop) + util.AssertContains(t, x.output, fmt.Sprintf("msg: %s", defaultCobraMessage)) +} +func TestCobraCommandMessage(t *testing.T) { + x := runFull(t, "hyperkube test-cobra-command --msg foobar", wait.NeverStop) + util.AssertContains(t, x.output, "msg: foobar") +} + +func TestCobraSubCommandHelp(t *testing.T) { + x := runFull(t, "hyperkube test-cobra-command subcommand --help", wait.NeverStop) + util.AssertNoError(t, x.err) + util.AssertContains(t, x.output, cobraSubMessageDesc) +} +func TestCobraSubCommandDefaultMessage(t *testing.T) { + x := runFull(t, "hyperkube test-cobra-command subcommand", wait.NeverStop) + util.AssertContains(t, x.output, fmt.Sprintf("submsg: %s", defaultCobraSubMessage)) +} +func TestCobraSubCommandMessage(t *testing.T) { + x := runFull(t, "hyperkube test-cobra-command subcommand --submsg foobar", wait.NeverStop) + util.AssertContains(t, x.output, "submsg: foobar") +} diff --git a/pkg/hyperkube/server.go b/pkg/hyperkube/server.go new file mode 100644 index 000000000000..8dc1603e662e --- /dev/null +++ b/pkg/hyperkube/server.go @@ -0,0 +1,77 @@ +/* +Copyright 2017 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 hyperkube + +import ( + "io/ioutil" + "strings" + + utiltemplate "github.com/kubernetes-incubator/service-catalog/pkg/kubernetes/pkg/util/template" + "k8s.io/apiserver/pkg/util/flag" + + "github.com/spf13/pflag" +) + +type serverRunFunc func(s *Server, args []string, stopCh <-chan struct{}) error + +// Server describes a server that this binary can morph into. +type Server struct { + SimpleUsage string // One line description of the server. + Long string // Longer free form description of the server + Run serverRunFunc // Run the server. This is not expected to return. + PrimaryName string + AlternativeName string + RespectsStopCh bool + + flags *pflag.FlagSet // Flags for the command (and all dependents) + hk *HyperKube +} + +// Usage returns the full usage string including all of the flags. +func (s *Server) Usage() error { + tt := `{{if .Long}}{{.Long | trim | wrap ""}} +{{end}}Usage: + {{.SimpleUsage}} [flags] + +Available Flags: +{{.Flags.FlagUsages}}` + + return utiltemplate.ExecuteTemplate(s.hk.Out(), tt, s) +} + +// Name returns the name of the command as derived from the usage line. +func (s *Server) Name() string { + if s.PrimaryName != "" { + return s.PrimaryName + } + name := s.SimpleUsage + i := strings.Index(name, " ") + if i >= 0 { + name = name[:i] + } + return name +} + +// Flags returns a flagset for this server +func (s *Server) Flags() *pflag.FlagSet { + if s.flags == nil { + s.flags = pflag.NewFlagSet(s.Name(), pflag.ContinueOnError) + s.flags.SetOutput(ioutil.Discard) + s.flags.SetNormalizeFunc(flag.WordSepNormalizeFunc) + } + return s.flags +} diff --git a/pkg/kubernetes/README.md b/pkg/kubernetes/README.md index 34d21a5bd4bd..2c377a755441 100644 --- a/pkg/kubernetes/README.md +++ b/pkg/kubernetes/README.md @@ -26,4 +26,4 @@ in sync. In the long term the packages used by Kubernetes-based projects will be moved from the main repo to separate top-level repositories, [k8s.io/common](https://github.com/kubernetes/common) and [k8s.io/utils](https://github.com/kubernetes/utils). Once it is done, we can -eventually switch to those packages and drop this directory. \ No newline at end of file +eventually switch to those packages and drop this directory. diff --git a/pkg/kubernetes/pkg/util/template/template.go b/pkg/kubernetes/pkg/util/template/template.go new file mode 100644 index 000000000000..2c03911dc21a --- /dev/null +++ b/pkg/kubernetes/pkg/util/template/template.go @@ -0,0 +1,49 @@ +/* +Copyright 2017 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 template + +import ( + "bytes" + "go/doc" + "io" + "strings" + "text/template" +) + +func wrap(indent string, s string) string { + var buf bytes.Buffer + doc.ToText(&buf, s, indent, indent+" ", 80-len(indent)) + return buf.String() +} + +// ExecuteTemplate executes templateText with data and output written to w. +func ExecuteTemplate(w io.Writer, templateText string, data interface{}) error { + t := template.New("top") + t.Funcs(template.FuncMap{ + "trim": strings.TrimSpace, + "wrap": wrap, + }) + template.Must(t.Parse(templateText)) + return t.Execute(w, data) +} + +// ExecuteTemplateToString executes templateText with data and output written to string. +func ExecuteTemplateToString(templateText string, data interface{}) (string, error) { + b := bytes.Buffer{} + err := ExecuteTemplate(&b, templateText, data) + return b.String(), err +} diff --git a/pkg/openapi/openapi_generated.go b/pkg/openapi/openapi_generated.go index 96d325384f3d..74467b59a663 100644 --- a/pkg/openapi/openapi_generated.go +++ b/pkg/openapi/openapi_generated.go @@ -36,14 +36,14 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "secretRef": { SchemaProps: spec.SchemaProps{ Description: "SecretRef is a reference to a Secret containing information the catalog should use to authenticate to this ServiceBroker.\n\nRequired at least one of the fields: - Secret.Data[\"username\"] - username used for authentication - Secret.Data[\"password\"] - password or token needed for authentication", - Ref: ref("k8s.io/api/core/v1.ObjectReference"), + Ref: ref("github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ObjectReference"), }, }, }, }, }, Dependencies: []string{ - "k8s.io/api/core/v1.ObjectReference"}, + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ObjectReference"}, }, "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.BearerTokenAuthConfig": { Schema: spec.Schema{ @@ -53,14 +53,31 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "secretRef": { SchemaProps: spec.SchemaProps{ Description: "SecretRef is a reference to a Secret containing information the catalog should use to authenticate to this ServiceBroker.\n\nRequired field: - Secret.Data[\"token\"] - bearer token for authentication", - Ref: ref("k8s.io/api/core/v1.ObjectReference"), + Ref: ref("github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ObjectReference"), }, }, }, }, }, Dependencies: []string{ - "k8s.io/api/core/v1.ObjectReference"}, + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ObjectReference"}, + }, + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ClusterObjectReference": { + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClusterObjectReference contains enough information to let you locate the cluster-scoped referenced object.", + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{}, }, "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ClusterServiceBroker": { Schema: spec.Schema{ @@ -591,7 +608,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "clusterServiceClassRef": { SchemaProps: spec.SchemaProps{ Description: "ClusterServiceClassRef is a reference to the service class that owns this plan.", - Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + Ref: ref("github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ClusterObjectReference"), }, }, }, @@ -599,7 +616,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA }, }, Dependencies: []string{ - "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ClusterObjectReference", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, }, "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ClusterServicePlanStatus": { Schema: spec.Schema{ @@ -619,6 +636,47 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA }, Dependencies: []string{}, }, + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.LocalObjectReference": { + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.", + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{}, + }, + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ObjectReference": { + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ObjectReference contains enough information to let you locate the referenced object.", + Properties: map[string]spec.Schema{ + "namespace": { + SchemaProps: spec.SchemaProps{ + Description: "Namespace of the referent.", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{}, + }, "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ParametersFromSource": { Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -639,18 +697,18 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.PlanReference": { Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "PlanReference defines the user specification for the desired ServicePlan and ServiceClass. Because there are multiple ways to specify the desired Class/Plan, this structure specifies the allowed ways to specify the intent.\n\nCurrently supported ways:\n - ExternalClusterServiceClassName and ExternalClusterServicePlanName\n - ClusterServiceClassName and ClusterServicePlanName\n\nFor both of these ways, if a ClusterServiceClass only has one plan then leaving the *ServicePlanName is optional.", + Description: "PlanReference defines the user specification for the desired ServicePlan and ServiceClass. Because there are multiple ways to specify the desired Class/Plan, this structure specifies the allowed ways to specify the intent.\n\nCurrently supported ways:\n - ClusterServiceClassExternalName and ClusterServicePlanExternalName\n - ClusterServiceClassName and ClusterServicePlanName\n\nFor both of these ways, if a ClusterServiceClass only has one plan then leaving the *ServicePlanName is optional.", Properties: map[string]spec.Schema{ - "externalClusterServiceClassName": { + "clusterServiceClassExternalName": { SchemaProps: spec.SchemaProps{ - Description: "ExternalClusterServiceClassName is the human-readable name of the service as reported by the broker. Note that if the broker changes the name of the ClusterServiceClass, it will not be reflected here, and to see the current name of the ClusterServiceClass, you should follow the ClusterServiceClassRef below.\n\nImmutable.", + Description: "ClusterServiceClassExternalName is the human-readable name of the service as reported by the broker. Note that if the broker changes the name of the ClusterServiceClass, it will not be reflected here, and to see the current name of the ClusterServiceClass, you should follow the ClusterServiceClassRef below.\n\nImmutable.", Type: []string{"string"}, Format: "", }, }, - "externalClusterServicePlanName": { + "clusterServicePlanExternalName": { SchemaProps: spec.SchemaProps{ - Description: "ExternalClusterServicePlanName is the human-readable name of the plan as reported by the broker. Note that if the broker changes the name of the ClusterServicePlan, it will not be reflected here, and to see the current name of the ClusterServicePlan, you should follow the ClusterServicePlanRef below.", + Description: "ClusterServicePlanExternalName is the human-readable name of the plan as reported by the broker. Note that if the broker changes the name of the ClusterServicePlan, it will not be reflected here, and to see the current name of the ClusterServicePlan, you should follow the ClusterServicePlanRef below.", Type: []string{"string"}, Format: "", }, @@ -867,7 +925,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "instanceRef": { SchemaProps: spec.SchemaProps{ Description: "ServiceInstanceRef is the reference to the Instance this ServiceBinding is to.\n\nImmutable.", - Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + Ref: ref("github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.LocalObjectReference"), }, }, "parameters": { @@ -914,7 +972,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA }, }, Dependencies: []string{ - "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ParametersFromSource", "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.UserInfo", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.LocalObjectReference", "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ParametersFromSource", "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.UserInfo", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, }, "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ServiceBindingStatus": { Schema: spec.Schema{ @@ -996,17 +1054,11 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA Ref: ref("github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.BearerTokenAuthConfig"), }, }, - "basicAuthSecret": { - SchemaProps: spec.SchemaProps{ - Description: "DEPRECATED: use `Basic` field for configuring basic authentication instead. BasicAuthSecret is a reference to a Secret containing auth information the catalog should use to authenticate to this ServiceBroker using basic auth.", - Ref: ref("k8s.io/api/core/v1.ObjectReference"), - }, - }, }, }, }, Dependencies: []string{ - "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.BasicAuthConfig", "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.BearerTokenAuthConfig", "k8s.io/api/core/v1.ObjectReference"}, + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.BasicAuthConfig", "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.BearerTokenAuthConfig"}, }, "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ServiceBrokerCondition": { Schema: spec.Schema{ @@ -1189,9 +1241,9 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA SchemaProps: spec.SchemaProps{ Description: "ServiceInstancePropertiesState is the state of a ServiceInstance that the ClusterServiceBroker knows about.", Properties: map[string]spec.Schema{ - "externalClusterServicePlanName": { + "clusterServicePlanExternalName": { SchemaProps: spec.SchemaProps{ - Description: "ExternalClusterServicePlanName is the name of the plan that the broker knows this ServiceInstance to be on. This is the human readable plan name from the OSB API.", + Description: "ClusterServicePlanExternalName is the name of the plan that the broker knows this ServiceInstance to be on. This is the human readable plan name from the OSB API.", Type: []string{"string"}, Format: "", }, @@ -1216,7 +1268,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA }, }, }, - Required: []string{"externalClusterServicePlanName"}, + Required: []string{"clusterServicePlanExternalName"}, }, }, Dependencies: []string{ @@ -1227,16 +1279,16 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA SchemaProps: spec.SchemaProps{ Description: "ServiceInstanceSpec represents the desired state of an Instance.", Properties: map[string]spec.Schema{ - "externalClusterServiceClassName": { + "clusterServiceClassExternalName": { SchemaProps: spec.SchemaProps{ - Description: "ExternalClusterServiceClassName is the human-readable name of the service as reported by the broker. Note that if the broker changes the name of the ClusterServiceClass, it will not be reflected here, and to see the current name of the ClusterServiceClass, you should follow the ClusterServiceClassRef below.\n\nImmutable.", + Description: "ClusterServiceClassExternalName is the human-readable name of the service as reported by the broker. Note that if the broker changes the name of the ClusterServiceClass, it will not be reflected here, and to see the current name of the ClusterServiceClass, you should follow the ClusterServiceClassRef below.\n\nImmutable.", Type: []string{"string"}, Format: "", }, }, - "externalClusterServicePlanName": { + "clusterServicePlanExternalName": { SchemaProps: spec.SchemaProps{ - Description: "ExternalClusterServicePlanName is the human-readable name of the plan as reported by the broker. Note that if the broker changes the name of the ClusterServicePlan, it will not be reflected here, and to see the current name of the ClusterServicePlan, you should follow the ClusterServicePlanRef below.", + Description: "ClusterServicePlanExternalName is the human-readable name of the plan as reported by the broker. Note that if the broker changes the name of the ClusterServicePlan, it will not be reflected here, and to see the current name of the ClusterServicePlan, you should follow the ClusterServicePlanRef below.", Type: []string{"string"}, Format: "", }, @@ -1257,14 +1309,14 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA }, "clusterServiceClassRef": { SchemaProps: spec.SchemaProps{ - Description: "ClusterServiceClassRef is a reference to the ClusterServiceClass that the user selected. This is set by the controller based on ExternalClusterServiceClassName", - Ref: ref("k8s.io/api/core/v1.ObjectReference"), + Description: "ClusterServiceClassRef is a reference to the ClusterServiceClass that the user selected. This is set by the controller based on ClusterServiceClassExternalName", + Ref: ref("github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ClusterObjectReference"), }, }, "clusterServicePlanRef": { SchemaProps: spec.SchemaProps{ - Description: "ClusterServicePlanRef is a reference to the ClusterServicePlan that the user selected. This is set by the controller based on ExternalClusterServicePlanName", - Ref: ref("k8s.io/api/core/v1.ObjectReference"), + Description: "ClusterServicePlanRef is a reference to the ClusterServicePlan that the user selected. This is set by the controller based on ClusterServicePlanExternalName", + Ref: ref("github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ClusterObjectReference"), }, }, "parameters": { @@ -1311,7 +1363,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA }, }, Dependencies: []string{ - "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ParametersFromSource", "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.UserInfo", "k8s.io/api/core/v1.ObjectReference", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ClusterObjectReference", "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ParametersFromSource", "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.UserInfo", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, }, "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1.ServiceInstanceStatus": { Schema: spec.Schema{ diff --git a/pkg/pretty/context_builder.go b/pkg/pretty/context_builder.go new file mode 100644 index 000000000000..294582684b5f --- /dev/null +++ b/pkg/pretty/context_builder.go @@ -0,0 +1,93 @@ +/* +Copyright 2017 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 pretty + +import ( + "fmt" +) + +// ContextBuilder allows building up pretty message lines with context +// that is important for debugging and tracing. This class helps create log +// line formatting consistency. Pretty lines should be in the form: +// "/": +type ContextBuilder struct { + Kind Kind + Namespace string + Name string +} + +// NewContextBuilder returns a new ContextBuilder that can be used to format messages in the +// form ` "/": `. +// kind, namespace, name are all optional. +func NewContextBuilder(kind Kind, namespace string, name string) *ContextBuilder { + lb := new(ContextBuilder) + lb.Kind = kind + lb.Namespace = namespace + lb.Name = name + return lb +} + +// SetKind sets the kind to use in the source context for messages. +func (pcb *ContextBuilder) SetKind(k Kind) *ContextBuilder { + pcb.Kind = k + return pcb +} + +// SetNamespace sets the namespace to use in the source context for messages. +func (pcb *ContextBuilder) SetNamespace(n string) *ContextBuilder { + pcb.Namespace = n + return pcb +} + +// SetName sets the name to use in the source context for messages. +func (pcb *ContextBuilder) SetName(n string) *ContextBuilder { + pcb.Name = n + return pcb +} + +// Message returns a string with message prepended with the current source context. +func (pcb *ContextBuilder) Message(msg string) string { + if pcb.Kind > 0 || pcb.Namespace != "" || pcb.Name != "" { + return fmt.Sprintf(`%s: %s`, pcb, msg) + } + return msg +} + +// Messagef returns a string with message formatted then prepended with the current source context. +func (pcb *ContextBuilder) Messagef(format string, a ...interface{}) string { + msg := fmt.Sprintf(format, a...) + return pcb.Message(msg) +} + +// TODO(n3wscott): Support (K8S: ExternalName: ) + +func (pcb ContextBuilder) String() string { + s := "" + space := "" + if pcb.Kind > 0 { + s += fmt.Sprintf("%s", pcb.Kind) + space = " " + } + if pcb.Namespace != "" && pcb.Name != "" { + s += fmt.Sprintf(`%s"%s/%s"`, space, pcb.Namespace, pcb.Name) + } else if pcb.Namespace != "" { + s += fmt.Sprintf(`%s"%s"`, space, pcb.Namespace) + } else if pcb.Name != "" { + s += fmt.Sprintf(`%s"%s"`, space, pcb.Name) + } + return s +} diff --git a/pkg/pretty/context_builder_test.go b/pkg/pretty/context_builder_test.go new file mode 100644 index 000000000000..668860e1b814 --- /dev/null +++ b/pkg/pretty/context_builder_test.go @@ -0,0 +1,157 @@ +/* +Copyright 2017 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 pretty + +import ( + "testing" +) + +func TestPrettyContextBuilderKind(t *testing.T) { + pcb := ContextBuilder{} + + pcb.SetKind(ServiceInstance) + + e := "ServiceInstance" + g := pcb.String() + if g != e { + t.Fatalf("Unexpected value of ContextBuilder String; expected %v, got %v", e, g) + } +} + +func TestPrettyContextBuilderNamespace(t *testing.T) { + pcb := ContextBuilder{} + + pcb.SetNamespace("Namespace") + + e := `"Namespace"` + g := pcb.String() + if g != e { + t.Fatalf("Unexpected value of ContextBuilder String; expected %v, got %v", e, g) + } +} + +func TestPrettyContextBuilderName(t *testing.T) { + pcb := ContextBuilder{} + + pcb.SetName("Name") + + e := `"Name"` + g := pcb.String() + if g != e { + t.Fatalf("Unexpected value of ContextBuilder String; expected %v, got %v", e, g) + } +} + +func TestPrettyContextBuilderKindAndNamespace(t *testing.T) { + pcb := ContextBuilder{} + + pcb.SetKind(ServiceInstance).SetNamespace("Namespace") + + e := `ServiceInstance "Namespace"` + g := pcb.String() + if g != e { + t.Fatalf("Unexpected value of ContextBuilder String; expected %v, got %v", e, g) + } +} + +func TestPrettyContextBuilderKindAndName(t *testing.T) { + pcb := ContextBuilder{} + + pcb.SetKind(ServiceInstance).SetName("Name") + + e := `ServiceInstance "Name"` + g := pcb.String() + if g != e { + t.Fatalf("Unexpected value of ContextBuilder String; expected %v, got %v", e, g) + } +} + +func TestPrettyContextBuilderKindNamespaceName(t *testing.T) { + pcb := ContextBuilder{} + + pcb.SetKind(ServiceInstance).SetNamespace("Namespace").SetName("Name") + + e := `ServiceInstance "Namespace/Name"` + g := pcb.String() + if g != e { + t.Fatalf("Unexpected value of ContextBuilder String; expected %v, got %v", e, g) + } +} + +func TestPrettyContextBuilderKindNamespaceNameNew(t *testing.T) { + pcb := NewContextBuilder(ServiceInstance, "Namespace", "Name") + + e := `ServiceInstance "Namespace/Name"` + g := pcb.String() + if g != e { + t.Fatalf("Unexpected value of ContextBuilder String; expected %v, got %v", e, g) + } +} + +func TestPrettyContextBuilderMessage(t *testing.T) { + pcb := ContextBuilder{} + + e := `Msg` + g := pcb.Message("Msg") + if g != e { + t.Fatalf("Unexpected value of ContextBuilder String; expected %v, got %v", e, g) + } +} + +func TestPrettyContextBuilderContextAndMessage(t *testing.T) { + pcb := ContextBuilder{} + + pcb.SetKind(ServiceInstance).SetNamespace("Namespace").SetName("Name") + + e := `ServiceInstance "Namespace/Name": Msg` + g := pcb.Message("Msg") + if g != e { + t.Fatalf("Unexpected value of ContextBuilder String; expected %v, got %v", e, g) + } +} + +func TestPrettyContextBuilderMessagef(t *testing.T) { + pcb := ContextBuilder{} + + e := `This was built.` + g := pcb.Messagef("This %s built.", "was") + if g != e { + t.Fatalf("Unexpected value of ContextBuilder String; expected %v, got %v", e, g) + } +} + +func TestPrettyContextBuilderMessagefMany(t *testing.T) { + pcb := ContextBuilder{} + + e := `One 2 three 4 "five" 6` + g := pcb.Messagef("%s %d %s %v %q %d", "One", 2, "three", 4, "five", 6) + if g != e { + t.Fatalf("Unexpected value of ContextBuilder String; expected %v, got %v", e, g) + } +} + +func TestPrettyContextBuilderContextMessagefAndContext(t *testing.T) { + pcb := ContextBuilder{} + + pcb.SetKind(ServiceInstance).SetNamespace("Namespace").SetName("Name") + + e := `ServiceInstance "Namespace/Name": This was the message: Msg` + g := pcb.Messagef("This was the message: %s", "Msg") + if g != e { + t.Fatalf("Unexpected value of ContextBuilder String; expected %v, got %v", e, g) + } +} diff --git a/pkg/pretty/pretty_kind.go b/pkg/pretty/pretty_kind.go new file mode 100644 index 000000000000..c8e0780378c0 --- /dev/null +++ b/pkg/pretty/pretty_kind.go @@ -0,0 +1,44 @@ +/* +Copyright 2017 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 pretty + +// Kind is used for the enum of the Type of object we are building context for. +type Kind int + +// Names of Types to use when creating pretty messages +const ( + Unknown Kind = iota + ClusterServiceBroker + ClusterServiceClass + ClusterServicePlan + ServiceInstance +) + +func (k Kind) String() string { + switch k { + case ClusterServiceBroker: + return "ClusterServiceBroker" + case ClusterServiceClass: + return "ClusterServiceClass" + case ClusterServicePlan: + return "ClusterServicePlan" + case ServiceInstance: + return "ServiceInstance" + default: + return "" + } +} diff --git a/pkg/pretty/pretty_names.go b/pkg/pretty/pretty_names.go new file mode 100644 index 000000000000..aa5465a6393e --- /dev/null +++ b/pkg/pretty/pretty_names.go @@ -0,0 +1,53 @@ +/* +Copyright 2017 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 pretty + +import ( + "fmt" + + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" +) + +// Name prints in the form ` (K8S: ExternalName: )` +// kind is required. k8sName and externalName are optional +func Name(kind Kind, k8sName, externalName string) string { + s := fmt.Sprintf("%s", kind) + if k8sName != "" && externalName != "" { + s += fmt.Sprintf(" (K8S: %q ExternalName: %q)", k8sName, externalName) + } else if k8sName != "" { + s += fmt.Sprintf(" (K8S: %q)", k8sName) + } else if externalName != "" { + s += fmt.Sprintf(" (ExternalName: %q)", externalName) + } + return s +} + +// ClusterServiceClassName returns a string with the k8s name and external name if available. +func ClusterServiceClassName(serviceClass *v1beta1.ClusterServiceClass) string { + if serviceClass != nil { + return Name(ClusterServiceClass, serviceClass.Name, serviceClass.Spec.ExternalName) + } + return Name(ClusterServiceClass, "", "") +} + +// ClusterServicePlanName returns a string with the k8s name and external name if available. +func ClusterServicePlanName(servicePlan *v1beta1.ClusterServicePlan) string { + if servicePlan != nil { + return Name(ClusterServicePlan, servicePlan.Name, servicePlan.Spec.ExternalName) + } + return Name(ClusterServicePlan, "", "") +} diff --git a/pkg/pretty/pretty_names_test.go b/pkg/pretty/pretty_names_test.go new file mode 100644 index 000000000000..144885c54726 --- /dev/null +++ b/pkg/pretty/pretty_names_test.go @@ -0,0 +1,61 @@ +/* +Copyright 2017 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 pretty + +import ( + "testing" + + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestPrettyNames(t *testing.T) { + e := `ServiceInstance (K8S: "k8s" ExternalName: "extern")` + g := Name(ServiceInstance, "k8s", "extern") + if g != e { + t.Fatalf("Unexpected value of PrettyName String; expected %v, got %v", e, g) + } +} + +func TestClusterServiceClassName(t *testing.T) { + serviceClass := &v1beta1.ClusterServiceClass{ + ObjectMeta: metav1.ObjectMeta{Name: "service-class"}, + Spec: v1beta1.ClusterServiceClassSpec{ + ExternalName: "external-class-name", + }, + } + e := `ClusterServiceClass (K8S: "service-class" ExternalName: "external-class-name")` + g := ClusterServiceClassName(serviceClass) + if g != e { + t.Fatalf("Unexpected value of PrettyName String; expected %v, got %v", e, g) + } +} + +func TestClusterServicePlanName(t *testing.T) { + servicePlan := &v1beta1.ClusterServicePlan{ + ObjectMeta: metav1.ObjectMeta{Name: "service-plan"}, + Spec: v1beta1.ClusterServicePlanSpec{ + ExternalName: "external-plan-name", + }, + } + + e := `ClusterServicePlan (K8S: "service-plan" ExternalName: "external-plan-name")` + g := ClusterServicePlanName(servicePlan) + if g != e { + t.Fatalf("Unexpected value of PrettyName String; expected %v, got %v", e, g) + } +} diff --git a/pkg/registry/servicecatalog/binding/storage.go b/pkg/registry/servicecatalog/binding/storage.go index 502ccbf0f53f..7eca052c849d 100644 --- a/pkg/registry/servicecatalog/binding/storage.go +++ b/pkg/registry/servicecatalog/binding/storage.go @@ -139,6 +139,11 @@ func NewStorage(opts server.Options) (rest.Storage, rest.Storage, error) { DestroyFunc: dFunc, } + options := &generic.StoreOptions{RESTOptions: opts.EtcdOptions.RESTOptions, AttrFunc: GetAttrs} + if err := store.CompleteWithOptions(options); err != nil { + panic(err) // TODO: Propagate error up + } + statusStore := store statusStore.UpdateStrategy = bindingStatusUpdateStrategy diff --git a/pkg/registry/servicecatalog/binding/strategy_test.go b/pkg/registry/servicecatalog/binding/strategy_test.go index 684fbb217273..a8875eab91a9 100644 --- a/pkg/registry/servicecatalog/binding/strategy_test.go +++ b/pkg/registry/servicecatalog/binding/strategy_test.go @@ -20,7 +20,6 @@ import ( "fmt" "testing" - "k8s.io/api/core/v1" "k8s.io/apiserver/pkg/authentication/user" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" utilfeature "k8s.io/apiserver/pkg/util/feature" @@ -36,7 +35,7 @@ func getTestInstanceCredential() *servicecatalog.ServiceBinding { Generation: 1, }, Spec: servicecatalog.ServiceBindingSpec{ - ServiceInstanceRef: v1.LocalObjectReference{ + ServiceInstanceRef: servicecatalog.LocalObjectReference{ Name: "some-string", }, }, @@ -81,7 +80,7 @@ func TestInstanceCredentialUpdate(t *testing.T) { // older: getTestInstanceCredential(), // newer: func() *v1beta1.ServiceBinding { // ic := getTestInstanceCredential() - // ic.Spec.ServiceInstanceRef = v1.LocalObjectReference{ + // ic.Spec.ServiceInstanceRef = servicecatalog.LocalObjectReference{ // Name: "new-string", // } // return ic diff --git a/pkg/registry/servicecatalog/broker/storage.go b/pkg/registry/servicecatalog/broker/storage.go index c79ea68e3f41..74c54601d910 100644 --- a/pkg/registry/servicecatalog/broker/storage.go +++ b/pkg/registry/servicecatalog/broker/storage.go @@ -139,6 +139,11 @@ func NewStorage(opts server.Options) (brokers, brokersStatus rest.Storage) { DestroyFunc: dFunc, } + options := &generic.StoreOptions{RESTOptions: opts.EtcdOptions.RESTOptions, AttrFunc: GetAttrs} + if err := store.CompleteWithOptions(options); err != nil { + panic(err) // TODO: Propagate error up + } + statusStore := store statusStore.UpdateStrategy = brokerStatusUpdateStrategy diff --git a/pkg/registry/servicecatalog/instance/storage.go b/pkg/registry/servicecatalog/instance/storage.go index 049eebd200db..b7365093bc10 100644 --- a/pkg/registry/servicecatalog/instance/storage.go +++ b/pkg/registry/servicecatalog/instance/storage.go @@ -88,8 +88,21 @@ func Match(label labels.Selector, field fields.Selector) storage.SelectionPredic // toSelectableFields returns a field set that represents the object for matching purposes. func toSelectableFields(instance *servicecatalog.ServiceInstance) fields.Set { + // If you add a new selectable field, you also need to modify + // pkg/apis/servicecatalog/v1beta1/conversion[_test].go objectMetaFieldsSet := generic.ObjectMetaFieldsSet(&instance.ObjectMeta, true) - return generic.MergeFieldsSets(objectMetaFieldsSet, nil) + + specFieldSet := make(fields.Set, 2) + + if instance.Spec.ClusterServiceClassRef != nil { + specFieldSet["spec.clusterServiceClassRef.name"] = instance.Spec.ClusterServiceClassRef.Name + } + + if instance.Spec.ClusterServicePlanRef != nil { + specFieldSet["spec.clusterServicePlanRef.name"] = instance.Spec.ClusterServicePlanRef.Name + } + + return generic.MergeFieldsSets(objectMetaFieldsSet, specFieldSet) } // GetAttrs returns labels and fields of a given object for filtering purposes. @@ -137,6 +150,10 @@ func NewStorage(opts server.Options) (rest.Storage, rest.Storage, rest.Storage) Storage: storageInterface, DestroyFunc: dFunc, } + options := &generic.StoreOptions{RESTOptions: opts.EtcdOptions.RESTOptions, AttrFunc: GetAttrs} + if err := store.CompleteWithOptions(options); err != nil { + panic(err) // TODO: Propagate error up + } statusStore := store statusStore.UpdateStrategy = instanceStatusUpdateStrategy diff --git a/pkg/registry/servicecatalog/instance/strategy.go b/pkg/registry/servicecatalog/instance/strategy.go index d06323071073..997edccbc7fc 100644 --- a/pkg/registry/servicecatalog/instance/strategy.go +++ b/pkg/registry/servicecatalog/instance/strategy.go @@ -154,7 +154,7 @@ func (instanceRESTStrategy) PrepareForUpdate(ctx genericapirequest.Context, new, newServiceInstance.Spec.ClusterServicePlanRef = oldServiceInstance.Spec.ClusterServicePlanRef // Clear out the ClusterServicePlanRef so that it is resolved during reconciliation - if newServiceInstance.Spec.ExternalClusterServicePlanName != oldServiceInstance.Spec.ExternalClusterServicePlanName { + if newServiceInstance.Spec.ClusterServicePlanExternalName != oldServiceInstance.Spec.ClusterServicePlanExternalName { newServiceInstance.Spec.ClusterServicePlanRef = nil } diff --git a/pkg/registry/servicecatalog/instance/strategy_test.go b/pkg/registry/servicecatalog/instance/strategy_test.go index 63625c3c2f9e..7d4741614bba 100644 --- a/pkg/registry/servicecatalog/instance/strategy_test.go +++ b/pkg/registry/servicecatalog/instance/strategy_test.go @@ -24,7 +24,6 @@ import ( "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog" scfeatures "github.com/kubernetes-incubator/service-catalog/pkg/features" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apiserver/pkg/authentication/user" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" @@ -37,11 +36,11 @@ func getTestInstance() *servicecatalog.ServiceInstance { }, Spec: servicecatalog.ServiceInstanceSpec{ PlanReference: servicecatalog.PlanReference{ - ExternalClusterServiceClassName: "test-serviceclass", - ExternalClusterServicePlanName: "test-plan", + ClusterServiceClassExternalName: "test-serviceclass", + ClusterServicePlanExternalName: "test-plan", }, - ClusterServiceClassRef: &corev1.ObjectReference{}, - ClusterServicePlanRef: &corev1.ObjectReference{}, + ClusterServiceClassRef: &servicecatalog.ClusterObjectReference{}, + ClusterServicePlanRef: &servicecatalog.ClusterObjectReference{}, UserInfo: &servicecatalog.UserInfo{ Username: "some-user", }, @@ -98,7 +97,7 @@ func TestInstanceUpdate(t *testing.T) { older: getTestInstance(), newer: func() *servicecatalog.ServiceInstance { i := getTestInstance() - i.Spec.ExternalClusterServicePlanName = "new-test-plan" + i.Spec.ClusterServicePlanExternalName = "new-test-plan" return i }(), shouldGenerationIncrement: true, diff --git a/pkg/registry/servicecatalog/rest/storage_servicecatalog_test.go b/pkg/registry/servicecatalog/rest/storage_servicecatalog_test.go index c2e3409be478..3976b851476e 100644 --- a/pkg/registry/servicecatalog/rest/storage_servicecatalog_test.go +++ b/pkg/registry/servicecatalog/rest/storage_servicecatalog_test.go @@ -36,7 +36,8 @@ type GetRESTOptionsHelper struct { func (g GetRESTOptionsHelper) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) { return generic.RESTOptions{ - StorageConfig: &storagebackend.Config{}, + ResourcePrefix: resource.Group + "/" + resource.Resource, + StorageConfig: &storagebackend.Config{}, Decorator: generic.StorageDecorator(func( copier runtime.ObjectCopier, config *storagebackend.Config, diff --git a/pkg/registry/servicecatalog/serviceclass/storage.go b/pkg/registry/servicecatalog/serviceclass/storage.go index d2c50096a1d7..8b5e44c60fb5 100644 --- a/pkg/registry/servicecatalog/serviceclass/storage.go +++ b/pkg/registry/servicecatalog/serviceclass/storage.go @@ -146,6 +146,11 @@ func NewStorage(opts server.Options) (rest.Storage, rest.Storage) { DestroyFunc: dFunc, } + options := &generic.StoreOptions{RESTOptions: opts.EtcdOptions.RESTOptions, AttrFunc: GetAttrs} + if err := store.CompleteWithOptions(options); err != nil { + panic(err) // TODO: Propagate error up + } + statusStore := store statusStore.UpdateStrategy = serviceClassStatusUpdateStrategy diff --git a/pkg/version.go b/pkg/version.go index f85b3e0765d1..9661664e266d 100644 --- a/pkg/version.go +++ b/pkg/version.go @@ -19,30 +19,14 @@ package pkg import ( "fmt" "os" - - "github.com/spf13/pflag" ) // VERSION is the version string for built artifacts. It's set by the build system, and should // not be changed in this codebase var VERSION = "UNKNOWN" -// Version decides whether we should print the version and leave. -type Version struct { - print bool -} - -// VersionFlag creates the version flag for your application. -func VersionFlag(fs *pflag.FlagSet) *Version { - v := Version{} - fs.BoolVar(&v.print, "version", false, "Print version information and quit") - return &v -} - -// PrintAndExitIfRequested will print the version if requested, and exit. -func (v Version) PrintAndExitIfRequested() { - if v.print { - fmt.Println(VERSION) - os.Exit(0) - } +// PrintAndExit will print the version and exit. +func PrintAndExit() { + fmt.Println(VERSION) + os.Exit(0) } diff --git a/pkg/version/base.go b/pkg/version/base.go new file mode 100644 index 000000000000..905ca514ff6a --- /dev/null +++ b/pkg/version/base.go @@ -0,0 +1,59 @@ +/* +Copyright 2017 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 version + +// Base version information. +// +// This is the fallback data used when version information from git is not +// provided via go ldflags. It provides an approximation of the service catalog +// version for ad-hoc builds (e.g. `go build`) that cannot get the version +// information from git. +// +// If you are looking at these fields in the git tree, they look +// strange. They are modified on the fly by the build process. The +// in-tree values are dummy values used for "git archive", which also +// works for GitHub tar downloads. +// +// When releasing a new Kubernetes version, this file is updated by +// build/mark_new_version.sh to reflect the new version, and then a +// git annotated tag (using format vX.Y where X == Major version and Y +// == Minor version) is created to point to the commit that updates +// pkg/version/base.go +var ( + // TODO: Deprecate gitMajor and gitMinor, use only gitVersion + // instead. First step in deprecation, keep the fields but make + // them irrelevant. (Next we'll take it out, which may muck with + // scripts consuming the kubectl version output - but most of + // these should be looking at gitVersion already anyways.) + gitMajor string // major version, always numeric + gitMinor string // minor version, numeric possibly followed by "+" + + // semantic version, derived by build scripts (see + // https://github.com/kubernetes/community/blob/master/contributors/design-proposals/release/versioning.md + // for a detailed discussion of this field) + // + // TODO: This field is still called "gitVersion" for legacy + // reasons. For prerelease versions, the build metadata on the + // semantic version is a git hash, but the version itself is no + // longer the direct output of "git describe", but a slight + // translation to be semver compliant. + gitVersion = "v0.0.0-master+$Format:%h$" + gitCommit = "$Format:%H$" // sha1 from git, output of $(git rev-parse HEAD) + gitTreeState = "not a git tree" // state of git tree, either "clean" or "dirty" + + buildDate = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ') +) diff --git a/pkg/version/version.go b/pkg/version/version.go index 0959ac77e4cd..d104aba129be 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -31,13 +31,13 @@ func Get() apimachineryversion.Info { return apimachineryversion.Info{ Major: "", // deprecated Minor: "", // deprecated - // TODO: we need to update these values when building a release. - // GitVersion: "", - // GitCommit: "", - // GitTreeState: "", - // BuildDate: "1970-01-01T00:00:00Z", - GoVersion: runtime.Version(), - Compiler: runtime.Compiler, - Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), + // The following four are filled in by the Makefile + GitVersion: gitVersion, + GitCommit: gitCommit, + GitTreeState: gitTreeState, + BuildDate: buildDate, + GoVersion: runtime.Version(), + Compiler: runtime.Compiler, + Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), } } diff --git a/plugin/pkg/admission/broker/authsarcheck/admission.go b/plugin/pkg/admission/broker/authsarcheck/admission.go index af86fbbe450a..93bd6decc8ed 100644 --- a/plugin/pkg/admission/broker/authsarcheck/admission.go +++ b/plugin/pkg/admission/broker/authsarcheck/admission.go @@ -85,14 +85,11 @@ func (s *sarcheck) Admit(a admission.Attributes) error { return nil } - var secretRef *corev1.ObjectReference + var secretRef *servicecatalog.ObjectReference if clusterClusterServiceBroker.Spec.AuthInfo.Basic != nil { secretRef = clusterClusterServiceBroker.Spec.AuthInfo.Basic.SecretRef } else if clusterClusterServiceBroker.Spec.AuthInfo.Bearer != nil { secretRef = clusterClusterServiceBroker.Spec.AuthInfo.Bearer.SecretRef - } else if clusterClusterServiceBroker.Spec.AuthInfo.BasicAuthSecret != nil { - // TODO: this field is deprecated, remove in v1beta1 - secretRef = clusterClusterServiceBroker.Spec.AuthInfo.BasicAuthSecret } userInfo := a.GetUserInfo() @@ -109,12 +106,10 @@ func (s *sarcheck) Admit(a admission.Attributes) error { User: userInfo.GetName(), Groups: userInfo.GetGroups(), Extra: convertToSARExtra(userInfo.GetExtra()), - // TODO: uncomment after rebase onto Kubernetes 1.8. - // See https://github.com/kubernetes/kubernetes/pull/49677 - //UID: userInfo.GetUID(), + UID: userInfo.GetUID(), }, } - sar, err := s.client.Authorization().SubjectAccessReviews().Create(sar) + sar, err := s.client.AuthorizationV1().SubjectAccessReviews().Create(sar) if err != nil { return err } diff --git a/plugin/pkg/admission/broker/authsarcheck/admission_test.go b/plugin/pkg/admission/broker/authsarcheck/admission_test.go index 24feaad89899..f74280d2c0e3 100644 --- a/plugin/pkg/admission/broker/authsarcheck/admission_test.go +++ b/plugin/pkg/admission/broker/authsarcheck/admission_test.go @@ -27,7 +27,6 @@ import ( "k8s.io/apiserver/pkg/authentication/user" authorizationapi "k8s.io/api/authorization/v1" - corev1 "k8s.io/api/core/v1" kubeinformers "k8s.io/client-go/informers" kubeclientset "k8s.io/client-go/kubernetes" kubefake "k8s.io/client-go/kubernetes/fake" @@ -109,7 +108,7 @@ func TestAdmissionBroker(t *testing.T) { URL: "http://example.com", AuthInfo: &servicecatalog.ServiceBrokerAuthInfo{ Basic: &servicecatalog.BasicAuthConfig{ - SecretRef: &corev1.ObjectReference{ + SecretRef: &servicecatalog.ObjectReference{ Namespace: "test-ns", Name: "test-secret", }, @@ -123,28 +122,6 @@ func TestAdmissionBroker(t *testing.T) { }, allowed: true, }, - { - name: "broker with basic auth, user authenticated (deprecated authinfo field)", - broker: &servicecatalog.ClusterServiceBroker{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-broker", - }, - Spec: servicecatalog.ClusterServiceBrokerSpec{ - URL: "http://example.com", - AuthInfo: &servicecatalog.ServiceBrokerAuthInfo{ - BasicAuthSecret: &corev1.ObjectReference{ - Namespace: "test-ns", - Name: "test-secret", - }, - }, - }, - }, - userInfo: &user.DefaultInfo{ - Name: "system:serviceaccount:test-ns:catalog", - Groups: []string{"system:serviceaccount", "system:serviceaccounts:test-ns"}, - }, - allowed: true, - }, { name: "broker with bearer token, user authenticated", broker: &servicecatalog.ClusterServiceBroker{ @@ -155,7 +132,7 @@ func TestAdmissionBroker(t *testing.T) { URL: "http://example.com", AuthInfo: &servicecatalog.ServiceBrokerAuthInfo{ Bearer: &servicecatalog.BearerTokenAuthConfig{ - SecretRef: &corev1.ObjectReference{ + SecretRef: &servicecatalog.ObjectReference{ Namespace: "test-ns", Name: "test-secret", }, @@ -179,7 +156,7 @@ func TestAdmissionBroker(t *testing.T) { URL: "http://example.com", AuthInfo: &servicecatalog.ServiceBrokerAuthInfo{ Bearer: &servicecatalog.BearerTokenAuthConfig{ - SecretRef: &corev1.ObjectReference{ + SecretRef: &servicecatalog.ObjectReference{ Namespace: "test-ns", Name: "test-secret", }, diff --git a/plugin/pkg/admission/namespace/lifecycle/admission.go b/plugin/pkg/admission/namespace/lifecycle/admission.go index 39702ae66ba7..ba590aca723a 100644 --- a/plugin/pkg/admission/namespace/lifecycle/admission.go +++ b/plugin/pkg/admission/namespace/lifecycle/admission.go @@ -113,7 +113,7 @@ func (l *lifecycle) Admit(a admission.Attributes) error { // refuse to operate on non-existent namespaces if !exists { // as a last resort, make a call directly to storage - namespace, err = l.client.Core().Namespaces().Get(a.GetNamespace(), metav1.GetOptions{}) + namespace, err = l.client.CoreV1().Namespaces().Get(a.GetNamespace(), metav1.GetOptions{}) switch { case errors.IsNotFound(err): return err diff --git a/plugin/pkg/admission/servicebindings/lifecycle/admission_test.go b/plugin/pkg/admission/servicebindings/lifecycle/admission_test.go index aa0869e97152..056261b21bcf 100644 --- a/plugin/pkg/admission/servicebindings/lifecycle/admission_test.go +++ b/plugin/pkg/admission/servicebindings/lifecycle/admission_test.go @@ -20,7 +20,6 @@ import ( "testing" "time" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/wait" @@ -63,7 +62,7 @@ func newServiceBinding() servicecatalog.ServiceBinding { Namespace: "test-ns", }, Spec: servicecatalog.ServiceBindingSpec{ - ServiceInstanceRef: corev1.LocalObjectReference{ + ServiceInstanceRef: servicecatalog.LocalObjectReference{ Name: "test-instance", }, SecretName: "test-secret", diff --git a/plugin/pkg/admission/serviceplan/changevalidator/admission.go b/plugin/pkg/admission/serviceplan/changevalidator/admission.go index 09b563dc55bf..61b455c9c779 100644 --- a/plugin/pkg/admission/serviceplan/changevalidator/admission.go +++ b/plugin/pkg/admission/serviceplan/changevalidator/admission.go @@ -72,10 +72,10 @@ func (d *denyPlanChangeIfNotUpdatable) Admit(a admission.Attributes) error { return apierrors.NewBadRequest("Resource was marked with kind Instance but was unable to be converted") } - sc, err := d.scLister.Get(instance.Spec.ExternalClusterServiceClassName) + sc, err := d.scLister.Get(instance.Spec.ClusterServiceClassExternalName) if err != nil { if apierrors.IsNotFound(err) { - glog.V(5).Infof("Could not locate service class %v, can not determine if UpdateablePlan.", instance.Spec.ExternalClusterServiceClassName) + glog.V(5).Infof("Could not locate service class %v, can not determine if UpdateablePlan.", instance.Spec.ClusterServiceClassExternalName) return nil // should this be `return err`? why would we allow the instance in if we cannot determine it is updatable? } glog.Error(err) @@ -86,15 +86,15 @@ func (d *denyPlanChangeIfNotUpdatable) Admit(a admission.Attributes) error { return nil } - if instance.Spec.ExternalClusterServicePlanName != "" { + if instance.Spec.ClusterServicePlanExternalName != "" { lister := d.instanceLister.ServiceInstances(instance.Namespace) origInstance, err := lister.Get(instance.Name) if err != nil { glog.Errorf("Error locating instance %v/%v", instance.Namespace, instance.Name) return err } - if instance.Spec.ExternalClusterServicePlanName != origInstance.Spec.ExternalClusterServicePlanName { - glog.V(4).Infof("update Service Instance %v/%v request specified Plan Name %v while original instance had %v", instance.Namespace, instance.Name, instance.Spec.ExternalClusterServicePlanName, origInstance.Spec.ExternalClusterServicePlanName) + if instance.Spec.ClusterServicePlanExternalName != origInstance.Spec.ClusterServicePlanExternalName { + glog.V(4).Infof("update Service Instance %v/%v request specified Plan Name %v while original instance had %v", instance.Namespace, instance.Name, instance.Spec.ClusterServicePlanExternalName, origInstance.Spec.ClusterServicePlanExternalName) msg := fmt.Sprintf("The Service Class %v does not allow plan changes.", sc.Name) glog.Error(msg) return admission.NewForbidden(a, errors.New(msg)) diff --git a/plugin/pkg/admission/serviceplan/changevalidator/admission_test.go b/plugin/pkg/admission/serviceplan/changevalidator/admission_test.go index 6ab90db0eab2..0ab1bfb03abb 100644 --- a/plugin/pkg/admission/serviceplan/changevalidator/admission_test.go +++ b/plugin/pkg/admission/serviceplan/changevalidator/admission_test.go @@ -69,8 +69,8 @@ func newServiceInstance(namespace string, serviceClassName string, planName stri instance := servicecatalog.ServiceInstance{ ObjectMeta: metav1.ObjectMeta{Name: "instance", Namespace: namespace}, } - instance.Spec.ExternalClusterServiceClassName = serviceClassName - instance.Spec.ExternalClusterServicePlanName = planName + instance.Spec.ClusterServiceClassExternalName = serviceClassName + instance.Spec.ClusterServicePlanExternalName = planName return instance } diff --git a/plugin/pkg/admission/serviceplan/defaultserviceplan/admission.go b/plugin/pkg/admission/serviceplan/defaultserviceplan/admission.go index 2b49ca8781a9..98b8194fe1c4 100644 --- a/plugin/pkg/admission/serviceplan/defaultserviceplan/admission.go +++ b/plugin/pkg/admission/serviceplan/defaultserviceplan/admission.go @@ -73,7 +73,7 @@ func (d *defaultServicePlan) Admit(a admission.Attributes) error { // If the plan is specified, let it through and have the controller // deal with finding the right plan, etc. - if instance.Spec.ExternalClusterServicePlanName != "" || instance.Spec.ClusterServicePlanName != "" { + if instance.Spec.ClusterServicePlanExternalName != "" || instance.Spec.ClusterServicePlanName != "" { return nil } @@ -83,7 +83,7 @@ func (d *defaultServicePlan) Admit(a admission.Attributes) error { if !apierrors.IsNotFound(err) { return admission.NewForbidden(a, err) } - msg := fmt.Sprintf("ServiceClass %q does not exist, can not figure out the default Service Plan.", instance.Spec.ExternalClusterServiceClassName) + msg := fmt.Sprintf("ServiceClass %q does not exist, can not figure out the default Service Plan.", instance.Spec.ClusterServiceClassExternalName) glog.V(4).Info(msg) return admission.NewForbidden(a, errors.New(msg)) } @@ -98,7 +98,7 @@ func (d *defaultServicePlan) Admit(a admission.Attributes) error { plans, err := d.getClusterServicePlansByClusterServiceClassName(sc.Name) if err != nil { - msg := fmt.Sprintf("Error listing plans for service class (K8S: %v ExternalName: %v) - retry and specify desired ClusterServicePlan", sc.Name, instance.Spec.ExternalClusterServiceClassName) + msg := fmt.Sprintf("Error listing plans for service class (K8S: %v ExternalName: %v) - retry and specify desired ClusterServicePlan", sc.Name, instance.Spec.ClusterServiceClassExternalName) glog.V(4).Info(msg) return admission.NewForbidden(a, errors.New(msg)) } @@ -106,14 +106,14 @@ func (d *defaultServicePlan) Admit(a admission.Attributes) error { // check if there were any service plans // TODO: in combination with not allowing classes with no plans, this should be impossible if len(plans) <= 0 { - msg := fmt.Sprintf("no plans found at all for service class %q", instance.Spec.ExternalClusterServiceClassName) + msg := fmt.Sprintf("no plans found at all for service class %q", instance.Spec.ClusterServiceClassExternalName) glog.V(4).Info(msg) return admission.NewForbidden(a, errors.New(msg)) } // check if more than one service plan was specified and error if len(plans) > 1 { - msg := fmt.Sprintf("ServiceClass %q has more than one plan, PlanName must be specified", instance.Spec.ExternalClusterServiceClassName) + msg := fmt.Sprintf("ServiceClass %q has more than one plan, PlanName must be specified", instance.Spec.ClusterServiceClassExternalName) glog.V(4).Info(msg) return admission.NewForbidden(a, errors.New(msg)) } @@ -122,8 +122,8 @@ func (d *defaultServicePlan) Admit(a admission.Attributes) error { p := plans[0] glog.V(4).Infof("Using default plan %q (K8S: %q) for Service Class %q for instance %s", p.Spec.ExternalName, p.Name, sc.Spec.ExternalName, instance.Name) - if instance.Spec.ExternalClusterServiceClassName != "" { - instance.Spec.ExternalClusterServicePlanName = p.Spec.ExternalName + if instance.Spec.ClusterServiceClassExternalName != "" { + instance.Spec.ClusterServicePlanExternalName = p.Spec.ExternalName } else { instance.Spec.ClusterServicePlanName = p.Name } @@ -156,8 +156,8 @@ func (d *defaultServicePlan) Validate() error { } func (d *defaultServicePlan) getClusterServiceClassByPlanReference(a admission.Attributes, ref *servicecatalog.PlanReference) (*servicecatalog.ClusterServiceClass, error) { - if ref.ExternalClusterServiceClassName != "" { - return d.getClusterServiceClassByExternalName(a, ref.ExternalClusterServiceClassName) + if ref.ClusterServiceClassExternalName != "" { + return d.getClusterServiceClassByExternalName(a, ref.ClusterServiceClassExternalName) } return d.getClusterServiceClassByK8SName(a, ref.ClusterServiceClassName) } diff --git a/plugin/pkg/admission/serviceplan/defaultserviceplan/admission_test.go b/plugin/pkg/admission/serviceplan/defaultserviceplan/admission_test.go index d88a418f1376..088e5573f1d2 100644 --- a/plugin/pkg/admission/serviceplan/defaultserviceplan/admission_test.go +++ b/plugin/pkg/admission/serviceplan/defaultserviceplan/admission_test.go @@ -24,7 +24,6 @@ import ( "github.com/golang/glog" - "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -129,7 +128,7 @@ func newClusterServicePlans(count uint, useDifferentClasses bool) []*servicecata Spec: servicecatalog.ClusterServicePlanSpec{ ExternalName: "bar", ExternalID: "12345", - ClusterServiceClassRef: v1.LocalObjectReference{ + ClusterServiceClassRef: servicecatalog.ClusterObjectReference{ Name: classname, }, }, @@ -142,7 +141,7 @@ func newClusterServicePlans(count uint, useDifferentClasses bool) []*servicecata Spec: servicecatalog.ClusterServicePlanSpec{ ExternalName: "baz", ExternalID: "23456", - ClusterServiceClassRef: v1.LocalObjectReference{ + ClusterServiceClassRef: servicecatalog.ClusterObjectReference{ Name: classname, }, }, @@ -172,7 +171,7 @@ func TestWithListFailure(t *testing.T) { informerFactory.Start(wait.NeverStop) instance := newServiceInstance("dummy") - instance.Spec.ExternalClusterServiceClassName = "foo" + instance.Spec.ClusterServiceClassExternalName = "foo" err = handler.Admit(admission.NewAttributesRecord(&instance, nil, servicecatalog.Kind("ServiceInstance").WithVersion("version"), instance.Namespace, instance.Name, servicecatalog.Resource("serviceinstances").WithVersion("version"), "", admission.Create, nil)) if err == nil { @@ -181,7 +180,7 @@ func TestWithListFailure(t *testing.T) { t.Errorf("did not find expected error, got %q", err) } assertPlanReference(t, - servicecatalog.PlanReference{ExternalClusterServiceClassName: "foo"}, + servicecatalog.PlanReference{ClusterServiceClassExternalName: "foo"}, instance.Spec.PlanReference) } @@ -194,8 +193,8 @@ func TestWithPlanWorks(t *testing.T) { informerFactory.Start(wait.NeverStop) instance := newServiceInstance("dummy") - instance.Spec.ExternalClusterServiceClassName = "foo" - instance.Spec.ExternalClusterServicePlanName = "bar" + instance.Spec.ClusterServiceClassExternalName = "foo" + instance.Spec.ClusterServicePlanExternalName = "bar" err = handler.Admit(admission.NewAttributesRecord(&instance, nil, servicecatalog.Kind("ServiceInstance").WithVersion("version"), instance.Namespace, instance.Name, servicecatalog.Resource("serviceinstances").WithVersion("version"), "", admission.Create, nil)) if err != nil { @@ -206,7 +205,7 @@ func TestWithPlanWorks(t *testing.T) { t.Errorf("unexpected error %q returned from admission handler: %v", err, actions) } assertPlanReference(t, - servicecatalog.PlanReference{ExternalClusterServiceClassName: "foo", ExternalClusterServicePlanName: "bar"}, + servicecatalog.PlanReference{ClusterServiceClassExternalName: "foo", ClusterServicePlanExternalName: "bar"}, instance.Spec.PlanReference) } @@ -219,7 +218,7 @@ func TestWithNoPlanFailsWithNoClusterServiceClass(t *testing.T) { informerFactory.Start(wait.NeverStop) instance := newServiceInstance("dummy") - instance.Spec.ExternalClusterServiceClassName = "foobar" + instance.Spec.ClusterServiceClassExternalName = "foobar" err = handler.Admit(admission.NewAttributesRecord(&instance, nil, servicecatalog.Kind("ServiceInstance").WithVersion("version"), instance.Namespace, instance.Name, servicecatalog.Resource("serviceinstances").WithVersion("version"), "", admission.Create, nil)) if err == nil { @@ -228,7 +227,7 @@ func TestWithNoPlanFailsWithNoClusterServiceClass(t *testing.T) { t.Errorf("did not find expected error, got %q", err) } assertPlanReference(t, - servicecatalog.PlanReference{ExternalClusterServiceClassName: "foobar"}, + servicecatalog.PlanReference{ClusterServiceClassExternalName: "foobar"}, instance.Spec.PlanReference) } @@ -246,7 +245,7 @@ func TestWithNoPlanWorksWithSinglePlan(t *testing.T) { informerFactory.Start(wait.NeverStop) instance := newServiceInstance("dummy") - instance.Spec.ExternalClusterServiceClassName = "foo" + instance.Spec.ClusterServiceClassExternalName = "foo" err = handler.Admit(admission.NewAttributesRecord(&instance, nil, servicecatalog.Kind("ServiceInstance").WithVersion("version"), instance.Namespace, instance.Name, servicecatalog.Resource("serviceinstances").WithVersion("version"), "", admission.Create, nil)) if err != nil { @@ -257,7 +256,7 @@ func TestWithNoPlanWorksWithSinglePlan(t *testing.T) { t.Errorf("unexpected error %q returned from admission handler: %v", err, actions) } assertPlanReference(t, - servicecatalog.PlanReference{ExternalClusterServiceClassName: "foo", ExternalClusterServicePlanName: "bar"}, + servicecatalog.PlanReference{ClusterServiceClassExternalName: "foo", ClusterServicePlanExternalName: "bar"}, instance.Spec.PlanReference) } @@ -274,7 +273,7 @@ func TestWithNoPlanFailsWithMultiplePlans(t *testing.T) { informerFactory.Start(wait.NeverStop) instance := newServiceInstance("dummy") - instance.Spec.ExternalClusterServiceClassName = "foo" + instance.Spec.ClusterServiceClassExternalName = "foo" err = handler.Admit(admission.NewAttributesRecord(&instance, nil, servicecatalog.Kind("ServiceInstance").WithVersion("version"), instance.Namespace, instance.Name, servicecatalog.Resource("serviceinstances").WithVersion("version"), "", admission.Create, nil)) if err == nil { @@ -284,7 +283,7 @@ func TestWithNoPlanFailsWithMultiplePlans(t *testing.T) { t.Errorf("did not find expected error, got %q", err) } assertPlanReference(t, - servicecatalog.PlanReference{ExternalClusterServiceClassName: "foo"}, + servicecatalog.PlanReference{ClusterServiceClassExternalName: "foo"}, instance.Spec.PlanReference) } @@ -303,7 +302,7 @@ func TestWithNoPlanSucceedsWithMultiplePlansFromDifferentClasses(t *testing.T) { informerFactory.Start(wait.NeverStop) instance := newServiceInstance("dummy") - instance.Spec.ExternalClusterServiceClassName = "foo" + instance.Spec.ClusterServiceClassExternalName = "foo" err = handler.Admit(admission.NewAttributesRecord(&instance, nil, servicecatalog.Kind("ServiceInstance").WithVersion("version"), instance.Namespace, instance.Name, servicecatalog.Resource("serviceinstances").WithVersion("version"), "", admission.Create, nil)) if err != nil { @@ -314,7 +313,7 @@ func TestWithNoPlanSucceedsWithMultiplePlansFromDifferentClasses(t *testing.T) { t.Errorf("unexpected error %q returned from admission handler: %v", err, actions) } assertPlanReference(t, - servicecatalog.PlanReference{ExternalClusterServiceClassName: "foo", ExternalClusterServicePlanName: "bar"}, + servicecatalog.PlanReference{ClusterServiceClassExternalName: "foo", ClusterServicePlanExternalName: "bar"}, instance.Spec.PlanReference) } diff --git a/test/e2e/framework/util.go b/test/e2e/framework/util.go index 2155675b400d..79ad8b5f7775 100644 --- a/test/e2e/framework/util.go +++ b/test/e2e/framework/util.go @@ -110,7 +110,7 @@ func CreateKubeNamespace(baseName string, c kubernetes.Interface) (*corev1.Names var got *corev1.Namespace err := wait.PollImmediate(Poll, defaultTimeout, func() (bool, error) { var err error - got, err = c.Core().Namespaces().Create(ns) + got, err = c.CoreV1().Namespaces().Create(ns) if err != nil { Logf("Unexpected error while creating namespace: %v", err) return false, nil @@ -124,7 +124,7 @@ func CreateKubeNamespace(baseName string, c kubernetes.Interface) (*corev1.Names } func DeleteKubeNamespace(c kubernetes.Interface, namespace string) error { - return c.Core().Namespaces().Delete(namespace, nil) + return c.CoreV1().Namespaces().Delete(namespace, nil) } func ExpectNoError(err error, explain ...interface{}) { @@ -171,7 +171,7 @@ func endpointAvailable(c kubernetes.Interface, namespace, name string) wait.Cond func podRunning(c kubernetes.Interface, podName, namespace string) wait.ConditionFunc { return func() (bool, error) { - pod, err := c.Core().Pods(namespace).Get(podName, metav1.GetOptions{}) + pod, err := c.CoreV1().Pods(namespace).Get(podName, metav1.GetOptions{}) if err != nil { return false, err } diff --git a/test/e2e/instance.go b/test/e2e/instance.go index 249e7213fa61..f11c2ca19ecb 100644 --- a/test/e2e/instance.go +++ b/test/e2e/instance.go @@ -42,8 +42,8 @@ func newTestInstance(name, serviceClassName, planName string) *v1beta1.ServiceIn }, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServicePlanName: planName, - ExternalClusterServiceClassName: serviceClassName, + ClusterServicePlanExternalName: planName, + ClusterServiceClassExternalName: serviceClassName, }, }, } diff --git a/test/e2e/walkthrough.go b/test/e2e/walkthrough.go index 7db870c1fcc5..d9d5656d6935 100644 --- a/test/e2e/walkthrough.go +++ b/test/e2e/walkthrough.go @@ -20,7 +20,6 @@ import ( v1beta1 "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" "github.com/kubernetes-incubator/service-catalog/test/e2e/framework" "github.com/kubernetes-incubator/service-catalog/test/util" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" . "github.com/onsi/ginkgo" @@ -127,8 +126,8 @@ var _ = framework.ServiceCatalogDescribe("walkthrough", func() { }, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServiceClassName: serviceclassName, - ExternalClusterServicePlanName: "default", + ClusterServiceClassExternalName: serviceclassName, + ClusterServicePlanExternalName: "default", }, }, } @@ -164,7 +163,7 @@ var _ = framework.ServiceCatalogDescribe("walkthrough", func() { Namespace: testnamespace.Name, }, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: corev1.LocalObjectReference{ + ServiceInstanceRef: v1beta1.LocalObjectReference{ Name: instanceName, }, SecretName: "my-secret", @@ -219,7 +218,7 @@ var _ = framework.ServiceCatalogDescribe("walkthrough", func() { }, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServiceClassName: serviceclassName, + ClusterServiceClassExternalName: serviceclassName, }, }, } diff --git a/test/integration.sh b/test/integration.sh index 5182899b58d5..5c73cea41971 100755 --- a/test/integration.sh +++ b/test/integration.sh @@ -26,11 +26,11 @@ runTests() { kube::etcd::start go test -race -i github.com/kubernetes-incubator/service-catalog/test/integration/... -c \ - && ./integration.test -test.v + && ./integration.test -test.v $@ } # Run cleanup to stop etcd on interrupt or other kill signal. trap kube::etcd::cleanup EXIT -runTests +runTests $@ diff --git a/test/integration/clientset_test.go b/test/integration/clientset_test.go index 1f55cb528961..9ccda2d25e2c 100644 --- a/test/integration/clientset_test.go +++ b/test/integration/clientset_test.go @@ -28,7 +28,6 @@ import ( "testing" "github.com/kubernetes-incubator/service-catalog/pkg/registry/servicecatalog/server" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" // our versioned types @@ -281,7 +280,7 @@ func testBrokerClient(sType server.StorageType, client servicecatalogclient.Inte ) } - authSecret := &corev1.ObjectReference{ + authSecret := &v1beta1.ObjectReference{ Namespace: "test-namespace", Name: "test-name", } @@ -619,7 +618,7 @@ func testClusterServicePlanClient(sType server.StorageType, client servicecatalo ExternalName: name, ExternalID: "b8269ab4-7d2d-456d-8c8b-5aab63b321d1", Description: "test description", - ClusterServiceClassRef: corev1.LocalObjectReference{ + ClusterServiceClassRef: v1beta1.ClusterObjectReference{ Name: "test-serviceclass", }, }, @@ -717,7 +716,7 @@ func testClusterServicePlanClient(sType server.StorageType, client servicecatalo ExternalName: sp2Name, ExternalID: sp2ID, Description: "test description 2", - ClusterServiceClassRef: corev1.LocalObjectReference{ + ClusterServiceClassRef: v1beta1.ClusterObjectReference{ Name: "test-serviceclass", }, }, @@ -825,8 +824,14 @@ func testInstanceClient(sType server.StorageType, client servicecatalogclient.In ObjectMeta: metav1.ObjectMeta{Name: name}, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServiceClassName: "service-class-name", - ExternalClusterServicePlanName: "plan-name", + ClusterServiceClassExternalName: "service-class-name", + ClusterServicePlanExternalName: "plan-name", + }, + ClusterServiceClassRef: &v1beta1.ClusterObjectReference{ + Name: "test-serviceclass", + }, + ClusterServicePlanRef: &v1beta1.ClusterObjectReference{ + Name: "test-serviceplan", }, Parameters: &runtime.RawExtension{Raw: []byte(instanceParameter)}, ExternalID: osbGUID, @@ -933,9 +938,9 @@ func testInstanceClient(sType server.StorageType, client servicecatalogclient.In } // Update the instance references - classRef := &corev1.ObjectReference{Name: "service-class-ref"} + classRef := &v1beta1.ClusterObjectReference{Name: "service-class-ref"} instanceServer.Spec.ClusterServiceClassRef = classRef - planRef := &corev1.ObjectReference{Name: "service-plan-ref"} + planRef := &v1beta1.ClusterObjectReference{Name: "service-plan-ref"} instanceServer.Spec.ClusterServicePlanRef = planRef returnedInstance, err := instanceClient.UpdateReferences(instanceServer) if err != nil { @@ -968,6 +973,34 @@ func testInstanceClient(sType server.StorageType, client servicecatalogclient.In return fmt.Errorf("Generation was changed, expected: %q got: %q", oldGeneration, instanceServer.Generation) } + // field selector tests + instances, err = instanceClient.List(metav1.ListOptions{FieldSelector: "spec.clusterServiceClassRef.name=should-return-zero"}) + if err != nil { + return fmt.Errorf("error listing instances: %v", err) + } + + if 0 != len(instances.Items) { + return fmt.Errorf("should have exactly zero instances, had %v instances", len(instances.Items)) + } + + instances, err = instanceClient.List(metav1.ListOptions{FieldSelector: "spec.clusterServiceClassRef.name=service-class-ref"}) + if err != nil { + return fmt.Errorf("error listing instances: %v", err) + } + + if 1 != len(instances.Items) { + return fmt.Errorf("should have exactly one instance, had %v instances", len(instances.Items)) + } + + instances, err = instanceClient.List(metav1.ListOptions{FieldSelector: "spec.clusterServicePlanRef.name=service-plan-ref"}) + if err != nil { + return fmt.Errorf("error listing instances: %v", err) + } + + if 1 != len(instances.Items) { + return fmt.Errorf("should have exactly one instance, had %v instances", len(instances.Items)) + } + // update the instance's spec updateRequests := instanceServer.Spec.UpdateRequests + 1 expectedGeneration := instanceServer.Generation + 1 @@ -1044,7 +1077,7 @@ func testBindingClient(sType server.StorageType, client servicecatalogclient.Int binding := &v1beta1.ServiceBinding{ ObjectMeta: metav1.ObjectMeta{Name: "test-binding"}, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: corev1.LocalObjectReference{ + ServiceInstanceRef: v1beta1.LocalObjectReference{ Name: "bar", }, Parameters: &runtime.RawExtension{Raw: []byte(bindingParameter)}, diff --git a/test/integration/controller_test.go b/test/integration/controller_test.go index b391982ab9f0..cadc87ad8aaf 100644 --- a/test/integration/controller_test.go +++ b/test/integration/controller_test.go @@ -27,7 +27,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/kubernetes/fake" - corev1 "k8s.io/api/core/v1" restclient "k8s.io/client-go/rest" clientgotesting "k8s.io/client-go/testing" "k8s.io/client-go/tools/record" @@ -50,28 +49,23 @@ import ( ) const ( - testNamespace = "test-namespace" - testBrokerName = "test-broker" - testClusterServiceClassName = "test-service" - testClusterServiceClassID = "12345" - testPlanName = "test-plan" - testPlanExternalID = "34567" - testInstanceName = "test-instance" - testBindingName = "test-binding" - testSecretName = "test-secret" - testBrokerURL = "https://example.com" - testExternalID = "9737b6ed-ca95-4439-8219-c53fcad118ab" - testDashboardURL = "http://test-dashboard.example.com" - testCreatorUsername = "create-username" - testUpdaterUsername = "update-username" - testDeleterUsername = "delete-username" + testNamespace = "test-namespace" + testClusterServiceBrokerName = "test-broker" + testClusterServiceClassName = "test-service" + testClusterServiceClassGUID = "12345" + testClusterServicePlanName = "test-plan" + testPlanExternalID = "34567" + testInstanceName = "test-instance" + testBindingName = "test-binding" + testSecretName = "test-secret" + testBrokerURL = "https://example.com" + testExternalID = "9737b6ed-ca95-4439-8219-c53fcad118ab" + testDashboardURL = "http://test-dashboard.example.com" + testCreatorUsername = "create-username" + testUpdaterUsername = "update-username" + testDeleterUsername = "delete-username" ) -func truePtr() *bool { - b := true - return &b -} - // TestBasicFlowsSync tests: // // - add Broker @@ -120,7 +114,7 @@ func TestBasicFlowsSync(t *testing.T) { client := catalogClient.ServicecatalogV1beta1() broker := &v1beta1.ClusterServiceBroker{ - ObjectMeta: metav1.ObjectMeta{Name: testBrokerName}, + ObjectMeta: metav1.ObjectMeta{Name: testClusterServiceBrokerName}, Spec: v1beta1.ClusterServiceBrokerSpec{ URL: testBrokerURL, }, @@ -132,7 +126,7 @@ func TestBasicFlowsSync(t *testing.T) { } err = util.WaitForBrokerCondition(client, - testBrokerName, + testClusterServiceBrokerName, v1beta1.ServiceBrokerCondition{ Type: v1beta1.ServiceBrokerConditionReady, Status: v1beta1.ConditionTrue, @@ -141,7 +135,7 @@ func TestBasicFlowsSync(t *testing.T) { t.Fatalf("error waiting for broker to become ready: %v", err) } - err = util.WaitForClusterServiceClassToExist(client, testClusterServiceClassID) + err = util.WaitForClusterServiceClassToExist(client, testClusterServiceClassGUID) if nil != err { t.Fatalf("error waiting from ClusterServiceClass to exist: %v", err) } @@ -155,8 +149,8 @@ func TestBasicFlowsSync(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testInstanceName}, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServiceClassName: testClusterServiceClassName, - ExternalClusterServicePlanName: testPlanName, + ClusterServiceClassExternalName: testClusterServiceClassName, + ClusterServicePlanExternalName: testClusterServicePlanName, }, ExternalID: testExternalID, }, @@ -210,7 +204,7 @@ func TestBasicFlowsSync(t *testing.T) { binding := &v1beta1.ServiceBinding{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testBindingName}, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: corev1.LocalObjectReference{ + ServiceInstanceRef: v1beta1.LocalObjectReference{ Name: testInstanceName, }, }, @@ -256,7 +250,7 @@ func TestBasicFlowsSync(t *testing.T) { // End provision test // Delete the broker - err = client.ClusterServiceBrokers().Delete(testBrokerName, &metav1.DeleteOptions{}) + err = client.ClusterServiceBrokers().Delete(testClusterServiceBrokerName, &metav1.DeleteOptions{}) if nil != err { t.Fatalf("broker should be deleted (%s)", err) } @@ -266,7 +260,7 @@ func TestBasicFlowsSync(t *testing.T) { t.Fatalf("error waiting for ClusterServiceClass to not exist: %v", err) } - err = util.WaitForBrokerToNotExist(client, testBrokerName) + err = util.WaitForBrokerToNotExist(client, testClusterServiceBrokerName) if err != nil { t.Fatalf("error waiting for Broker to not exist: %v", err) } @@ -315,7 +309,7 @@ func TestBasicFlowsAsync(t *testing.T) { client := catalogClient.ServicecatalogV1beta1() broker := &v1beta1.ClusterServiceBroker{ - ObjectMeta: metav1.ObjectMeta{Name: testBrokerName}, + ObjectMeta: metav1.ObjectMeta{Name: testClusterServiceBrokerName}, Spec: v1beta1.ClusterServiceBrokerSpec{ URL: testBrokerURL, }, @@ -327,7 +321,7 @@ func TestBasicFlowsAsync(t *testing.T) { } err = util.WaitForBrokerCondition(client, - testBrokerName, + testClusterServiceBrokerName, v1beta1.ServiceBrokerCondition{ Type: v1beta1.ServiceBrokerConditionReady, Status: v1beta1.ConditionTrue, @@ -336,7 +330,7 @@ func TestBasicFlowsAsync(t *testing.T) { t.Fatalf("error waiting for broker to become ready: %v", err) } - err = util.WaitForClusterServiceClassToExist(client, testClusterServiceClassID) + err = util.WaitForClusterServiceClassToExist(client, testClusterServiceClassGUID) if nil != err { t.Fatalf("error waiting from ClusterServiceClass to exist: %v", err) } @@ -350,8 +344,8 @@ func TestBasicFlowsAsync(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testInstanceName}, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServiceClassName: testClusterServiceClassName, - ExternalClusterServicePlanName: testPlanName, + ClusterServiceClassExternalName: testClusterServiceClassName, + ClusterServicePlanExternalName: testClusterServicePlanName, }, ExternalID: testExternalID, }, @@ -405,7 +399,7 @@ func TestBasicFlowsAsync(t *testing.T) { binding := &v1beta1.ServiceBinding{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testBindingName}, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: corev1.LocalObjectReference{ + ServiceInstanceRef: v1beta1.LocalObjectReference{ Name: testInstanceName, }, }, @@ -451,7 +445,7 @@ func TestBasicFlowsAsync(t *testing.T) { // End provision test // Delete the broker - err = client.ClusterServiceBrokers().Delete(testBrokerName, &metav1.DeleteOptions{}) + err = client.ClusterServiceBrokers().Delete(testClusterServiceBrokerName, &metav1.DeleteOptions{}) if nil != err { t.Fatalf("broker should be deleted (%s)", err) } @@ -461,7 +455,7 @@ func TestBasicFlowsAsync(t *testing.T) { t.Fatalf("error waiting for ClusterServiceClass to not exist: %v", err) } - err = util.WaitForBrokerToNotExist(client, testBrokerName) + err = util.WaitForBrokerToNotExist(client, testClusterServiceBrokerName) if err != nil { t.Fatalf("error waiting for Broker to not exist: %v", err) } @@ -494,7 +488,7 @@ func TestProvisionFailure(t *testing.T) { client := catalogClient.ServicecatalogV1beta1() broker := &v1beta1.ClusterServiceBroker{ - ObjectMeta: metav1.ObjectMeta{Name: testBrokerName}, + ObjectMeta: metav1.ObjectMeta{Name: testClusterServiceBrokerName}, Spec: v1beta1.ClusterServiceBrokerSpec{ URL: testBrokerURL, }, @@ -506,7 +500,7 @@ func TestProvisionFailure(t *testing.T) { } err = util.WaitForBrokerCondition(client, - testBrokerName, + testClusterServiceBrokerName, v1beta1.ServiceBrokerCondition{ Type: v1beta1.ServiceBrokerConditionReady, Status: v1beta1.ConditionTrue, @@ -515,7 +509,7 @@ func TestProvisionFailure(t *testing.T) { t.Fatalf("error waiting for broker to become ready: %v", err) } - err = util.WaitForClusterServiceClassToExist(client, testClusterServiceClassID) + err = util.WaitForClusterServiceClassToExist(client, testClusterServiceClassGUID) if nil != err { t.Fatalf("error waiting from ClusterServiceClass to exist: %v", err) } @@ -529,8 +523,8 @@ func TestProvisionFailure(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testInstanceName}, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServiceClassName: testClusterServiceClassName, - ExternalClusterServicePlanName: testPlanName, + ClusterServiceClassExternalName: testClusterServiceClassName, + ClusterServicePlanExternalName: testClusterServicePlanName, }, ExternalID: testExternalID, }, @@ -573,7 +567,7 @@ func TestProvisionFailure(t *testing.T) { // End provision test // Delete the broker - err = client.ClusterServiceBrokers().Delete(testBrokerName, &metav1.DeleteOptions{}) + err = client.ClusterServiceBrokers().Delete(testClusterServiceBrokerName, &metav1.DeleteOptions{}) if nil != err { t.Fatalf("broker should be deleted (%s)", err) } @@ -583,7 +577,7 @@ func TestProvisionFailure(t *testing.T) { t.Fatalf("error waiting for ClusterServiceClass to not exist: %v", err) } - err = util.WaitForBrokerToNotExist(client, testBrokerName) + err = util.WaitForBrokerToNotExist(client, testClusterServiceBrokerName) if err != nil { t.Fatalf("error waiting for Broker to not exist: %v", err) } @@ -621,7 +615,7 @@ func TestBindingFailure(t *testing.T) { client := fakeCatalogClient.ServicecatalogV1beta1() broker := &v1beta1.ClusterServiceBroker{ - ObjectMeta: metav1.ObjectMeta{Name: testBrokerName}, + ObjectMeta: metav1.ObjectMeta{Name: testClusterServiceBrokerName}, Spec: v1beta1.ClusterServiceBrokerSpec{ URL: testBrokerURL, }, @@ -633,7 +627,7 @@ func TestBindingFailure(t *testing.T) { } err = util.WaitForBrokerCondition(client, - testBrokerName, + testClusterServiceBrokerName, v1beta1.ServiceBrokerCondition{ Type: v1beta1.ServiceBrokerConditionReady, Status: v1beta1.ConditionTrue, @@ -642,7 +636,7 @@ func TestBindingFailure(t *testing.T) { t.Fatalf("error waiting for broker to become ready: %v", err) } - err = util.WaitForClusterServiceClassToExist(client, testClusterServiceClassID) + err = util.WaitForClusterServiceClassToExist(client, testClusterServiceClassGUID) if nil != err { t.Fatalf("error waiting from ClusterServiceClass to exist: %v", err) } @@ -656,8 +650,8 @@ func TestBindingFailure(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testInstanceName}, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServiceClassName: testClusterServiceClassName, - ExternalClusterServicePlanName: testPlanName, + ClusterServiceClassExternalName: testClusterServiceClassName, + ClusterServicePlanExternalName: testClusterServicePlanName, }, ExternalID: testExternalID, }, @@ -692,7 +686,7 @@ func TestBindingFailure(t *testing.T) { binding := &v1beta1.ServiceBinding{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testBindingName}, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: corev1.LocalObjectReference{ + ServiceInstanceRef: v1beta1.LocalObjectReference{ Name: testInstanceName, }, }, @@ -738,7 +732,7 @@ func TestBindingFailure(t *testing.T) { // End provision test // Delete the broker - err = client.ClusterServiceBrokers().Delete(testBrokerName, &metav1.DeleteOptions{}) + err = client.ClusterServiceBrokers().Delete(testClusterServiceBrokerName, &metav1.DeleteOptions{}) if nil != err { t.Fatalf("broker should be deleted (%s)", err) } @@ -748,7 +742,7 @@ func TestBindingFailure(t *testing.T) { t.Fatalf("error waiting for ClusterServiceClass to not exist: %v", err) } - err = util.WaitForBrokerToNotExist(client, testBrokerName) + err = util.WaitForBrokerToNotExist(client, testClusterServiceBrokerName) if err != nil { t.Fatalf("error waiting for Broker to not exist: %v", err) } @@ -772,7 +766,7 @@ func TestBasicFlowsWithOriginatingIdentity(t *testing.T) { Bindable: true, Plans: []osb.Plan{ { - Name: testPlanName, + Name: testClusterServicePlanName, Free: truePtr(), ID: "34567", Description: "a test plan", @@ -813,7 +807,7 @@ func TestBasicFlowsWithOriginatingIdentity(t *testing.T) { client := catalogClient.ServicecatalogV1beta1() broker := &v1beta1.ClusterServiceBroker{ - ObjectMeta: metav1.ObjectMeta{Name: testBrokerName}, + ObjectMeta: metav1.ObjectMeta{Name: testClusterServiceBrokerName}, Spec: v1beta1.ClusterServiceBrokerSpec{ URL: testBrokerURL, }, @@ -825,7 +819,7 @@ func TestBasicFlowsWithOriginatingIdentity(t *testing.T) { } err = util.WaitForBrokerCondition(client, - testBrokerName, + testClusterServiceBrokerName, v1beta1.ServiceBrokerCondition{ Type: v1beta1.ServiceBrokerConditionReady, Status: v1beta1.ConditionTrue, @@ -834,7 +828,7 @@ func TestBasicFlowsWithOriginatingIdentity(t *testing.T) { t.Fatalf("error waiting for broker to become ready: %v", err) } - err = util.WaitForClusterServiceClassToExist(client, testClusterServiceClassID) + err = util.WaitForClusterServiceClassToExist(client, testClusterServiceClassGUID) if nil != err { t.Fatalf("error waiting from ClusterServiceClass to exist: %v", err) } @@ -855,8 +849,8 @@ func TestBasicFlowsWithOriginatingIdentity(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testInstanceName}, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServiceClassName: testClusterServiceClassName, - ExternalClusterServicePlanName: testPlanName, + ClusterServiceClassExternalName: testClusterServiceClassName, + ClusterServicePlanExternalName: testClusterServicePlanName, }, ExternalID: testExternalID, }, @@ -927,7 +921,7 @@ func TestBasicFlowsWithOriginatingIdentity(t *testing.T) { binding := &v1beta1.ServiceBinding{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testBindingName}, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: corev1.LocalObjectReference{ + ServiceInstanceRef: v1beta1.LocalObjectReference{ Name: testInstanceName, }, }, @@ -1019,7 +1013,7 @@ func TestServiceInstanceOrphanMitigation(t *testing.T) { }, ProvisionReaction: &fakeosb.ProvisionReaction{ Error: osb.HTTPStatusCodeError{ - StatusCode: http.StatusInternalServerError, + StatusCode: http.StatusInternalServerError, }, }, DeprovisionReaction: &fakeosb.DeprovisionReaction{ @@ -1032,7 +1026,7 @@ func TestServiceInstanceOrphanMitigation(t *testing.T) { client := catalogClient.ServicecatalogV1beta1() broker := &v1beta1.ClusterServiceBroker{ - ObjectMeta: metav1.ObjectMeta{Name: testBrokerName}, + ObjectMeta: metav1.ObjectMeta{Name: testClusterServiceBrokerName}, Spec: v1beta1.ClusterServiceBrokerSpec{ URL: testBrokerURL, }, @@ -1044,7 +1038,7 @@ func TestServiceInstanceOrphanMitigation(t *testing.T) { } err = util.WaitForBrokerCondition(client, - testBrokerName, + testClusterServiceBrokerName, v1beta1.ServiceBrokerCondition{ Type: v1beta1.ServiceBrokerConditionReady, Status: v1beta1.ConditionTrue, @@ -1053,7 +1047,7 @@ func TestServiceInstanceOrphanMitigation(t *testing.T) { t.Fatalf("error waiting for broker to become ready: %v", err) } - err = util.WaitForClusterServiceClassToExist(client, testClusterServiceClassID) + err = util.WaitForClusterServiceClassToExist(client, testClusterServiceClassGUID) if nil != err { t.Fatalf("error waiting from ClusterServiceClass to exist: %v", err) } @@ -1067,14 +1061,13 @@ func TestServiceInstanceOrphanMitigation(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testInstanceName}, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServiceClassName: testClusterServiceClassName, - ExternalClusterServicePlanName: testPlanName, + ClusterServiceClassExternalName: testClusterServiceClassName, + ClusterServicePlanExternalName: testClusterServicePlanName, }, ExternalID: testExternalID, }, } - if _, err := client.ServiceInstances(testNamespace).Create(instance); err != nil { t.Fatalf("error creating Instance: %v", err) } @@ -1117,7 +1110,7 @@ func TestServiceInstanceOrphanMitigation(t *testing.T) { // End orphan mitigation test // Delete the broker - err = client.ClusterServiceBrokers().Delete(testBrokerName, &metav1.DeleteOptions{}) + err = client.ClusterServiceBrokers().Delete(testClusterServiceBrokerName, &metav1.DeleteOptions{}) if nil != err { t.Fatalf("broker should be deleted (%s)", err) } @@ -1127,7 +1120,7 @@ func TestServiceInstanceOrphanMitigation(t *testing.T) { t.Fatalf("error waiting for ClusterServiceClass to not exist: %v", err) } - err = util.WaitForBrokerToNotExist(client, testBrokerName) + err = util.WaitForBrokerToNotExist(client, testClusterServiceBrokerName) if err != nil { t.Fatalf("error waiting for Broker to not exist: %v", err) } @@ -1143,7 +1136,7 @@ func TestServiceBindingOrphanMitigation(t *testing.T) { }, BindReaction: &fakeosb.BindReaction{ Error: osb.HTTPStatusCodeError{ - StatusCode: http.StatusInternalServerError, + StatusCode: http.StatusInternalServerError, }, }, UnbindReaction: &fakeosb.UnbindReaction{}, @@ -1160,7 +1153,7 @@ func TestServiceBindingOrphanMitigation(t *testing.T) { client := fakeCatalogClient.ServicecatalogV1beta1() broker := &v1beta1.ClusterServiceBroker{ - ObjectMeta: metav1.ObjectMeta{Name: testBrokerName}, + ObjectMeta: metav1.ObjectMeta{Name: testClusterServiceBrokerName}, Spec: v1beta1.ClusterServiceBrokerSpec{ URL: testBrokerURL, }, @@ -1172,7 +1165,7 @@ func TestServiceBindingOrphanMitigation(t *testing.T) { } err = util.WaitForBrokerCondition(client, - testBrokerName, + testClusterServiceBrokerName, v1beta1.ServiceBrokerCondition{ Type: v1beta1.ServiceBrokerConditionReady, Status: v1beta1.ConditionTrue, @@ -1181,7 +1174,7 @@ func TestServiceBindingOrphanMitigation(t *testing.T) { t.Fatalf("error waiting for broker to become ready: %v", err) } - err = util.WaitForClusterServiceClassToExist(client, testClusterServiceClassID) + err = util.WaitForClusterServiceClassToExist(client, testClusterServiceClassGUID) if nil != err { t.Fatalf("error waiting from ClusterServiceClass to exist: %v", err) } @@ -1195,8 +1188,8 @@ func TestServiceBindingOrphanMitigation(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testInstanceName}, Spec: v1beta1.ServiceInstanceSpec{ PlanReference: v1beta1.PlanReference{ - ExternalClusterServiceClassName: testClusterServiceClassName, - ExternalClusterServicePlanName: testPlanName, + ClusterServiceClassExternalName: testClusterServiceClassName, + ClusterServicePlanExternalName: testClusterServicePlanName, }, ExternalID: testExternalID, }, @@ -1230,7 +1223,7 @@ func TestServiceBindingOrphanMitigation(t *testing.T) { binding := &v1beta1.ServiceBinding{ ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: testBindingName}, Spec: v1beta1.ServiceBindingSpec{ - ServiceInstanceRef: corev1.LocalObjectReference{ + ServiceInstanceRef: v1beta1.LocalObjectReference{ Name: testInstanceName, }, }, @@ -1285,7 +1278,7 @@ func TestServiceBindingOrphanMitigation(t *testing.T) { // End provision test // Delete the broker - err = client.ClusterServiceBrokers().Delete(testBrokerName, &metav1.DeleteOptions{}) + err = client.ClusterServiceBrokers().Delete(testClusterServiceBrokerName, &metav1.DeleteOptions{}) if nil != err { t.Fatalf("broker should be deleted (%s)", err) } @@ -1295,7 +1288,7 @@ func TestServiceBindingOrphanMitigation(t *testing.T) { t.Fatalf("error waiting for ClusterServiceClass to not exist: %v", err) } - err = util.WaitForBrokerToNotExist(client, testBrokerName) + err = util.WaitForBrokerToNotExist(client, testClusterServiceBrokerName) if err != nil { t.Fatalf("error waiting for Broker to not exist: %v", err) } @@ -1403,7 +1396,7 @@ func getTestCatalogResponse() *osb.CatalogResponse { Bindable: true, Plans: []osb.Plan{ { - Name: testPlanName, + Name: testClusterServicePlanName, Free: truePtr(), ID: testPlanExternalID, Description: "a test plan", diff --git a/test/integration/deleted_services_and_plans_test.go b/test/integration/deleted_services_and_plans_test.go new file mode 100644 index 000000000000..fdd7c3d6fe7b --- /dev/null +++ b/test/integration/deleted_services_and_plans_test.go @@ -0,0 +1,193 @@ +/* +Copyright 2017 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 integration + +import ( + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + // avoid error `servicecatalog/v1beta1 is not enabled` + _ "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/install" + + fakeosb "github.com/pmorie/go-open-service-broker-client/v2/fake" + + "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1" + "github.com/kubernetes-incubator/service-catalog/test/util" +) + +func TestClusterServicePlanRemovedFromCatalogWithoutInstances(t *testing.T) { + _, catalogClient, _, _, _, _, shutdownServer, shutdownController := newTestController(t, fakeosb.FakeClientConfiguration{ + CatalogReaction: &fakeosb.CatalogReaction{ + Response: getTestCatalogResponse(), + }, + }) + defer shutdownController() + defer shutdownServer() + + client := catalogClient.ServicecatalogV1beta1() + + broker := &v1beta1.ClusterServiceBroker{ + ObjectMeta: metav1.ObjectMeta{Name: testClusterServiceBrokerName}, + Spec: v1beta1.ClusterServiceBrokerSpec{ + URL: testBrokerURL, + }, + } + + _, err := client.ClusterServiceBrokers().Create(broker) + if nil != err { + t.Fatalf("error creating the broker %q (%q)", broker.Name, err) + } + + err = util.WaitForBrokerCondition(client, + testClusterServiceBrokerName, + v1beta1.ServiceBrokerCondition{ + Type: v1beta1.ServiceBrokerConditionReady, + Status: v1beta1.ConditionTrue, + }) + if err != nil { + t.Fatalf("error waiting for broker to become ready: %v", err) + } + + err = util.WaitForClusterServiceClassToExist(client, testClusterServiceClassGUID) + if nil != err { + t.Fatalf("error waiting from ClusterServiceClass to exist: %v", err) + } + + removedPlan := getTestClusterServicePlanRemoved() + removedPlan, err = client.ClusterServicePlans().Create(removedPlan) + if err != nil { + t.Fatalf("error creating ClusterServicePlan: %v", err) + } + + err = util.WaitForClusterServicePlanToExist(client, testRemovedClusterServicePlanGUID) + if err != nil { + t.Fatalf("error waiting for ClusterServicePlan to exist: %v", err) + } + + t.Log("updating ClusterServiceClass status") + removedPlan.Status.RemovedFromBrokerCatalog = true + _, err = client.ClusterServicePlans().UpdateStatus(removedPlan) + if err != nil { + t.Fatalf("error marking ClusterServicePlan as removed from catalog: %v", err) + } + + err = util.WaitForClusterServicePlanToNotExist(client, testRemovedClusterServicePlanGUID) + if err != nil { + t.Fatalf("error waiting for remove ClusterServicePlan to not exist: %v", err) + } +} + +const ( + testRemovedClusterServicePlanGUID = "removed-plan" + testRemovedClusterServicePlanExternalName = "removed-plan-name" + testRemovedClusterServiceClassGUID = "removed-class" + testRemovedClusterServiceClassExternalName = "removed-class-name" +) + +func getTestClusterServicePlanRemoved() *v1beta1.ClusterServicePlan { + return &v1beta1.ClusterServicePlan{ + ObjectMeta: metav1.ObjectMeta{Name: testRemovedClusterServicePlanGUID}, + Spec: v1beta1.ClusterServicePlanSpec{ + ClusterServiceBrokerName: testClusterServiceBrokerName, + ExternalID: testRemovedClusterServicePlanGUID, + ExternalName: testRemovedClusterServicePlanExternalName, + Description: "a plan that will be removed", + Bindable: truePtr(), + ClusterServiceClassRef: v1beta1.ClusterObjectReference{ + Name: testClusterServiceClassGUID, + }, + }, + Status: v1beta1.ClusterServicePlanStatus{}, + } +} + +func TestClusterServiceClassRemovedFromCatalogWithoutInstances(t *testing.T) { + _, catalogClient, _, _, _, _, shutdownServer, shutdownController := newTestController(t, fakeosb.FakeClientConfiguration{ + CatalogReaction: &fakeosb.CatalogReaction{ + Response: getTestCatalogResponse(), + }, + }) + defer shutdownController() + defer shutdownServer() + + client := catalogClient.ServicecatalogV1beta1() + + broker := &v1beta1.ClusterServiceBroker{ + ObjectMeta: metav1.ObjectMeta{Name: testClusterServiceBrokerName}, + Spec: v1beta1.ClusterServiceBrokerSpec{ + URL: testBrokerURL, + }, + } + + _, err := client.ClusterServiceBrokers().Create(broker) + if nil != err { + t.Fatalf("error creating the broker %q (%q)", broker.Name, err) + } + + err = util.WaitForBrokerCondition(client, + testClusterServiceBrokerName, + v1beta1.ServiceBrokerCondition{ + Type: v1beta1.ServiceBrokerConditionReady, + Status: v1beta1.ConditionTrue, + }) + if err != nil { + t.Fatalf("error waiting for broker to become ready: %v", err) + } + + err = util.WaitForClusterServiceClassToExist(client, testClusterServiceClassGUID) + if nil != err { + t.Fatalf("error waiting from ClusterServiceClass to exist: %v", err) + } + + removedClass := getTestClusterServiceClassRemoved() + removedClass, err = client.ClusterServiceClasses().Create(removedClass) + if err != nil { + t.Fatalf("error creating ClusterServiceClass: %v", err) + } + + err = util.WaitForClusterServiceClassToExist(client, testRemovedClusterServiceClassGUID) + if err != nil { + t.Fatalf("error waiting for ClusterServiceClass to exist: %v", err) + } + + t.Log("updating ClusterServiceClass status") + removedClass.Status.RemovedFromBrokerCatalog = true + _, err = client.ClusterServiceClasses().UpdateStatus(removedClass) + if err != nil { + t.Fatalf("error marking ClusterServiceClass as removed from catalog: %v", err) + } + + err = util.WaitForClusterServiceClassToNotExist(client, testRemovedClusterServiceClassGUID) + if err != nil { + t.Fatalf("error waiting for remove ClusterServiceClass to not exist: %v", err) + } +} + +func getTestClusterServiceClassRemoved() *v1beta1.ClusterServiceClass { + return &v1beta1.ClusterServiceClass{ + ObjectMeta: metav1.ObjectMeta{Name: testRemovedClusterServiceClassGUID}, + Spec: v1beta1.ClusterServiceClassSpec{ + ClusterServiceBrokerName: testClusterServiceBrokerName, + ExternalID: testRemovedClusterServiceClassGUID, + ExternalName: testRemovedClusterServiceClassExternalName, + Description: "a serviceclass that will be removed", + Bindable: true, + }, + Status: v1beta1.ClusterServiceClassStatus{}, + } +} diff --git a/test/integration/framework.go b/test/integration/framework.go index 7047e92da9e9..e6d3535daab6 100644 --- a/test/integration/framework.go +++ b/test/integration/framework.go @@ -82,6 +82,7 @@ func withConfigGetFreshApiserverAndClient( if serverstorage.StorageTypeEtcd == serverConfig.storageType { etcdOptions = server.NewEtcdOptions() etcdOptions.StorageConfig.ServerList = serverConfig.etcdServerList + etcdOptions.EtcdOptions.StorageConfig.Prefix = fmt.Sprintf("%s-%08X", server.DefaultEtcdPathPrefix, rand.Int31()) } else { t.Fatal("no storage type specified") } @@ -96,13 +97,12 @@ func withConfigGetFreshApiserverAndClient( AuthorizationOptions: genericserveroptions.NewDelegatingAuthorizationOptions(), AuditOptions: genericserveroptions.NewAuditOptions(), DisableAuth: true, - StopCh: stopCh, StandaloneMode: true, // this must be true because we have no kube server for integration. } options.SecureServingOptions.BindPort = securePort options.SecureServingOptions.ServerCert.CertDirectory = certDir - if err := server.RunServer(options); err != nil { + if err := server.RunServer(options, stopCh); err != nil { close(serverFailed) t.Fatalf("Error in bringing up the server: %v", err) } diff --git a/test/integration/util.go b/test/integration/util.go index e21ebfb70f88..537a6eefc14d 100644 --- a/test/integration/util.go +++ b/test/integration/util.go @@ -21,3 +21,8 @@ package integration func strPtr(s string) *string { return &s } + +func truePtr() *bool { + b := true + return &b +} diff --git a/test/repo-sync.sh b/test/repo-sync.sh new file mode 100644 index 000000000000..574e069c518c --- /dev/null +++ b/test/repo-sync.sh @@ -0,0 +1,45 @@ +#!/bin/bash -xe +# Copyright 2017 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. + +HELM_URL=https://storage.googleapis.com/kubernetes-helm +HELM_TARBALL=helm-v2.6.2-linux-amd64.tar.gz +SVC_CATALOG_BUCKET=svc-catalog-charts +SVC_CATALOG_REPO_URL=https://$SVC_CATALOG_BUCKET.storage.googleapis.com/ + +# Setup Helm +wget -q ${HELM_URL}/${HELM_TARBALL} +tar xzfv ${HELM_TARBALL} +PATH=`pwd`/linux-amd64/:$PATH +helm init --client-only + +# Authenticate before uploading to Google Cloud Storage +cat > sa.json <= reflect.Chan && kind <= reflect.Slice && value.IsNil() { + return true + } + + return false +} + +// includeElement try loop over the list check if the list includes the element. +// return (false, false) if impossible. +// return (true, false) if element was not found. +// return (true, true) if element was found. +func includeElement(list interface{}, element interface{}) (ok, found bool) { + + listValue := reflect.ValueOf(list) + elementValue := reflect.ValueOf(element) + defer func() { + if e := recover(); e != nil { + ok = false + found = false + } + }() + + if reflect.TypeOf(list).Kind() == reflect.String { + return true, strings.Contains(listValue.String(), elementValue.String()) + } + + if reflect.TypeOf(list).Kind() == reflect.Map { + mapKeys := listValue.MapKeys() + for i := 0; i < len(mapKeys); i++ { + if ObjectsAreEqual(mapKeys[i].Interface(), element) { + return true, true + } + } + return true, false + } + + for i := 0; i < listValue.Len(); i++ { + if ObjectsAreEqual(listValue.Index(i).Interface(), element) { + return true, true + } + } + return true, false + +} + +// ObjectsAreEqual determines if two objects are considered equal. +// +// This function does no assertion of any kind. +func ObjectsAreEqual(expected, actual interface{}) bool { + + if expected == nil || actual == nil { + return expected == actual + } + if exp, ok := expected.([]byte); ok { + act, ok := actual.([]byte) + if !ok { + return false + } else if exp == nil || act == nil { + return exp == nil && act == nil + } + return bytes.Equal(exp, act) + } + return reflect.DeepEqual(expected, actual) + +} diff --git a/test/util/util.go b/test/util/util.go index 7a115e523d62..a56525b3abdc 100644 --- a/test/util/util.go +++ b/test/util/util.go @@ -93,6 +93,42 @@ func WaitForClusterServiceClassToExist(client v1beta1servicecatalog.Servicecatal ) } +// WaitForClusterServiceClassToExist waits for the ClusterServiceClass with the given name +// to exist. +func WaitForClusterServicePlanToExist(client v1beta1servicecatalog.ServicecatalogV1beta1Interface, name string) error { + return wait.PollImmediate(500*time.Millisecond, wait.ForeverTestTimeout, + func() (bool, error) { + glog.V(5).Infof("Waiting for ClusterServicePlan %v to exist", name) + _, err := client.ClusterServicePlans().Get(name, metav1.GetOptions{}) + if nil == err { + return true, nil + } + + return false, nil + }, + ) +} + +// WaitForClusterServicePlanToNotExist waits for the ClusterServicePlan with the given name +// to not exist. +func WaitForClusterServicePlanToNotExist(client v1beta1servicecatalog.ServicecatalogV1beta1Interface, name string) error { + return wait.PollImmediate(500*time.Millisecond, wait.ForeverTestTimeout, + func() (bool, error) { + glog.V(5).Infof("Waiting for ClusterServicePlan %q to not exist", name) + _, err := client.ClusterServicePlans().Get(name, metav1.GetOptions{}) + if nil == err { + return false, nil + } + + if errors.IsNotFound(err) { + return true, nil + } + + return false, nil + }, + ) +} + // WaitForClusterServiceClassToNotExist waits for the ClusterServiceClass with the given // name to no longer exist. func WaitForClusterServiceClassToNotExist(client v1beta1servicecatalog.ServicecatalogV1beta1Interface, name string) error { diff --git a/vendor/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go b/vendor/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go index 20e7bd88d179..f220237369bf 100644 --- a/vendor/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go +++ b/vendor/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go @@ -165,7 +165,7 @@ func (l *lifecycle) Admit(a admission.Attributes) error { // refuse to operate on non-existent namespaces if !exists || forceLiveLookup { // as a last resort, make a call directly to storage - namespace, err = l.client.Core().Namespaces().Get(a.GetNamespace(), metav1.GetOptions{}) + namespace, err = l.client.CoreV1().Namespaces().Get(a.GetNamespace(), metav1.GetOptions{}) switch { case errors.IsNotFound(err): return err