From 6fb9fe88a6631425504eadc56c9e9f5a3d472e3e Mon Sep 17 00:00:00 2001 From: Nail Islamov Date: Wed, 4 Oct 2017 11:46:01 +1100 Subject: [PATCH] Drop TPR storage support --- charts/catalog/README.md | 3 +- .../templates/apiserver-deployment.yaml | 3 - charts/catalog/values.yaml | 8 +- cmd/apiserver/app/server/options.go | 6 +- cmd/apiserver/app/server/run_server.go | 54 +- cmd/apiserver/app/server/run_server_test.go | 81 -- cmd/apiserver/app/server/server.go | 26 +- cmd/apiserver/app/server/tpr_options.go | 57 - contrib/jenkins/install_catalog.sh | 6 - docs/design.md | 11 +- docs/install-1.6.md | 14 +- pkg/apiserver/tpr_config.go | 106 -- pkg/apiserver/tpr_rest_options_factory.go | 43 - .../servicecatalog/binding/storage.go | 5 +- pkg/registry/servicecatalog/broker/storage.go | 5 +- .../servicecatalog/instance/storage.go | 5 +- .../rest/storage_servicecatalog.go | 88 -- .../rest/storage_servicecatalog_test.go | 2 +- pkg/registry/servicecatalog/server/options.go | 18 +- .../servicecatalog/server/options_test.go | 3 +- .../servicecatalog/serviceclass/storage.go | 5 +- .../servicecatalog/serviceplan/storage.go | 5 +- pkg/storage/tpr/client.go | 49 - pkg/storage/tpr/delete.go | 56 - pkg/storage/tpr/get.go | 88 -- pkg/storage/tpr/init_test.go | 50 - pkg/storage/tpr/install_types.go | 106 -- pkg/storage/tpr/install_types_test.go | 185 --- pkg/storage/tpr/keyer.go | 91 -- pkg/storage/tpr/keyer_test.go | 100 -- pkg/storage/tpr/kinds.go | 110 -- pkg/storage/tpr/kinds_test.go | 65 - pkg/storage/tpr/list_resources.go | 76 - pkg/storage/tpr/list_resources_test.go | 124 -- pkg/storage/tpr/obj_state.go | 66 - pkg/storage/tpr/options.go | 43 - pkg/storage/tpr/put.go | 71 - pkg/storage/tpr/resources.go | 137 -- pkg/storage/tpr/resources_test.go | 96 -- pkg/storage/tpr/storage_interface.go | 674 --------- pkg/storage/tpr/storage_interface_test.go | 1224 ----------------- .../tpr/storage_interface_watch_utils_test.go | 136 -- pkg/storage/tpr/tpr_converter.go | 50 - test/integration/clientset_test.go | 5 +- test/integration/framework.go | 14 - 45 files changed, 35 insertions(+), 4135 deletions(-) delete mode 100644 cmd/apiserver/app/server/run_server_test.go delete mode 100644 cmd/apiserver/app/server/tpr_options.go delete mode 100644 pkg/apiserver/tpr_config.go delete mode 100644 pkg/apiserver/tpr_rest_options_factory.go delete mode 100644 pkg/storage/tpr/client.go delete mode 100644 pkg/storage/tpr/delete.go delete mode 100644 pkg/storage/tpr/get.go delete mode 100644 pkg/storage/tpr/init_test.go delete mode 100644 pkg/storage/tpr/install_types.go delete mode 100644 pkg/storage/tpr/install_types_test.go delete mode 100644 pkg/storage/tpr/keyer.go delete mode 100644 pkg/storage/tpr/keyer_test.go delete mode 100644 pkg/storage/tpr/kinds.go delete mode 100644 pkg/storage/tpr/kinds_test.go delete mode 100644 pkg/storage/tpr/list_resources.go delete mode 100644 pkg/storage/tpr/list_resources_test.go delete mode 100644 pkg/storage/tpr/obj_state.go delete mode 100644 pkg/storage/tpr/options.go delete mode 100644 pkg/storage/tpr/put.go delete mode 100644 pkg/storage/tpr/resources.go delete mode 100644 pkg/storage/tpr/resources_test.go delete mode 100644 pkg/storage/tpr/storage_interface.go delete mode 100644 pkg/storage/tpr/storage_interface_test.go delete mode 100644 pkg/storage/tpr/storage_interface_watch_utils_test.go delete mode 100644 pkg/storage/tpr/tpr_converter.go diff --git a/charts/catalog/README.md b/charts/catalog/README.md index f46afc0f437..2201a6eee1d 100644 --- a/charts/catalog/README.md +++ b/charts/catalog/README.md @@ -48,10 +48,9 @@ chart and their default values. | `apiserver.tls.requestHeaderCA` | Base64-encoded CA used to validate request-header authentication, when receiving delegated authentication from an aggregator | *none (will disable requestheader authentication)* | | `apiserver.service.type` | Type of service; valid values are `LoadBalancer` and `NodePort` | `NodePort` | | `apiserver.service.nodePort.securePort` | If service type is `NodePort`, specifies a port in allowable range (e.g. 30000 - 32767 on minikube); The TLS-enabled endpoint will be exposed here | `30443` | -| `apiserver.storage.type` | The storage backend to use; valid values are `etcd` and `tpr` | `etcd` | +| `apiserver.storage.type` | The storage backend to use; the only valid value is `etcd`, left for other storages support in future, e.g. `crd` | `etcd` | | `apiserver.storage.etcd.useEmbedded` | If storage type is `etcd`: Whether to embed an etcd container in the apiserver pod; THIS IS INADEQUATE FOR PRODUCTION USE! | `true` | | `apiserver.storage.etcd.servers` | If storage type is `etcd`: etcd URL(s); override this if NOT using embedded etcd | `http://localhost:2379` | -| `apiserver.storage.tpr.globalNamespace` | If storage type is `tpr`: Some service catalog resources are not namespaced, but third party resources must be; setting this designates a namespace that will be treated as a container for such resources | `servicecatalog` | | `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.0.22` | diff --git a/charts/catalog/templates/apiserver-deployment.yaml b/charts/catalog/templates/apiserver-deployment.yaml index c837c7b2965..0ac4b7217df 100644 --- a/charts/catalog/templates/apiserver-deployment.yaml +++ b/charts/catalog/templates/apiserver-deployment.yaml @@ -46,9 +46,6 @@ spec: {{- if eq .Values.apiserver.storage.type "etcd" }} - --etcd-servers - {{ .Values.apiserver.storage.etcd.servers }} - {{- else if eq .Values.apiserver.storage.type "tpr" }} - - --global-namespace - - {{ .Values.apiserver.storage.tpr.globalNamespace }} {{- end }} - -v - "{{ .Values.apiserver.verbosity }}" diff --git a/charts/catalog/values.yaml b/charts/catalog/values.yaml index 8b639092fa3..cf7b9a75fd1 100644 --- a/charts/catalog/values.yaml +++ b/charts/catalog/values.yaml @@ -43,7 +43,8 @@ apiserver: # The TLS-enabled endpoint will be exposed here securePort: 30443 storage: - # The storage backend to use; valid values are "etcd" and "tpr" + # The storage backend to use; the only valid value is "etcd" + # (left for "crd" support in future) type: etcd # Further configuration for the etcd-based backend etcd: @@ -52,11 +53,6 @@ apiserver: useEmbedded: true # etcd URL(s); override this if NOT using embedded etcd servers: http://localhost:2379 - tpr: - # Some service catalog resources are not namespaced, but third party - # resources must be; setting this designates a namespace that will be - # treated as a container for such resources - globalNamespace: servicecatalog # Log level; valid values are in the range 0 - 10 verbosity: 10 auth: diff --git a/cmd/apiserver/app/server/options.go b/cmd/apiserver/app/server/options.go index 0e259c9c814..322812be25e 100644 --- a/cmd/apiserver/app/server/options.go +++ b/cmd/apiserver/app/server/options.go @@ -45,8 +45,6 @@ type ServiceCatalogServerOptions struct { AuditOptions *genericserveroptions.AuditOptions // EtcdOptions are options for serving with etcd as the backing store EtcdOptions *EtcdOptions - // TPROptions are options for serving with TPR as the backing store - TPROptions *TPROptions // DisableAuth disables delegating authentication and authorization for testing scenarios DisableAuth bool StopCh <-chan struct{} @@ -65,7 +63,6 @@ func NewServiceCatalogServerOptions() *ServiceCatalogServerOptions { AuthorizationOptions: genericserveroptions.NewDelegatingAuthorizationOptions(), AuditOptions: genericserveroptions.NewAuditOptions(), EtcdOptions: NewEtcdOptions(), - TPROptions: NewTPROptions(), } } @@ -90,7 +87,6 @@ func (s *ServiceCatalogServerOptions) addFlags(flags *pflag.FlagSet) { s.AuthenticationOptions.AddFlags(flags) s.AuthorizationOptions.AddFlags(flags) s.EtcdOptions.addFlags(flags) - s.TPROptions.addFlags(flags) s.AuditOptions.AddFlags(flags) } @@ -119,7 +115,7 @@ func (s *ServiceCatalogServerOptions) Validate() error { errors = append(errors, etcdErrs...) } // TODO add alternative storage validation - // errors = append(errors, s.TPROptions.Validate()...) + // errors = append(errors, s.CRDOptions.Validate()...) // TODO uncomment after 1.8 rebase expecting // https://github.com/kubernetes/kubernetes/pull/47043 // errors = append(errors, s.AuditOptions.Validate()...) diff --git a/cmd/apiserver/app/server/run_server.go b/cmd/apiserver/app/server/run_server.go index c253c439709..f42f471da4f 100644 --- a/cmd/apiserver/app/server/run_server.go +++ b/cmd/apiserver/app/server/run_server.go @@ -22,14 +22,12 @@ import ( "github.com/kubernetes-incubator/service-catalog/pkg/api" genericapiserverstorage "k8s.io/apiserver/pkg/server/storage" - clientset "k8s.io/client-go/kubernetes" "k8s.io/kubernetes/cmd/kube-apiserver/app/preflight" "github.com/golang/glog" "github.com/kubernetes-incubator/service-catalog/pkg/apiserver" "github.com/kubernetes-incubator/service-catalog/pkg/apiserver/options" "github.com/kubernetes-incubator/service-catalog/pkg/registry/servicecatalog/server" - "github.com/kubernetes-incubator/service-catalog/pkg/storage/tpr" ) // RunServer runs an API server with configuration according to opts @@ -49,55 +47,11 @@ func RunServer(opts *ServiceCatalogServerOptions) error { return err } - if storageType == server.StorageTypeTPR { - return runTPRServer(opts) + if storageType == server.StorageTypeEtcd { + return runEtcdServer(opts) } - return runEtcdServer(opts) -} - -func installTPRsToCore(cl clientset.Interface) func() error { - return func() error { - if err := tpr.InstallTypes(cl.Extensions().ThirdPartyResources()); err != nil { - glog.Errorf("Failed to install TPR types (%s)", err) - return err - } - return nil - } -} - -func runTPRServer(opts *ServiceCatalogServerOptions) error { - tprOpts := opts.TPROptions - glog.Infoln("Installing TPR types to the cluster") - if err := tprOpts.InstallTPRsFunc(); err != nil { - glog.V(4).Infof("Installing TPR types failed, continuing anyway (%s)", err) - return err - } - - glog.V(4).Infoln("Preparing to run API server") - genericConfig, scConfig, err := buildGenericConfig(opts) - if err != nil { - return err - } - - config := apiserver.NewTPRConfig( - tprOpts.RESTClient, - genericConfig, - tprOpts.GlobalNamespace, - tprOpts.storageFactory(), - ) - completed := config.Complete() - // make the server - glog.V(4).Infoln("Completing API server configuration") - server, err := completed.NewServer() - if err != nil { - return fmt.Errorf("error completing API server configuration: %v", err) - } - addPostStartHooks(server.GenericAPIServer, scConfig, opts.StopCh) - - glog.Infoln("Running the API server") - server.GenericAPIServer.PrepareRun().Run(opts.StopCh) - - return nil + // This should never happen, catch for potential bugs + panic("Unexpected storage type: " + storageType) } func runEtcdServer(opts *ServiceCatalogServerOptions) error { diff --git a/cmd/apiserver/app/server/run_server_test.go b/cmd/apiserver/app/server/run_server_test.go deleted file mode 100644 index d57ab2a3a59..00000000000 --- a/cmd/apiserver/app/server/run_server_test.go +++ /dev/null @@ -1,81 +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 server - -import ( - "errors" - "testing" - - "github.com/kubernetes-incubator/service-catalog/pkg/storage/tpr" - - "k8s.io/apimachinery/pkg/runtime" - kubeclientfake "k8s.io/client-go/kubernetes/fake" - core "k8s.io/client-go/testing" -) - -// make sure RunServer returns with an error when TPR fails to install -func TestRunServerInstallTPRFails(t *testing.T) { - options := NewServiceCatalogServerOptions() - - fakeClientset := &kubeclientfake.Clientset{} - fakeClientset.AddReactor("get", "thirdpartyresources", func(core.Action) (bool, runtime.Object, error) { - return true, nil, errors.New("TPR not found") - }) - fakeClientset.AddReactor("create", "thirdpartyresources", func(core.Action) (bool, runtime.Object, error) { - return true, nil, errors.New("Failed to create TPR") - }) - - options.StorageTypeString = "tpr" - options.TPROptions = &TPROptions{ - "default-name-space", - fakeClientset.Core().RESTClient(), - installTPRsToCore(fakeClientset), - "name-space", - } - - err := RunServer(options) - if _, ok := err.(tpr.ErrTPRInstall); !ok { - t.Errorf("API Server did not report failure after failing to install Third Party Resources") - } - - // make sure no more action after tpr failed to install - getAction := 0 - createAction := 0 - actions := fakeClientset.Actions() - for _, action := range actions { - switch verb := action.GetVerb(); verb { - case "get": - getAction++ - case "create": - createAction++ - default: - t.Errorf("Unexpected action only 'get' and 'create' should be performed, got action: %s", verb) - } - - if action.GetResource().Resource != "thirdpartyresources" { - t.Errorf("Unexpected action performed after failing to install third party resource") - } - } - - if getAction != 5 { - t.Errorf("Expected 5 'get' actions, got %d", getAction) - } - - if createAction != 5 { - t.Errorf("Expected 5 'create' actions, got %d", createAction) - } -} diff --git a/cmd/apiserver/app/server/server.go b/cmd/apiserver/app/server/server.go index 4fa51381e14..06e093c92c5 100644 --- a/cmd/apiserver/app/server/server.go +++ b/cmd/apiserver/app/server/server.go @@ -30,11 +30,8 @@ import ( "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/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/admission" genericserveroptions "k8s.io/apiserver/pkg/server/options" - clientset "k8s.io/client-go/kubernetes" - restclient "k8s.io/client-go/rest" "k8s.io/kubernetes/pkg/util/interrupt" ) @@ -51,8 +48,7 @@ const ( // GroupName I made this up. Maybe we'll need it. GroupName = "service-catalog.k8s.io" - storageTypeFlagName = "storageType" - tprGlobalNamespaceName = "tprGlobalNamespace" + storageTypeFlagName = "storageType" ) // NewCommandServer creates a new cobra command to run our server. @@ -78,7 +74,6 @@ func NewCommandServer( AuthorizationOptions: genericserveroptions.NewDelegatingAuthorizationOptions(), AuditOptions: genericserveroptions.NewAuditOptions(), EtcdOptions: NewEtcdOptions(), - TPROptions: NewTPROptions(), StopCh: stopCh, StandaloneMode: standaloneMode(), } @@ -104,23 +99,8 @@ func NewCommandServer( // Store resources in etcd under our special prefix opts.EtcdOptions.StorageConfig.Prefix = etcdPathPrefix } else { - cfg, err := restclient.InClusterConfig() - if err != nil { - glog.Errorf("Failed to get kube client config (%s)", err) - return nil, err - } - cfg.GroupVersion = &schema.GroupVersion{} - - clIface, err := clientset.NewForConfig(cfg) - if err != nil { - glog.Errorf("Failed to create clientset Interface (%s)", err) - return nil, err - } - - glog.Infof("using third party resources for storage") - opts.TPROptions.DefaultGlobalNamespace = "servicecatalog" - opts.TPROptions.RESTClient = clIface.Core().RESTClient() - opts.TPROptions.InstallTPRsFunc = installTPRsToCore(clIface) + // This should never happen, catch for potential bugs + panic("Unexpected storage type: " + storageType) } cmd.Run = func(c *cobra.Command, args []string) { diff --git a/cmd/apiserver/app/server/tpr_options.go b/cmd/apiserver/app/server/tpr_options.go deleted file mode 100644 index 00603cd4c10..00000000000 --- a/cmd/apiserver/app/server/tpr_options.go +++ /dev/null @@ -1,57 +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 server - -import ( - "github.com/kubernetes-incubator/service-catalog/pkg/api" - "github.com/spf13/pflag" - restclient "k8s.io/client-go/rest" - - serverstorage "k8s.io/apiserver/pkg/server/storage" - "k8s.io/apiserver/pkg/storage/storagebackend" -) - -// TPROptions contains the complete configuration for an API server that -// communicates with the core Kubernetes API server to use third party resources (TPRs) -// as a database. It is exported so that integration tests can use it -type TPROptions struct { - DefaultGlobalNamespace string - RESTClient restclient.Interface - InstallTPRsFunc func() error - GlobalNamespace string -} - -// NewTPROptions creates a new, empty TPROptions struct -func NewTPROptions() *TPROptions { - return &TPROptions{} -} - -// NewStorageFactory returns a new StorageFactory from the config in opts -func (s *TPROptions) storageFactory() serverstorage.StorageFactory { - return serverstorage.NewDefaultStorageFactory( - storagebackend.Config{}, - "application/json", - api.Codecs, - serverstorage.NewDefaultResourceEncodingConfig(api.Registry), - serverstorage.NewResourceConfig(), - ) -} - -func (s *TPROptions) addFlags(fs *pflag.FlagSet) { - fs.StringVar(&s.GlobalNamespace, "global-namespace", s.DefaultGlobalNamespace, ""+ - "The namespace in which to store all TPRs that represent global service-catalog resources.") -} diff --git a/contrib/jenkins/install_catalog.sh b/contrib/jenkins/install_catalog.sh index 867f47d9c80..6b024d46f5f 100755 --- a/contrib/jenkins/install_catalog.sh +++ b/contrib/jenkins/install_catalog.sh @@ -24,7 +24,6 @@ while [[ $# -gt 0 ]]; do case "${1}" in --registry) REGISTRY="${2:-}"; shift ;; --version) VERSION="${2:-}"; shift ;; - --with-tpr) WITH_TPR=true ;; --fix-auth) FIX_CONFIGMAP=true ;; *) error_exit "Unrecognized command line parameter: $1" ;; esac @@ -33,7 +32,6 @@ done REGISTRY="${REGISTRY:-}" VERSION="${VERSION:-"canary"}" -WITH_TPR="${WITH_TPR:-false}" FIX_CONFIGMAP="${FIX_CONFIGMAP:-false}" CONTROLLER_MANAGER_IMAGE="${REGISTRY}controller-manager:${VERSION}" @@ -66,10 +64,6 @@ VALUES+="controllerManager.image=${CONTROLLER_MANAGER_IMAGE}" VALUES+=",apiserver.image=${APISERVER_IMAGE}" VALUES+=",apiserver.service.type=NodePort" VALUES+=",apiserver.service.nodePort.securePort=30443" -if [[ "${WITH_TPR}" == true ]]; then - VALUES+=',apiserver.storage.type=tpr' - VALUES+=',apiserver.storage.tpr.globalNamespace=test-ns' -fi retry \ helm install "${ROOT}/charts/catalog" \ diff --git a/docs/design.md b/docs/design.md index 591be9a66ab..49e8afb217d 100644 --- a/docs/design.md +++ b/docs/design.md @@ -83,17 +83,17 @@ resource model. The storage component behind the Service Catalog's API Server can either be [etcd](https://github.com/coreos/etcd) or -[Third Party Resources](https://kubernetes.io/docs/user-guide/thirdpartyresources/) (TPRs). +[Custom Resources](https://kubernetes.io/docs/concepts/api-extension/custom-resources/) (CRDs). The `rest.storage` interface abstracts the specific persistent storage facility being used. When etcd is used, the instance(s) of etcd will be distinct from the etcd instances of the Kubernetes core - meaning, the Service Catalog will have its own persistent storage that is separate from the Kubernetes core. -When TPRs are used, those resources will be stored in the Kubernetes core +When CRDs are used, those resources will be stored in the Kubernetes core and therefore a separate persistent storage (from Kubernetes) is not needed. **[DIFF]** *As of now the API Server can only use etcd as its persistent -storage. The plan is to add support for TPRs to the `rest.storage` interface +storage. The plan is to add support for CRDs to the `rest.storage` interface of the API Server in the near future.* The Service Catalog resource model is defined within a file called @@ -328,8 +328,3 @@ Below are the key aspects of the code that differ from the design above: actually used as part of the running system yet. Any resources created by talking to the API Server will be stored but nothing beyond storing them will happen. -- Creating Third Party Resource versions of the Service Catalog resources - in the Kubernetes core API Server is the current way the system works. - The Controller will then talk to the Kubernetes core API Server - and monitor the TPR version of the Service Catalog resources and take - all appropriate actions. diff --git a/docs/install-1.6.md b/docs/install-1.6.md index 2742e7dbdc6..d300c564a3d 100644 --- a/docs/install-1.6.md +++ b/docs/install-1.6.md @@ -96,12 +96,8 @@ wide variety of customizations which are detailed in that directory's We'll be interacting with a variety of resources in the following steps. The service catalog API server needs to store all of these resources in a data -store. The data store implementation in the API server is pluggable, and we -currently support the following implementations: - -1. Etcd 3 -2. Third Party Resources (also, known as TPRs) - this is an _alpha_ feature -right now. It has known issues and may be removed at any time. +store. The data store implementation in the API server is pluggable, but we +currently support only Etcd 3. The first implementation requires that the API server has access to an Etcd 3 cluster, and the second only requires access to the Kubernetes API to store TPRs. @@ -117,12 +113,6 @@ To install the service catalog system with Etcd 3 as the backing data store: helm install charts/catalog --name catalog --namespace catalog ``` -To install the service catalog system with TPRs as the backing data store: - -```console -helm install charts/catalog --name catalog --namespace catalog --set apiserver.storage.type=tpr,apiserver.storage.tpr.globalNamespace=catalog -``` - Regardless of which data store implementation you choose, the remainder of the steps in this walkthrough will stay the same. diff --git a/pkg/apiserver/tpr_config.go b/pkg/apiserver/tpr_config.go deleted file mode 100644 index e85ee69660a..00000000000 --- a/pkg/apiserver/tpr_config.go +++ /dev/null @@ -1,106 +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 apiserver - -import ( - "github.com/golang/glog" - "github.com/kubernetes-incubator/service-catalog/pkg/registry/servicecatalog/server" - genericapiserver "k8s.io/apiserver/pkg/server" - "k8s.io/apiserver/pkg/server/storage" - restclient "k8s.io/client-go/rest" -) - -// tprConfig is the configuration needed to run the API server in TPR storage mode -type tprConfig struct { - restClient restclient.Interface - genericConfig *genericapiserver.Config - globalNamespace string - storageFactory storage.StorageFactory -} - -// NewTPRConfig returns a new Config for a server that is backed by TPR storage -func NewTPRConfig( - restClient restclient.Interface, - genericCfg *genericapiserver.Config, - globalNS string, - factory storage.StorageFactory, -) Config { - return &tprConfig{ - restClient: restClient, - genericConfig: genericCfg, - globalNamespace: globalNS, - storageFactory: factory, - } -} - -// Complete fills in the remaining fields of t and returns a completed config -func (t *tprConfig) Complete() CompletedConfig { - completeGenericConfig(t.genericConfig) - return &completedTPRConfig{ - restClient: t.restClient, - tprConfig: t, - // Not every API group compiled in is necessarily enabled by the operator - // at runtime. - // - // Install the API resource config source, which describes versions of - // which API groups are enabled. - apiResourceConfigSource: DefaultAPIResourceConfigSource(), - factory: t.storageFactory, - } -} - -// CompletedTPRConfig is the completed version of the TPR config. It can be used to create a -// new server, ready to be run -type completedTPRConfig struct { - restClient restclient.Interface - *tprConfig - apiResourceConfigSource storage.APIResourceConfigSource - factory storage.StorageFactory -} - -// NewServer returns a new service catalog server, that is ready for execution -func (c *completedTPRConfig) NewServer() (*ServiceCatalogAPIServer, error) { - s, err := createSkeletonServer(c.tprConfig.genericConfig) - if err != nil { - return nil, err - } - glog.V(4).Infoln("Created skeleton API server. Installing API groups") - - roFactory := tprRESTOptionsFactory{ - storageFactory: c.factory, - } - - providers := restStorageProviders(c.globalNamespace, server.StorageTypeTPR, c.restClient) - for _, provider := range providers { - groupInfo, err := provider.NewRESTStorage( - c.apiResourceConfigSource, // genericapiserver.APIResourceConfigSource - roFactory, // registry.RESTOptionsGetter - ) - if IsErrAPIGroupDisabled(err) { - glog.Warningf("Skipping API group %v because it is not enabled", provider.GroupName()) - continue - } else if err != nil { - return nil, err - } - glog.V(4).Infof("Installing API group %v", provider.GroupName()) - if err := s.GenericAPIServer.InstallAPIGroup(groupInfo); err != nil { - glog.Fatalf("Error installing API group %v: %v", provider.GroupName(), err) - } - } - glog.Infoln("Finished installing API groups") - return s, nil -} diff --git a/pkg/apiserver/tpr_rest_options_factory.go b/pkg/apiserver/tpr_rest_options_factory.go deleted file mode 100644 index 241b75a7257..00000000000 --- a/pkg/apiserver/tpr_rest_options_factory.go +++ /dev/null @@ -1,43 +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 apiserver - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apiserver/pkg/registry/generic" - "k8s.io/apiserver/pkg/server/storage" -) - -type tprRESTOptionsFactory struct { - storageFactory storage.StorageFactory -} - -func (t tprRESTOptionsFactory) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) { - storageConfig, err := t.storageFactory.NewConfig(resource) - if err != nil { - return generic.RESTOptions{}, err - } - // this function should create a RESTOptions that contains a Decorator function to create - // a TPR based storage config. This should be done in a follow up to - // https://github.com/kubernetes-incubator/service-catalog/pull/338. When this decorator - // function is implemented, the 'NewStorage' functions in - // ./pkg/registry/servicecatalog/{binding,broker,instance,serviceclass} no longer will need - // to have the switching logic to choose between TPR and etcd - return generic.RESTOptions{ - StorageConfig: storageConfig, - }, nil -} diff --git a/pkg/registry/servicecatalog/binding/storage.go b/pkg/registry/servicecatalog/binding/storage.go index b5040ff8e3b..c30ed958257 100644 --- a/pkg/registry/servicecatalog/binding/storage.go +++ b/pkg/registry/servicecatalog/binding/storage.go @@ -23,7 +23,6 @@ import ( scmeta "github.com/kubernetes-incubator/service-catalog/pkg/api/meta" "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog" "github.com/kubernetes-incubator/service-catalog/pkg/registry/servicecatalog/server" - "github.com/kubernetes-incubator/service-catalog/pkg/storage/tpr" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" @@ -45,7 +44,7 @@ var ( func NewSingular(ns, name string) runtime.Object { return &servicecatalog.ServiceInstanceCredential{ TypeMeta: metav1.TypeMeta{ - Kind: tpr.ServiceInstanceCredentialKind.String(), + Kind: "ServiceInstanceCredential", }, ObjectMeta: metav1.ObjectMeta{ Namespace: ns, @@ -63,7 +62,7 @@ func EmptyObject() runtime.Object { func NewList() runtime.Object { return &servicecatalog.ServiceInstanceCredentialList{ TypeMeta: metav1.TypeMeta{ - Kind: tpr.ServiceInstanceCredentialListKind.String(), + Kind: "ServiceInstanceCredentialList", }, Items: []servicecatalog.ServiceInstanceCredential{}, } diff --git a/pkg/registry/servicecatalog/broker/storage.go b/pkg/registry/servicecatalog/broker/storage.go index 67506ca4c3d..59f66f2907b 100644 --- a/pkg/registry/servicecatalog/broker/storage.go +++ b/pkg/registry/servicecatalog/broker/storage.go @@ -23,7 +23,6 @@ import ( scmeta "github.com/kubernetes-incubator/service-catalog/pkg/api/meta" "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog" "github.com/kubernetes-incubator/service-catalog/pkg/registry/servicecatalog/server" - "github.com/kubernetes-incubator/service-catalog/pkg/storage/tpr" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" @@ -46,7 +45,7 @@ var ( func NewSingular(ns, name string) runtime.Object { return &servicecatalog.ServiceBroker{ TypeMeta: metav1.TypeMeta{ - Kind: tpr.ServiceBrokerKind.String(), + Kind: "ServiceBroker", }, ObjectMeta: metav1.ObjectMeta{ Namespace: ns, @@ -64,7 +63,7 @@ func EmptyObject() runtime.Object { func NewList() runtime.Object { return &servicecatalog.ServiceBrokerList{ TypeMeta: metav1.TypeMeta{ - Kind: tpr.ServiceBrokerListKind.String(), + Kind: "ServiceBrokerList", }, Items: []servicecatalog.ServiceBroker{}, } diff --git a/pkg/registry/servicecatalog/instance/storage.go b/pkg/registry/servicecatalog/instance/storage.go index 5bf75d94865..e0c451e1c75 100644 --- a/pkg/registry/servicecatalog/instance/storage.go +++ b/pkg/registry/servicecatalog/instance/storage.go @@ -23,7 +23,6 @@ import ( scmeta "github.com/kubernetes-incubator/service-catalog/pkg/api/meta" "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog" "github.com/kubernetes-incubator/service-catalog/pkg/registry/servicecatalog/server" - "github.com/kubernetes-incubator/service-catalog/pkg/storage/tpr" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" @@ -45,7 +44,7 @@ var ( func NewSingular(ns, name string) runtime.Object { return &servicecatalog.ServiceInstance{ TypeMeta: metav1.TypeMeta{ - Kind: tpr.ServiceInstanceKind.String(), + Kind: "ServiceInstance", }, ObjectMeta: metav1.ObjectMeta{ Namespace: ns, @@ -63,7 +62,7 @@ func EmptyObject() runtime.Object { func NewList() runtime.Object { return &servicecatalog.ServiceInstanceList{ TypeMeta: metav1.TypeMeta{ - Kind: tpr.ServiceInstanceListKind.String(), + Kind: "ServiceInstanceList", }, Items: []servicecatalog.ServiceInstance{}, } diff --git a/pkg/registry/servicecatalog/rest/storage_servicecatalog.go b/pkg/registry/servicecatalog/rest/storage_servicecatalog.go index b0b2c055bf2..239101617f7 100644 --- a/pkg/registry/servicecatalog/rest/storage_servicecatalog.go +++ b/pkg/registry/servicecatalog/rest/storage_servicecatalog.go @@ -27,7 +27,6 @@ import ( "github.com/kubernetes-incubator/service-catalog/pkg/registry/servicecatalog/serviceclass" "github.com/kubernetes-incubator/service-catalog/pkg/registry/servicecatalog/serviceplan" "github.com/kubernetes-incubator/service-catalog/pkg/storage/etcd" - "github.com/kubernetes-incubator/service-catalog/pkg/storage/tpr" "k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/rest" genericapiserver "k8s.io/apiserver/pkg/server" @@ -84,23 +83,6 @@ func (p StorageProvider) v1alpha1Storage( GetAttrsFunc: broker.GetAttrs, Trigger: storage.NoTriggerPublisher, }, - tpr.Options{ - HasNamespace: false, - RESTOptions: brokerRESTOptions, - DefaultNamespace: p.DefaultNamespace, - RESTClient: p.RESTClient, - SingularKind: tpr.ServiceBrokerKind, - NewSingularFunc: broker.NewSingular, - ListKind: tpr.ServiceBrokerListKind, - NewListFunc: broker.NewList, - CheckObjectFunc: broker.CheckObject, - DestroyFunc: func() {}, - Keyer: tpr.Keyer{ - DefaultNamespace: p.DefaultNamespace, - ResourceName: tpr.ServiceBrokerKind.String(), - Separator: "/", - }, - }, p.StorageType, ) @@ -118,24 +100,6 @@ func (p StorageProvider) v1alpha1Storage( GetAttrsFunc: serviceclass.GetAttrs, Trigger: storage.NoTriggerPublisher, }, - tpr.Options{ - HasNamespace: false, - RESTOptions: serviceClassRESTOptions, - DefaultNamespace: p.DefaultNamespace, - RESTClient: p.RESTClient, - SingularKind: tpr.ServiceClassKind, - NewSingularFunc: serviceclass.NewSingular, - ListKind: tpr.ServiceClassListKind, - NewListFunc: serviceclass.NewList, - CheckObjectFunc: serviceclass.CheckObject, - DestroyFunc: func() {}, - Keyer: tpr.Keyer{ - DefaultNamespace: p.DefaultNamespace, - ResourceName: tpr.ServiceClassKind.String(), - Separator: "/", - }, - HardDelete: true, - }, p.StorageType, ) @@ -153,24 +117,6 @@ func (p StorageProvider) v1alpha1Storage( GetAttrsFunc: serviceplan.GetAttrs, Trigger: storage.NoTriggerPublisher, }, - tpr.Options{ - HasNamespace: false, - RESTOptions: servicePlanRESTOptions, - DefaultNamespace: p.DefaultNamespace, - RESTClient: p.RESTClient, - SingularKind: tpr.ServicePlanKind, - NewSingularFunc: serviceplan.NewSingular, - ListKind: tpr.ServicePlanListKind, - NewListFunc: serviceplan.NewList, - CheckObjectFunc: serviceplan.CheckObject, - DestroyFunc: func() {}, - Keyer: tpr.Keyer{ - DefaultNamespace: p.DefaultNamespace, - ResourceName: tpr.ServicePlanKind.String(), - Separator: "/", - }, - HardDelete: true, - }, p.StorageType, ) @@ -188,23 +134,6 @@ func (p StorageProvider) v1alpha1Storage( GetAttrsFunc: instance.GetAttrs, Trigger: storage.NoTriggerPublisher, }, - tpr.Options{ - HasNamespace: true, - RESTOptions: instanceClassRESTOptions, - DefaultNamespace: p.DefaultNamespace, - RESTClient: p.RESTClient, - SingularKind: tpr.ServiceInstanceKind, - NewSingularFunc: instance.NewSingular, - ListKind: tpr.ServiceInstanceListKind, - NewListFunc: instance.NewList, - CheckObjectFunc: instance.CheckObject, - DestroyFunc: func() {}, - Keyer: tpr.Keyer{ - DefaultNamespace: p.DefaultNamespace, - ResourceName: tpr.ServiceInstanceKind.String(), - Separator: "/", - }, - }, p.StorageType, ) @@ -222,23 +151,6 @@ func (p StorageProvider) v1alpha1Storage( GetAttrsFunc: binding.GetAttrs, Trigger: storage.NoTriggerPublisher, }, - tpr.Options{ - HasNamespace: true, - RESTOptions: bindingClassRESTOptions, - DefaultNamespace: p.DefaultNamespace, - RESTClient: p.RESTClient, - SingularKind: tpr.ServiceInstanceCredentialKind, - NewSingularFunc: binding.NewSingular, - ListKind: tpr.ServiceInstanceCredentialListKind, - NewListFunc: binding.NewList, - CheckObjectFunc: binding.CheckObject, - DestroyFunc: func() {}, - Keyer: tpr.Keyer{ - DefaultNamespace: p.DefaultNamespace, - ResourceName: tpr.ServiceInstanceCredentialKind.String(), - Separator: "/", - }, - }, p.StorageType, ) diff --git a/pkg/registry/servicecatalog/rest/storage_servicecatalog_test.go b/pkg/registry/servicecatalog/rest/storage_servicecatalog_test.go index 044d5e1639e..dc36930af81 100644 --- a/pkg/registry/servicecatalog/rest/storage_servicecatalog_test.go +++ b/pkg/registry/servicecatalog/rest/storage_servicecatalog_test.go @@ -62,7 +62,7 @@ func testRESTOptionsGetter( func TestV1Alpha1Storage(t *testing.T) { provider := StorageProvider{ DefaultNamespace: "test-default", - StorageType: server.StorageTypeTPR, + StorageType: server.StorageTypeEtcd, RESTClient: nil, } configSource := serverstorage.NewResourceConfig() diff --git a/pkg/registry/servicecatalog/server/options.go b/pkg/registry/servicecatalog/server/options.go index efd9f5db7aa..8389c5094a1 100644 --- a/pkg/registry/servicecatalog/server/options.go +++ b/pkg/registry/servicecatalog/server/options.go @@ -21,7 +21,6 @@ import ( "github.com/kubernetes-incubator/service-catalog/pkg/api" "github.com/kubernetes-incubator/service-catalog/pkg/storage/etcd" - "github.com/kubernetes-incubator/service-catalog/pkg/storage/tpr" "k8s.io/apimachinery/pkg/runtime" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/generic/registry" @@ -45,8 +44,6 @@ type StorageType string // error if s names an invalid or unsupported storage type func StorageTypeFromString(s string) (StorageType, error) { switch s { - case StorageTypeTPR.String(): - return StorageTypeTPR, nil case StorageTypeEtcd.String(): return StorageTypeEtcd, nil default: @@ -59,9 +56,6 @@ func (s StorageType) String() string { } const ( - // StorageTypeTPR indicates a storage interface should use TPRs - // TPRs - StorageTypeTPR StorageType = "tpr" // StorageTypeEtcd indicates a storage interface should use etcd StorageTypeEtcd StorageType = "etcd" ) @@ -70,19 +64,16 @@ const ( // specific things type Options struct { EtcdOptions etcd.Options - TPROptions tpr.Options storageType StorageType } // NewOptions returns a new Options with the given parameters func NewOptions( etcdOpts etcd.Options, - tprOpts tpr.Options, sType StorageType, ) *Options { return &Options{ EtcdOptions: etcdOpts, - TPROptions: tprOpts, storageType: sType, } } @@ -91,7 +82,7 @@ func NewOptions( // storage type is indicated func (o Options) StorageType() (StorageType, error) { switch o.storageType { - case StorageTypeTPR, StorageTypeEtcd: + case StorageTypeEtcd: return o.storageType, nil default: return StorageType(""), errUnsupportedStorageType{t: o.storageType} @@ -117,7 +108,8 @@ func (o Options) KeyRootFunc() func(genericapirequest.Context) string { return registry.NamespaceKeyRootFunc(ctx, prefix) } } - return o.TPROptions.Keyer.KeyRoot + // This should never happen, catch for potential bugs + panic("Unexpected storage type: " + sType) } // KeyFunc returns the appropriate key function for the storage type in o. @@ -137,7 +129,7 @@ func (o Options) KeyFunc(namespaced bool) func(genericapirequest.Context, string return registry.NoNamespaceKeyFunc(ctx, prefix, name) } } - return o.TPROptions.Keyer.Key + panic("Unexpected storage type: " + o.storageType) } // GetStorage returns the storage from the given parameters @@ -164,5 +156,5 @@ func (o Options) GetStorage( trigger, ) } - return tpr.NewStorage(o.TPROptions) + panic("Unexpected storage type: " + o.storageType) } diff --git a/pkg/registry/servicecatalog/server/options_test.go b/pkg/registry/servicecatalog/server/options_test.go index cee42d53c28..baa4b726ff8 100644 --- a/pkg/registry/servicecatalog/server/options_test.go +++ b/pkg/registry/servicecatalog/server/options_test.go @@ -20,12 +20,11 @@ import ( "testing" "github.com/kubernetes-incubator/service-catalog/pkg/storage/etcd" - "github.com/kubernetes-incubator/service-catalog/pkg/storage/tpr" ) func TestNewOptions(t *testing.T) { origStorageType := StorageTypeEtcd - opts := NewOptions(etcd.Options{}, tpr.Options{}, origStorageType) + opts := NewOptions(etcd.Options{}, origStorageType) retStorageType, err := opts.StorageType() if err != nil { t.Fatalf("getting storage type (%s)", err) diff --git a/pkg/registry/servicecatalog/serviceclass/storage.go b/pkg/registry/servicecatalog/serviceclass/storage.go index 0fcb26e0a60..2334488f9d6 100644 --- a/pkg/registry/servicecatalog/serviceclass/storage.go +++ b/pkg/registry/servicecatalog/serviceclass/storage.go @@ -23,7 +23,6 @@ import ( scmeta "github.com/kubernetes-incubator/service-catalog/pkg/api/meta" "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog" "github.com/kubernetes-incubator/service-catalog/pkg/registry/servicecatalog/server" - "github.com/kubernetes-incubator/service-catalog/pkg/storage/tpr" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" @@ -44,7 +43,7 @@ var ( func NewSingular(ns, name string) runtime.Object { return &servicecatalog.ServiceClass{ TypeMeta: metav1.TypeMeta{ - Kind: tpr.ServiceClassKind.String(), + Kind: "ServiceClass", }, ObjectMeta: metav1.ObjectMeta{ Namespace: ns, @@ -62,7 +61,7 @@ func EmptyObject() runtime.Object { func NewList() runtime.Object { return &servicecatalog.ServiceClassList{ TypeMeta: metav1.TypeMeta{ - Kind: tpr.ServiceClassListKind.String(), + Kind: "ServiceClassList", }, Items: []servicecatalog.ServiceClass{}, } diff --git a/pkg/registry/servicecatalog/serviceplan/storage.go b/pkg/registry/servicecatalog/serviceplan/storage.go index a5e23c6c321..573631ff7ef 100644 --- a/pkg/registry/servicecatalog/serviceplan/storage.go +++ b/pkg/registry/servicecatalog/serviceplan/storage.go @@ -23,7 +23,6 @@ import ( scmeta "github.com/kubernetes-incubator/service-catalog/pkg/api/meta" "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog" "github.com/kubernetes-incubator/service-catalog/pkg/registry/servicecatalog/server" - "github.com/kubernetes-incubator/service-catalog/pkg/storage/tpr" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" @@ -44,7 +43,7 @@ var ( func NewSingular(ns, name string) runtime.Object { return &servicecatalog.ServicePlan{ TypeMeta: metav1.TypeMeta{ - Kind: tpr.ServicePlanKind.String(), + Kind: "ServicePlan", }, ObjectMeta: metav1.ObjectMeta{ Namespace: ns, @@ -62,7 +61,7 @@ func EmptyObject() runtime.Object { func NewList() runtime.Object { return &servicecatalog.ServicePlanList{ TypeMeta: metav1.TypeMeta{ - Kind: tpr.ServicePlanListKind.String(), + Kind: "ServicePlanList", }, Items: []servicecatalog.ServicePlan{}, } diff --git a/pkg/storage/tpr/client.go b/pkg/storage/tpr/client.go deleted file mode 100644 index ddef6bdd1ae..00000000000 --- a/pkg/storage/tpr/client.go +++ /dev/null @@ -1,49 +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 tpr - -import ( - "fmt" - - "k8s.io/client-go/dynamic" -) - -type errUnsupportedResource struct { - kind Kind -} - -func (e errUnsupportedResource) Error() string { - return fmt.Sprintf("unsupported resource %s", e.kind) -} - -// GetResourceClient returns the *dynamic.ResourceClient for a given resource type -func GetResourceClient(cl *dynamic.Client, kind Kind, namespace string) (*dynamic.ResourceClient, error) { - switch kind { - case ServiceInstanceKind, ServiceInstanceListKind: - return cl.Resource(&ServiceInstanceResource, namespace), nil - case ServiceInstanceCredentialKind, ServiceInstanceCredentialListKind: - return cl.Resource(&ServiceInstanceCredentialResource, namespace), nil - case ServiceBrokerKind, ServiceBrokerListKind: - return cl.Resource(&ServiceBrokerResource, namespace), nil - case ServiceClassKind, ServiceClassListKind: - return cl.Resource(&ServiceClassResource, namespace), nil - case ServicePlanKind, ServicePlanListKind: - return cl.Resource(&ServicePlanResource, namespace), nil - default: - return nil, errUnsupportedResource{kind: kind} - } -} diff --git a/pkg/storage/tpr/delete.go b/pkg/storage/tpr/delete.go deleted file mode 100644 index 439f1314d42..00000000000 --- a/pkg/storage/tpr/delete.go +++ /dev/null @@ -1,56 +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 tpr - -import ( - "fmt" - "net/http" - - "github.com/golang/glog" - "k8s.io/apiserver/pkg/storage" - restclient "k8s.io/client-go/rest" -) - -func delete(cl restclient.Interface, kind Kind, key, ns, name string, expectedCode int) error { - req := cl.Delete().AbsPath( - "apis", - groupName, - tprVersion, - "namespaces", - ns, - kind.URLName(), - name, - ) - res := req.Do() - if res.Error() != nil { - glog.Errorf("executing DELETE for %s/%s (%s)", ns, name, res.Error()) - } - var statusCode int - res.StatusCode(&statusCode) - if statusCode == http.StatusNotFound { - return storage.NewKeyNotFoundError(key, 0) - } - if statusCode != expectedCode { - return fmt.Errorf( - "executing DELETE for %s/%s, received response code %d", - ns, - name, - statusCode, - ) - } - return nil -} diff --git a/pkg/storage/tpr/get.go b/pkg/storage/tpr/get.go deleted file mode 100644 index 703cd89cd26..00000000000 --- a/pkg/storage/tpr/get.go +++ /dev/null @@ -1,88 +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 tpr - -import ( - "fmt" - "net/http" - - "github.com/golang/glog" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apiserver/pkg/storage" - restclient "k8s.io/client-go/rest" -) - -func get( - cl restclient.Interface, - codec runtime.Codec, - kind Kind, - key, - ns, - name string, - out runtime.Object, - hasNamespace, - ignoreNotFound bool, -) error { - req := cl.Get().AbsPath( - "apis", - groupName, - tprVersion, - "namespaces", - ns, - kind.URLName(), - name, - ) - - res := req.Do() - if res.Error() != nil { - glog.Errorf("executing GET for %s/%s (%s)", ns, name, res.Error()) - } - var statusCode int - res.StatusCode(&statusCode) - if statusCode == http.StatusNotFound { - if ignoreNotFound { - return runtime.SetZeroValue(out) - } - glog.Errorf("executing GET for %s/%s: not found", ns, name) - return storage.NewKeyNotFoundError(key, 0) - } - if statusCode != http.StatusOK { - return fmt.Errorf( - "executing GET for %s/%s, received response code %d", - ns, - name, - statusCode, - ) - } - - var unknown runtime.Unknown - if err := res.Into(&unknown); err != nil { - glog.Errorf("decoding response (%s)", err) - return err - } - - if err := decode(codec, unknown.Raw, out); err != nil { - return nil - } - if !hasNamespace { - if err := removeNamespace(out); err != nil { - glog.Errorf("removing namespace from %#v (%s)", out, err) - return err - } - } - return nil -} diff --git a/pkg/storage/tpr/init_test.go b/pkg/storage/tpr/init_test.go deleted file mode 100644 index 8eb2208ed6b..00000000000 --- a/pkg/storage/tpr/init_test.go +++ /dev/null @@ -1,50 +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 tpr - -import ( - "log" - - "github.com/kubernetes-incubator/service-catalog/pkg/api" - "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog" - _ "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/install" - "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/testapi" - "k8s.io/apimachinery/pkg/runtime/schema" - _ "k8s.io/client-go/rest" -) - -func serviceCatalogAPIGroup() testapi.TestGroup { - // OOPS: didn't register the right group version - groupVersion := schema.GroupVersion{Group: servicecatalog.GroupName, Version: "v1alpha1"} - - externalGroupVersion := schema.GroupVersion{ - Group: groupName, - Version: api.Registry.GroupOrDie(servicecatalog.GroupName).GroupVersion.Version, - } - - return testapi.NewTestGroup( - groupVersion, - servicecatalog.SchemeGroupVersion, - api.Scheme.KnownTypes(servicecatalog.SchemeGroupVersion), - api.Scheme.KnownTypes(externalGroupVersion), - ) -} - -func init() { - log.SetFlags(log.Lshortfile) - testapi.Groups[servicecatalog.GroupName] = serviceCatalogAPIGroup() -} diff --git a/pkg/storage/tpr/install_types.go b/pkg/storage/tpr/install_types.go deleted file mode 100644 index 9e25b97d3f8..00000000000 --- a/pkg/storage/tpr/install_types.go +++ /dev/null @@ -1,106 +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 tpr - -import ( - "fmt" - "sync" - "time" - - "github.com/golang/glog" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/wait" - extensionsv1beta "k8s.io/client-go/kubernetes/typed/extensions/v1beta1" - "k8s.io/client-go/pkg/apis/extensions/v1beta1" -) - -// this is the set of third party resources to be installed. each key is the name of the TPR to -// install, and each value is the resource to install -// -var thirdPartyResources = []v1beta1.ThirdPartyResource{ - serviceBrokerTPR, - serviceClassTPR, - servicePlanTPR, - serviceInstanceTPR, - serviceInstanceCredentialTPR, -} - -// ErrTPRInstall is returned when we fail to install TPR -type ErrTPRInstall struct { - errMsg string -} - -func (e ErrTPRInstall) Error() string { - return e.errMsg -} - -// InstallTypes installs all third party resource types to the cluster -func InstallTypes(cl extensionsv1beta.ThirdPartyResourceInterface) error { - var wg sync.WaitGroup - errMsg := make(chan string, len(thirdPartyResources)) - - for _, tpr := range thirdPartyResources { - glog.Infof("Checking for existence of %s", tpr.Name) - if _, err := cl.Get(tpr.Name, metav1.GetOptions{}); err == nil { - glog.Infof("Found existing TPR %s", tpr.Name) - continue - } - - glog.Infof("Creating Third Party Resource Type: %s", tpr.Name) - - wg.Add(1) - go func(tpr v1beta1.ThirdPartyResource, client extensionsv1beta.ThirdPartyResourceInterface) { - defer wg.Done() - if _, err := cl.Create(&tpr); err != nil { - errMsg <- fmt.Sprintf("%s: %s", tpr.Name, err) - } else { - glog.Infof("Created TPR '%s'", tpr.Name) - - // There can be a delay, so poll until it's ready to go... - err := wait.PollImmediate(1*time.Second, 30*time.Second, func() (bool, error) { - if _, err := client.Get(tpr.Name, metav1.GetOptions{}); err == nil { - glog.Infof("TPR %s is ready", tpr.Name) - return true, nil - } - - glog.Infof("TPR %s is not ready yet... waiting...", tpr.Name) - return false, nil - }) - if err != nil { - glog.Infof("Error polling for TPR status:", err) - } - } - }(tpr, cl) - } - - wg.Wait() - close(errMsg) - - var allErrMsg string - for msg := range errMsg { - if msg != "" { - allErrMsg = fmt.Sprintf("%s\n%s", allErrMsg, msg) - } - } - - if allErrMsg != "" { - glog.Errorf("Failed to create Third Party Resource:\n%s)", allErrMsg) - return ErrTPRInstall{fmt.Sprintf("Failed to create Third Party Resource:%s)", allErrMsg)} - } - - return nil -} diff --git a/pkg/storage/tpr/install_types_test.go b/pkg/storage/tpr/install_types_test.go deleted file mode 100644 index b06465c88c0..00000000000 --- a/pkg/storage/tpr/install_types_test.go +++ /dev/null @@ -1,185 +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 tpr - -import ( - "errors" - "fmt" - "strconv" - "strings" - "testing" - - "k8s.io/apimachinery/pkg/runtime" - - kubeclientfake "k8s.io/client-go/kubernetes/fake" - "k8s.io/client-go/pkg/apis/extensions/v1beta1" - core "k8s.io/client-go/testing" -) - -func setup(getFn, createFn func(core.Action) (bool, runtime.Object, error)) *kubeclientfake.Clientset { - fakeClientset := &kubeclientfake.Clientset{} - - fakeClientset.AddReactor("get", "thirdpartyresources", getFn) - - fakeClientset.AddReactor("create", "thirdpartyresources", createFn) - - return fakeClientset -} - -//make sure all resources types are installed -func TestInstallTypesAllResources(t *testing.T) { - createCallCount := 0 - - fakeClientset := setup( - func(core.Action) (bool, runtime.Object, error) { - // if 'create' has been called on all tprs, return 'nil' error to indicate tpr is created - if createCallCount == len(thirdPartyResources) { - return true, &v1beta1.ThirdPartyResource{}, nil - } - - // return error to indicate tpr is not found - return true, nil, fmt.Errorf("Resource not found : %v", name) - }, - func(core.Action) (bool, runtime.Object, error) { - createCallCount++ - return true, nil, nil - }, - ) - - if err := InstallTypes(fakeClientset.Extensions().ThirdPartyResources()); err != nil { - t.Fatalf("error installing types (%s)", err) - } - - expectTotal := len(thirdPartyResources) - if createCallCount != expectTotal { - t.Errorf("Expected %d Third Party Resources created instead of %d", expectTotal, createCallCount) - } -} - -//make sure to skip resource that is already installed -func TestInstallTypesResourceExisted(t *testing.T) { - getCallCount := 0 - createCallCount := 0 - createCallArgs := []string{} - - fakeClientset := setup( - func(core.Action) (bool, runtime.Object, error) { - getCallCount++ - if getCallCount == 1 { - // return broker TPR on 1st call to indicate broker TPR exists - return true, &serviceBrokerTPR, nil - } else if createCallCount == len(thirdPartyResources)-1 { - // once 'create' has been called on all tprs, return 'nil' error to indicate tpr is created - return true, &v1beta1.ThirdPartyResource{}, nil - } - - return true, nil, errors.New("Resource not found") - }, - func(action core.Action) (bool, runtime.Object, error) { - createCallCount++ - createCallArgs = append(createCallArgs, action.(core.CreateAction).GetObject().(*v1beta1.ThirdPartyResource).Name) - return true, nil, nil - }, - ) - - if err := InstallTypes(fakeClientset.Extensions().ThirdPartyResources()); err != nil { - t.Fatalf("error installing (%s)", err) - } - - if createCallCount != len(thirdPartyResources)-1 { - t.Errorf("Failed to skip 1 installed Third Party Resource") - } - - for _, name := range createCallArgs { - if name == serviceBrokerTPR.Name { - t.Errorf("Failed to skip installing 'broker' as Third Party Resource as it already existed") - } - } -} - -//make sure all errors are received for all failed install -func TestInstallTypesErrors(t *testing.T) { - getCallCount := 0 - createCallCount := 0 - - fakeClientset := setup( - func(core.Action) (bool, runtime.Object, error) { - getCallCount++ - // if 'create' has been called on all tprs, return 'nil' error to indicate tpr is created - if createCallCount == len(thirdPartyResources) { - return true, &v1beta1.ThirdPartyResource{}, nil - } - - // return error to indicate tpr is not found - return true, nil, errors.New("Resource not found") - }, - func(core.Action) (bool, runtime.Object, error) { - createCallCount++ - if createCallCount <= 2 { - return true, nil, errors.New("Error " + strconv.Itoa(createCallCount)) - } - return true, nil, nil - }, - ) - - err := InstallTypes(fakeClientset.Extensions().ThirdPartyResources()) - - errStr := err.Error() - if !strings.Contains(errStr, "Error 1") && !strings.Contains(errStr, "Error 2") { - t.Errorf("Failed to receive correct errors during installation of Third Party Resource concurrently, error received: %s", errStr) - } -} - -//make sure we don't poll on resource that was failed on install -func TestInstallTypesPolling(t *testing.T) { - getCallCount := 0 - createCallCount := 0 - getCallActions := []core.GetAction{} - - fakeClientset := setup( - func(action core.Action) (bool, runtime.Object, error) { - getCallCount++ - // wait until we've had enough GET calls to return a tpr. - if getCallCount > len(thirdPartyResources) { - getCallActions = append(getCallActions, action.(core.GetAction)) - return true, &v1beta1.ThirdPartyResource{}, nil - } - - // return error to indicate tpr is not found - return true, nil, errors.New("Resource not found") - }, - func(action core.Action) (bool, runtime.Object, error) { - createCallCount++ - name := action.(core.CreateAction).GetObject().(*v1beta1.ThirdPartyResource).Name - if name == serviceBrokerTPR.Name || name == serviceInstanceTPR.Name { - return true, nil, fmt.Errorf("Error creatingTPR : %v", name) - } - return true, nil, nil - }, - ) - - if err := InstallTypes(fakeClientset.Extensions().ThirdPartyResources()); err == nil { - t.Fatal("InstallTypes was supposed to error but didn't") - } - - for _, action := range getCallActions { - name := action.GetName() - if name == serviceBrokerTPR.Name || name == serviceInstanceTPR.Name { - t.Errorf("Failed to skip polling for resource that failed to install: %q", action) - } - } -} diff --git a/pkg/storage/tpr/keyer.go b/pkg/storage/tpr/keyer.go deleted file mode 100644 index 867e7b61540..00000000000 --- a/pkg/storage/tpr/keyer.go +++ /dev/null @@ -1,91 +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 tpr - -import ( - "errors" - "fmt" - "strings" - - "k8s.io/apiserver/pkg/endpoints/request" -) - -var ( - errEmptyKey = errors.New("empty key") -) - -type errInvalidKey struct { - k string -} - -func (e errInvalidKey) Error() string { - return fmt.Sprintf("invalid key '%s'", e.k) -} - -// Keyer is a containing struct for -type Keyer struct { - DefaultNamespace string - ResourceName string - Separator string -} - -// KeyRoot is a (k8s.io/kubernetes/pkg/registry/generic/registry).Store compatible function to -// get the key root to be passed to a third party resource based storage Interface. -// -// It is meant to be passed to a Store's KeyRootFunc field, so that a TPR based storage interface -// can parse the namespace and name from fields that it is later given. -// -// The returned string will never be empty is k.DefaultNamespace is not empty -func (k Keyer) KeyRoot(ctx request.Context) string { - ns, ok := request.NamespaceFrom(ctx) - if ok && len(ns) > 0 { - return ns - } - return k.DefaultNamespace -} - -// Key is a (k8s.io/kubernetes/pkg/registry/generic/registry).Store compatible function to -// get the key to be passed to a third party resource based storage Interface. -// -// It is meant to be passed to a Store's KeyRoot field, so that a TPR based storage interface -// can parse the namespace and name from fields that it is later given -func (k Keyer) Key(ctx request.Context, name string) (string, error) { - root := k.KeyRoot(ctx) - return strings.Join([]string{root, name}, k.Separator), nil -} - -// NamespaceAndNameFromKey returns the namespace (or an empty string if there wasn't one) and -// name for a given etcd-style key. This function is intended to be used in a TPR based -// storage.Interface that uses k.KeyRoot and k.Key to construct its etcd keys. -// -// The first return value is the namespace, and may be empty if the key represents a -// namespace-less resource. The second return value is the name and will not be empty if the -// error is nil. The error will be non-nil if the key was malformed, in which case all other -// return values will be empty strings. -func (k Keyer) NamespaceAndNameFromKey(key string) (string, string, error) { - spl := strings.Split(key, k.Separator) - splLen := len(spl) - if splLen == 1 { - // single slice entry is name-less, so return an empty name - return spl[0], "", nil - } else if splLen == 2 { - // two slice entries has a namespace - return spl[0], spl[1], nil - } - - return "", "", errInvalidKey{k: key} -} diff --git a/pkg/storage/tpr/keyer_test.go b/pkg/storage/tpr/keyer_test.go deleted file mode 100644 index 7ddf33bbd67..00000000000 --- a/pkg/storage/tpr/keyer_test.go +++ /dev/null @@ -1,100 +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 tpr - -import ( - "testing" - - "k8s.io/apiserver/pkg/endpoints/request" -) - -const ( - defaultCtxNS = "defaultTestNS" - ctxNS = "testNS" - separator = "/" - resourceName = "myResource" -) - -func TestKeyRoot(t *testing.T) { - ctx := request.NewContext() - ctx = request.WithNamespace(ctx, ctxNS) - keyer := Keyer{DefaultNamespace: defaultCtxNS} - root := keyer.KeyRoot(ctx) - if root != ctxNS { - t.Fatalf("key root '%s' wasn't expected '%s'", root, ctxNS) - } - ctx = request.NewContext() - root = keyer.KeyRoot(ctx) - if root != keyer.DefaultNamespace { - t.Fatalf("key root '%s' wasn't expected '%s'", root, keyer.DefaultNamespace) - } -} - -func TestKey(t *testing.T) { - ctx := request.NewContext() - ctx = request.WithNamespace(ctx, ctxNS) - keyer := Keyer{Separator: separator, ResourceName: resourceName} - key, err := keyer.Key(ctx, resourceName) - if err != nil { - t.Fatalf("key func returned an error %s", err) - } - expected := ctxNS + separator + resourceName - if key != expected { - t.Fatalf("key was '%s', not expected '%s', key, expected", key, expected) - } -} - -func TestNamespaceAndNameFromKey(t *testing.T) { - const testName = "testName" - keyer := Keyer{Separator: separator, ResourceName: resourceName} - key := ctxNS + separator + testName - ns, name, err := keyer.NamespaceAndNameFromKey(key) - if err != nil { - t.Fatalf("unexpected error %s", err) - } - if ns != ctxNS { - t.Fatalf("namespace was '%s', not expected '%s'", ns, ctxNS) - } - if name != testName { - t.Fatalf("name was '%s', not expected '%s'", name, testName) - } - - key = ctxNS - ns, name, err = keyer.NamespaceAndNameFromKey(key) - if err != nil { - t.Fatalf("unexpected error %s", err) - } - if ns != ctxNS { - t.Fatalf("namespace was '%s', not expected '%s'", ns, ctxNS) - } - if name != "" { - t.Fatalf("expected empty name, got '%s'", name) - } - - key = "a" + separator + "b" + separator + "c" - ns, name, err = keyer.NamespaceAndNameFromKey(key) - if err == nil { - t.Fatalf("expected error") - } - if ns != "" { - t.Fatalf("expected empty namespace") - } - if name != "" { - t.Fatalf("expected empty name") - } - -} diff --git a/pkg/storage/tpr/kinds.go b/pkg/storage/tpr/kinds.go deleted file mode 100644 index c4fe355915c..00000000000 --- a/pkg/storage/tpr/kinds.go +++ /dev/null @@ -1,110 +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 tpr - -import ( - "fmt" - "strings" - "unicode" -) - -const ( - groupName = "servicecatalog.k8s.io" -) - -func withGroupName(name string) string { - return fmt.Sprintf("%s.%s", name, groupName) -} - -// Kind represents the kind of a third party resource. This type implements fmt.Stringer -type Kind string - -// String is the fmt.Stringer interface implementation -func (k Kind) String() string { - return string(k) -} - -// TPRName returns the lowercase name, suitable for creating third party resources of this kind -func (k Kind) TPRName() string { - // this code taken from code under - // https://github.com/kubernetes/community/blob/master/contributors/design-proposals/extending-api.md#expectations-about-third-party-objects - var result string - for ix := range k.String() { - current := rune(k.String()[ix]) - if unicode.IsUpper(current) && ix > 0 { - result = result + "-" - } - result = result + string(unicode.ToLower(current)) - } - return result -} - -// URLName returns the URL-worthy name this TPR kind. Examples: -// -// Kind("ServiceBroker").URLName() == "servicebrokers" -// Kind("ServiceClass").URLName() == "serviceclasses" -// Kind("ServiceInstance").URLName() == "serviceinstances" -// Kind("ServiceInstanceCredntial").URLName() == "serviceinstancecredentials" -// -// Note that this function is incomplete - it is only guaranteed to properly pluralize our 4 -// resource types ("ServiceBroker", "ServiceClass", "ServiceInstance", "ServiceInstanceCredential") -func (k Kind) URLName() string { - str := k.String() - strLen := len(str) - lastChar := str[strLen-1] - var ret string - if lastChar == 's' { - ret = str + "es" - } else { - ret = str + "s" - } - return strings.ToLower(ret) -} - -const ( - // ServiceBrokerKind is the name of a Service Broker resource, a Kubernetes third party resource. - ServiceBrokerKind Kind = "ServiceBroker" - - // ServiceBrokerListKind is the name of a list of Service Broker resources - ServiceBrokerListKind Kind = "ServiceBrokerList" - - // ServiceInstanceCredentialKind is the name of a Service Instance - // Credential resource, a Kubernetes third party resource. - ServiceInstanceCredentialKind Kind = "ServiceInstanceCredential" - - // ServiceInstanceCredentialListKind is the name for lists of Service - // Instance Credentials - ServiceInstanceCredentialListKind Kind = "ServiceInstanceCredentialList" - - // ServiceClassKind is the name of a Service Class resource, a Kubernetes third party resource. - ServiceClassKind Kind = "ServiceClass" - - // ServiceClassListKind is the name of a list of service class resources - ServiceClassListKind Kind = "ServiceClassList" - - // ServicePlanKind is the name of a Service Plan resource, a Kubernetes third party resource. - ServicePlanKind Kind = "ServicePlan" - - // ServicePlanListKind is the name of a list of service plan resources - ServicePlanListKind Kind = "ServicePlanList" - - // ServiceInstanceKind is the name of a Service Instance resource, a Kubernetes third party resource. - ServiceInstanceKind Kind = "ServiceInstance" - - // ServiceInstanceListKind is the name of a list of service instance resources - ServiceInstanceListKind Kind = "ServiceInstanceList" -) diff --git a/pkg/storage/tpr/kinds_test.go b/pkg/storage/tpr/kinds_test.go deleted file mode 100644 index 39ff6d4839e..00000000000 --- a/pkg/storage/tpr/kinds_test.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package tpr - -import ( - "testing" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestTPRName(t *testing.T) { - testCases := []struct { - before string - after string - }{ - {before: "ServiceClass", after: "service-class"}, - {before: "ThisIsAThing", after: "this-is-a-thing"}, - {before: "thisIsAThing", after: "this-is-a-thing"}, - {before: "ServiceInstanceCredential", after: "service-instance-credential"}, - {before: "ThisIsAAThing", after: "this-is-a-a-thing"}, - } - for _, testCase := range testCases { - kind := Kind(testCase.before) - if kind.TPRName() != testCase.after { - t.Errorf("expected %s, got %s", testCase.after, kind.TPRName()) - } - } -} - -func TestURLName(t *testing.T) { - testCases := []struct { - before string - after string - }{ - {before: "ServiceClass", after: "serviceclasses"}, - {before: "ThisIsAThing", after: "thisisathings"}, - {before: "thisIsAThing", after: "thisisathings"}, - {before: "ServiceInstanceCredential", after: "serviceinstancecredentials"}, - } - - for _, testCase := range testCases { - kind := Kind(testCase.before) - if kind.URLName() != testCase.after { - t.Errorf("expected %s, got %s", testCase.after, kind.URLName()) - } - } -} - -func newTypeMeta(kind Kind) metav1.TypeMeta { - return metav1.TypeMeta{Kind: kind.TPRName(), APIVersion: groupName + "/v1alpha1'"} -} diff --git a/pkg/storage/tpr/list_resources.go b/pkg/storage/tpr/list_resources.go deleted file mode 100644 index d223f5154db..00000000000 --- a/pkg/storage/tpr/list_resources.go +++ /dev/null @@ -1,76 +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 tpr - -import ( - "github.com/golang/glog" - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/runtime" - apiv1 "k8s.io/client-go/pkg/api/v1" - restclient "k8s.io/client-go/rest" -) - -// listResource uses cl to get resources of the given kind from the given namespace, and decodes -// the resources into listObj. -func listResource( - cl restclient.Interface, - ns string, - kind Kind, - listObj runtime.Object, - codec runtime.Codec, -) ([]runtime.Object, error) { - req := cl.Get().AbsPath( - "apis", - groupName, - tprVersion, - "namespaces", - ns, - kind.URLName(), - ) - - var unknown runtime.Unknown - if err := req.Do().Into(&unknown); err != nil { - glog.Errorf("doing request (%s)", err) - return nil, err - } - - if err := decode(codec, unknown.Raw, listObj); err != nil { - return nil, err - } - objs, err := meta.ExtractList(listObj) - if err != nil { - glog.Errorf("extracting list items from the list object (%s)", err) - return nil, err - } - return objs, nil -} - -// getAllNamespaces uses cl to get all namespaces -func getAllNamespaces(cl restclient.Interface) (*apiv1.NamespaceList, error) { - req := cl.Get().AbsPath("api", "v1", "namespaces") - var nsList apiv1.NamespaceList - if err := req.Do().Into(&nsList); err != nil { - glog.Errorf("getting all namespaces (%s)", err) - return nil, err - } - return &nsList, nil -} - -// stripNamespacesFromList removes the namespaces from each object in the list represented by obj -func stripNamespacesFromList(obj runtime.Object) error { - return meta.EachListItem(obj, removeNamespace) -} diff --git a/pkg/storage/tpr/list_resources_test.go b/pkg/storage/tpr/list_resources_test.go deleted file mode 100644 index 2431463128c..00000000000 --- a/pkg/storage/tpr/list_resources_test.go +++ /dev/null @@ -1,124 +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 tpr - -import ( - "testing" - - sc "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog" - _ "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/install" - "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/testapi" - "github.com/kubernetes-incubator/service-catalog/pkg/rest/core/fake" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -func TestStripNamespacesFromList(t *testing.T) { - lst := sc.ServiceBrokerList{ - Items: []sc.ServiceBroker{ - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "testns1", - Name: "test1", - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "testns2", - Name: "test2", - }, - }, - }, - } - if err := stripNamespacesFromList(&lst); err != nil { - t.Fatalf("removing namespaces from list (%s)", err) - } - for i, item := range lst.Items { - if item.Namespace != "" { - t.Errorf("item %d has a non-empty namespace %s", i, item.Namespace) - } - } -} - -func TestGetAllNamespaces(t *testing.T) { - const ( - ns1Name = "ns1" - ) - cl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceBroker{} - }) - nsList, err := getAllNamespaces(cl) - if err != nil { - t.Fatalf("getting all namespaces (%s)", err) - } - if len(nsList.Items) != 0 { - t.Fatalf("expected 0 namespaces, got %d", len(nsList.Items)) - } - cl.Storage[ns1Name] = fake.NewTypedStorage() - nsList, err = getAllNamespaces(cl) - if err != nil { - t.Fatalf("getting all namespaces (%s)", err) - } - if len(nsList.Items) != 1 { - t.Fatalf("expected 1 namespace, got %d", len(nsList.Items)) - } - if nsList.Items[0].Name != ns1Name { - t.Fatalf("expected namespace with name %s, got %s instead", ns1Name, nsList.Items[0].Name) - } -} - -func TestListResource(t *testing.T) { - const ( - ns = "testns" - kind = ServiceBrokerKind - ) - - cl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceBroker{} - }) - listObj := sc.ServiceBrokerList{TypeMeta: newTypeMeta(kind)} - codec, err := testapi.GetCodecForObject(&sc.ServiceBrokerList{TypeMeta: newTypeMeta(kind)}) - if err != nil { - t.Fatalf("error getting codec (%s)", err) - } - objs, err := listResource(cl, ns, kind, &listObj, codec) - if err != nil { - t.Fatalf("error listing resource (%s)", err) - } - if len(objs) != 0 { - t.Fatalf("expected 0 objects returned, got %d instead", len(objs)) - } - cl.Storage.Set(ns, ServiceBrokerKind.URLName(), "broker1", &sc.ServiceBroker{ - TypeMeta: newTypeMeta(kind), - ObjectMeta: metav1.ObjectMeta{Name: "broker1"}, - }) - cl.Storage.Set(ns, ServiceBrokerKind.URLName(), "broker2", &sc.ServiceBroker{ - TypeMeta: newTypeMeta(kind), - ObjectMeta: metav1.ObjectMeta{Name: "broker2"}, - }) - objs, err = listResource(cl, ns, kind, &listObj, codec) - if err != nil { - t.Fatalf("error listing resource (%s)", err) - } - if len(objs) != len(cl.Storage[ns][ServiceBrokerKind.URLName()]) { - t.Fatalf( - "expected %d objects returned, got %d instead", - len(cl.Storage[ns][ServiceBrokerKind.URLName()]), - len(objs), - ) - } -} diff --git a/pkg/storage/tpr/obj_state.go b/pkg/storage/tpr/obj_state.go deleted file mode 100644 index 1015a64283c..00000000000 --- a/pkg/storage/tpr/obj_state.go +++ /dev/null @@ -1,66 +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 tpr - -import ( - "errors" - "fmt" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apiserver/pkg/storage" -) - -var ( - errResourceVersionCannotBeSet = errors.New("resource version cannot be set on objects") -) - -type errCouldntGetResourceVersion struct { - err error -} - -func (e errCouldntGetResourceVersion) Error() string { - return fmt.Sprintf("couldn't get resource version (%s)", e.err) -} - -type objState struct { - obj runtime.Object - meta *storage.ResponseMeta - rev int64 - data []byte -} - -func (t *store) getStateFromObject(obj runtime.Object) (*objState, error) { - versioner := t.Versioner() - state := &objState{ - obj: obj, - meta: &storage.ResponseMeta{}, - } - - rv, err := versioner.ObjectResourceVersion(obj) - if err != nil { - return nil, errCouldntGetResourceVersion{err: err} - } - state.rev = int64(rv) - state.meta.ResourceVersion = uint64(state.rev) - - data, err := runtime.Encode(t.codec, obj) - if err != nil { - return nil, err - } - state.data = data - return state, nil -} diff --git a/pkg/storage/tpr/options.go b/pkg/storage/tpr/options.go deleted file mode 100644 index 57fa394da04..00000000000 --- a/pkg/storage/tpr/options.go +++ /dev/null @@ -1,43 +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 tpr - -import ( - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apiserver/pkg/registry/generic" - restclient "k8s.io/client-go/rest" -) - -// Options is the set of options to create a new TPR storage interface -type Options struct { - HasNamespace bool - RESTOptions generic.RESTOptions - DefaultNamespace string - RESTClient restclient.Interface - SingularKind Kind - // NewSingularFunc is a function that returns a new object of the appropriate type, - // with the namespace (first param) and name (second param) pre-filled - NewSingularFunc func(string, string) runtime.Object - ListKind Kind - // NewListFunc is a function that returns a new, empty list object of the appropriate - // type. The list object should hold elements that are returned by NewSingularFunc - NewListFunc func() runtime.Object - CheckObjectFunc func(runtime.Object) error - DestroyFunc func() - Keyer Keyer - HardDelete bool -} diff --git a/pkg/storage/tpr/put.go b/pkg/storage/tpr/put.go deleted file mode 100644 index 9d5fd50a2af..00000000000 --- a/pkg/storage/tpr/put.go +++ /dev/null @@ -1,71 +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 tpr - -import ( - "fmt" - "net/http" - - "github.com/golang/glog" - "k8s.io/apimachinery/pkg/runtime" - restclient "k8s.io/client-go/rest" -) - -func put( - cl restclient.Interface, - codec runtime.Codec, - kind Kind, - ns, - name string, - data []byte, - out runtime.Object, -) error { - putReq := cl.Put().AbsPath( - "apis", - groupName, - tprVersion, - "namespaces", - ns, - kind.URLName(), - name, - ).Body(data) - putRes := putReq.Do() - if putRes.Error() != nil { - glog.Errorf("executing PUT to %s/%s (%s)", ns, name, putRes.Error()) - return putRes.Error() - } - var statusCode int - putRes.StatusCode(&statusCode) - if statusCode != http.StatusOK { - return fmt.Errorf( - "executing PUT for %s/%s, received response code %d", - ns, - name, - statusCode, - ) - } - var putUnknown runtime.Unknown - if err := putRes.Into(&putUnknown); err != nil { - glog.Errorf("reading response (%s)", err) - return err - } - if err := decode(codec, putUnknown.Raw, out); err != nil { - glog.Errorf("decoding response (%s)", err) - return err - } - return nil -} diff --git a/pkg/storage/tpr/resources.go b/pkg/storage/tpr/resources.go deleted file mode 100644 index 41c72c426a5..00000000000 --- a/pkg/storage/tpr/resources.go +++ /dev/null @@ -1,137 +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 tpr - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/pkg/apis/extensions/v1beta1" -) - -const ( - tprKind = "ThirdPartyResource" - tprVersion = "v1alpha1" -) - -// ServiceInstanceResource represents the API resource for the service instance third -// party resource -var ServiceInstanceResource = metav1.APIResource{ - Name: ServiceInstanceKind.TPRName(), - Namespaced: true, -} - -// ServiceInstanceCredentialResource represents the API resource for the service -// instance credential third party resource -var ServiceInstanceCredentialResource = metav1.APIResource{ - Name: ServiceInstanceCredentialKind.TPRName(), - Namespaced: true, -} - -// ServiceBrokerResource represents the API resource for the service broker -// third party resource -var ServiceBrokerResource = metav1.APIResource{ - Name: ServiceBrokerKind.TPRName(), - Namespaced: true, -} - -// ServiceClassResource represents the API resource for the service class -// third party resource -var ServiceClassResource = metav1.APIResource{ - Name: ServiceClassKind.TPRName(), - Namespaced: true, -} - -// ServicePlanResource represents the API resource for the service plan -// third party resource -var ServicePlanResource = metav1.APIResource{ - Name: ServicePlanKind.TPRName(), - Namespaced: true, -} - -// ServiceInstanceResource represents the API resource for the service instance third -// party resource -var serviceInstanceTPR = v1beta1.ThirdPartyResource{ - TypeMeta: metav1.TypeMeta{ - Kind: tprKind, - APIVersion: tprVersion, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: withGroupName(ServiceInstanceKind.TPRName()), - }, - Versions: []v1beta1.APIVersion{ - {Name: tprVersion}, - }, -} - -// ServiceInstanceCredentialResource represents the API resource for the service binding third -// party resource -var serviceInstanceCredentialTPR = v1beta1.ThirdPartyResource{ - TypeMeta: metav1.TypeMeta{ - Kind: tprKind, - APIVersion: tprVersion, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: withGroupName(ServiceInstanceCredentialKind.TPRName()), - }, - Versions: []v1beta1.APIVersion{ - {Name: tprVersion}, - }, -} - -// ServiceBrokerResource represents the API resource for the service broker third -// party resource -var serviceBrokerTPR = v1beta1.ThirdPartyResource{ - TypeMeta: metav1.TypeMeta{ - Kind: tprKind, - APIVersion: tprVersion, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: withGroupName(ServiceBrokerKind.TPRName()), - }, - Versions: []v1beta1.APIVersion{ - {Name: tprVersion}, - }, -} - -// ServiceClassResource represents the API resource for the service class third -// party resource -var serviceClassTPR = v1beta1.ThirdPartyResource{ - TypeMeta: metav1.TypeMeta{ - Kind: tprKind, - APIVersion: tprVersion, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: withGroupName(ServiceClassKind.TPRName()), - }, - Versions: []v1beta1.APIVersion{ - {Name: tprVersion}, - }, -} - -// ServicePlanResource represents the API resource for the service plan third -// party resource -var servicePlanTPR = v1beta1.ThirdPartyResource{ - TypeMeta: metav1.TypeMeta{ - Kind: tprKind, - APIVersion: tprVersion, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: withGroupName(ServicePlanKind.TPRName()), - }, - Versions: []v1beta1.APIVersion{ - {Name: tprVersion}, - }, -} diff --git a/pkg/storage/tpr/resources_test.go b/pkg/storage/tpr/resources_test.go deleted file mode 100644 index 79fda38bac9..00000000000 --- a/pkg/storage/tpr/resources_test.go +++ /dev/null @@ -1,96 +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 tpr - -import ( - "reflect" - "testing" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/pkg/apis/extensions/v1beta1" -) - -//make sure each of Third Party Resource kinds are built with the correct structure -func TestTPRKinds(t *testing.T) { - var serviceInstanceSample = v1beta1.ThirdPartyResource{ - TypeMeta: metav1.TypeMeta{ - Kind: "ThirdPartyResource", - APIVersion: "v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: withGroupName("service-instance"), - }, - Versions: []v1beta1.APIVersion{ - {Name: "v1alpha1"}, - }, - } - - var serviceBrokerSample = v1beta1.ThirdPartyResource{ - TypeMeta: metav1.TypeMeta{ - Kind: "ThirdPartyResource", - APIVersion: "v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: withGroupName("service-broker"), - }, - Versions: []v1beta1.APIVersion{ - {Name: "v1alpha1"}, - }, - } - - var serviceClassSample = v1beta1.ThirdPartyResource{ - TypeMeta: metav1.TypeMeta{ - Kind: "ThirdPartyResource", - APIVersion: "v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: withGroupName("service-class"), - }, - Versions: []v1beta1.APIVersion{ - {Name: "v1alpha1"}, - }, - } - - var serviceInstanceCredentialSample = v1beta1.ThirdPartyResource{ - TypeMeta: metav1.TypeMeta{ - Kind: "ThirdPartyResource", - APIVersion: "v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: withGroupName("service-instance-credential"), - }, - Versions: []v1beta1.APIVersion{ - {Name: "v1alpha1"}, - }, - } - - if !reflect.DeepEqual(serviceInstanceSample, serviceInstanceTPR) { - t.Errorf("Unexpected ServiceInstance TPR structure") - } - - if !reflect.DeepEqual(serviceInstanceCredentialSample, serviceInstanceCredentialTPR) { - t.Errorf("Unexpected ServiceBroker TPR structure") - } - - if !reflect.DeepEqual(serviceBrokerSample, serviceBrokerTPR) { - t.Errorf("Unexpected ServiceInstanceCredential TPR structure") - } - - if !reflect.DeepEqual(serviceClassSample, serviceClassTPR) { - t.Errorf("Unexpected Service Class TPR structure") - } -} diff --git a/pkg/storage/tpr/storage_interface.go b/pkg/storage/tpr/storage_interface.go deleted file mode 100644 index 150b6620d29..00000000000 --- a/pkg/storage/tpr/storage_interface.go +++ /dev/null @@ -1,674 +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 tpr - -import ( - "bytes" - "errors" - "fmt" - "net/http" - - "github.com/golang/glog" - scmeta "github.com/kubernetes-incubator/service-catalog/pkg/api/meta" - "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1alpha1" - "golang.org/x/net/context" - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/conversion" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/apiserver/pkg/storage" - "k8s.io/apiserver/pkg/storage/etcd" - "k8s.io/apiserver/pkg/storage/storagebackend/factory" - restclient "k8s.io/client-go/rest" -) - -var ( - errNotImplemented = errors.New("not implemented for third party resources") -) - -type store struct { - hasNamespace bool - codec runtime.Codec - defaultNamespace string - cl restclient.Interface - singularKind Kind - // singularShell is a function that returns a new object of the appropriate type, - // with the namespace (first param) and name (second param) pre-filled - singularShell func(string, string) runtime.Object - listKind Kind - // listShell is a function that returns a new, empty list object of the appropriate - // type. The list object should hold elements that are returned by singularShell - listShell func() runtime.Object - checkObject func(runtime.Object) error - decodeKey func(string) (string, string, error) - versioner storage.Versioner - hardDelete bool -} - -// NewStorage creates a new TPR-based storage.Interface implementation -func NewStorage(opts Options) (storage.Interface, factory.DestroyFunc) { - return &store{ - hasNamespace: opts.HasNamespace, - codec: opts.RESTOptions.StorageConfig.Codec, - defaultNamespace: opts.DefaultNamespace, - cl: opts.RESTClient, - singularKind: opts.SingularKind, - singularShell: opts.NewSingularFunc, - listKind: opts.ListKind, - listShell: opts.NewListFunc, - checkObject: opts.CheckObjectFunc, - decodeKey: opts.Keyer.NamespaceAndNameFromKey, - versioner: etcd.APIObjectVersioner{}, - hardDelete: opts.HardDelete, - }, opts.DestroyFunc -} - -// Versioned returns the versioned associated with this interface -func (t *store) Versioner() storage.Versioner { - return t.versioner -} - -// Create adds a new object at a key unless it already exists. 'ttl' is time-to-live -// in seconds (0 means forever). If no error is returned and out is not nil, out will be -// set to the read value from database. -func (t *store) Create( - ctx context.Context, - key string, - obj, - out runtime.Object, - ttl uint64, -) error { - - ns, name, err := t.decodeKey(key) - if err != nil { - glog.Errorf("decoding key %s (%s)", key, err) - return err - } - - if err := scmeta.AddFinalizer(obj, v1alpha1.FinalizerServiceCatalog); err != nil { - glog.Errorf("adding finalizer to %s (%s)", key, err) - return err - } - data, err := runtime.Encode(t.codec, obj) - if err != nil { - return err - } - req := t.cl.Post().AbsPath( - "apis", - groupName, - tprVersion, - "namespaces", - ns, - t.singularKind.URLName(), - ).Body(data) - - res := req.Do() - if res.Error() != nil { - errStr := fmt.Sprintf("executing POST for %s/%s (%s)", ns, name, res.Error()) - glog.Errorf(errStr) - // Don't return an error here so that, in case there was a 409 (conflict), we go and - // return the key exists error - } - var statusCode int - res.StatusCode(&statusCode) - if statusCode == http.StatusConflict { - return storage.NewKeyExistsError(key, 0) - } - if statusCode != http.StatusCreated { - errStr := fmt.Sprintf( - "executing POST for %s/%s, received response code %d", - ns, - name, - statusCode, - ) - glog.Errorf(errStr) - return errors.New(errStr) - } - - var unknown runtime.Unknown - if err := res.Into(&unknown); err != nil { - glog.Errorf("decoding response (%s)", err) - return err - } - - return decode(t.codec, unknown.Raw, out) -} - -// Delete fetches the resource at key, removes its finalizer, updates it, and returns the -// resource before its finalizer was removed. -// -// If key didn't exist, it will return NotFound storage error. -func (t *store) Delete( - ctx context.Context, - key string, - out runtime.Object, - preconditions *storage.Preconditions, -) error { - // create adds the get the object remove its finalizer, and - ns, name, err := t.decodeKey(key) - if err != nil { - glog.Errorf("decoding key %s (%s)", key, err) - return err - } - if t.hardDelete { - // if we are hard-deleting this item, then propagate this delete to the core API server. - // after the core API server gets the DELETE call, it will set the deletion timestamp - // as we expect, so we should proceed to remove the deletion timestamp & update as usual - // (below), so that the object is removed completely - if err := delete(t.cl, t.singularKind, key, ns, name, http.StatusOK); err != nil { - glog.Errorf("hard-deleting %s (%s)", key, err) - return err - } - } - if err := get( - t.cl, - t.codec, - t.singularKind, - key, - ns, - name, - out, - t.hasNamespace, - false, - ); err != nil { - glog.Errorf("getting %s (%s)", key, err) - return err - } - - if _, err := scmeta.RemoveFinalizer(out, v1alpha1.FinalizerServiceCatalog); err != nil { - glog.Errorf("removing finalizer from %#v (%s)", out, err) - return err - } - encoded, err := runtime.Encode(t.codec, out) - if err != nil { - glog.Errorf("encoding %#v (%s)", out, err) - return err - } - if err := put(t.cl, t.codec, t.singularKind, ns, name, encoded, out); err != nil { - glog.Errorf("putting %s (%s)", key, err) - return err - } - return nil -} - -// Watch begins watching the specified key. Events are decoded into API objects, -// and any items selected by 'p' are sent down to returned watch.Interface. -// resourceVersion may be used to specify what version to begin watching, -// which should be the current resourceVersion, and no longer rv+1 -// (e.g. reconnecting without missing any updates). -// If resource version is "0", this interface will get current object at given key -// and send it in an "ADDED" event, before watch starts. -func (t *store) Watch( - ctx context.Context, - key string, - resourceVersion string, - p storage.SelectionPredicate, -) (watch.Interface, error) { - ns, name, err := t.decodeKey(key) - if err != nil { - return nil, err - } - - req := t.cl.Get().AbsPath( - "apis", - groupName, - tprVersion, - "watch", - "namespaces", - ns, - t.singularKind.URLName(), - name, - ).Param("resourceVersion", resourceVersion) - watchIface, err := req.Watch() - if err != nil { - glog.Errorf("initiating the raw watch (%s)", err) - return nil, err - } - filteredIFace := watch.Filter(watchIface, watchFilterer(t, ns, false)) - return filteredIFace, nil -} - -// watchFilterer returns a function that can be used as an argument to watch.Filter -func watchFilterer(t *store, ns string, list bool) func(watch.Event) (watch.Event, bool) { - return func(in watch.Event) (watch.Event, bool) { - encodedBytes, err := runtime.Encode(t.codec, in.Object) - if err != nil { - glog.Errorf("couldn't encode watch event object (%s)", err) - return watch.Event{}, false - } - if list { - // if we're watching a list, extract to a list object - finalObj := t.listShell() - if err := decode(t.codec, encodedBytes, finalObj); err != nil { - glog.Errorf("couldn't decode watch event bytes (%s)", err) - return watch.Event{}, false - } - if !t.hasNamespace { - // if we're watching a list and not supposed to have a namespace, strip namespaces - objs, err := meta.ExtractList(finalObj) - if err != nil { - glog.Errorf("couldn't extract a list from %#v (%s)", finalObj, err) - return watch.Event{}, false - } - objList := make([]runtime.Object, len(objs)) - for i, obj := range objs { - if err := removeNamespace(obj); err != nil { - glog.Errorf("couldn't remove namespace from %#v (%s)", obj, err) - return watch.Event{}, false - } - objList[i] = obj - } - if err := meta.SetList(finalObj, objList); err != nil { - glog.Errorf("setting list items (%s)", err) - return watch.Event{}, false - } - return watch.Event{ - Type: in.Type, - Object: finalObj, - }, true - } - return watch.Event{ - Type: in.Type, - Object: finalObj, - }, true - } - finalObj := t.singularShell("", "") - if err := decode(t.codec, encodedBytes, finalObj); err != nil { - glog.Errorf("couldn't decode watch event bytes (%s)", err) - return watch.Event{}, false - } - if !t.hasNamespace { - if err := removeNamespace(finalObj); err != nil { - glog.Errorf("couldn't remove namespace from %#v (%s)", finalObj, err) - return watch.Event{}, false - } - } - return watch.Event{ - Type: in.Type, - Object: finalObj, - }, true - } - -} - -// WatchList begins watching the specified key's items. Items are decoded into API -// objects and any item selected by 'p' are sent down to returned watch.Interface. -// resourceVersion may be used to specify what version to begin watching, -// which should be the current resourceVersion, and no longer rv+1 -// (e.g. reconnecting without missing any updates). -// If resource version is "0", this interface will list current objects directory defined by key -// and send them in "ADDED" events, before watch starts. -func (t *store) WatchList( - ctx context.Context, - key string, - resourceVersion string, - p storage.SelectionPredicate, -) (watch.Interface, error) { - ns, _, err := t.decodeKey(key) - if err != nil { - return nil, err - } - - req := t.cl.Get().AbsPath( - "apis", - groupName, - tprVersion, - "watch", - "namespaces", - ns, - t.singularKind.URLName(), - ).Param("resourceVersion", resourceVersion) - - watchIface, err := req.Watch() - if err != nil { - glog.Errorf("initiating the raw watch (%s)", err) - return nil, err - } - return watch.Filter(watchIface, watchFilterer(t, ns, true)), nil -} - -// Get unmarshals json found at key into objPtr. On a not found error, will either -// return a zero object of the requested type, or an error, depending on ignoreNotFound. -// Treats empty responses and nil response nodes exactly like a not found error. -// The returned contents may be delayed, but it is guaranteed that they will -// be have at least 'resourceVersion'. -func (t *store) Get( - ctx context.Context, - key string, - resourceVersion string, - objPtr runtime.Object, - ignoreNotFound bool, -) error { - ns, name, err := t.decodeKey(key) - if err != nil { - glog.Errorf("decoding key %s (%s)", key, err) - return err - } - req := t.cl.Get().AbsPath( - "apis", - groupName, - tprVersion, - "namespaces", - ns, - t.singularKind.URLName(), - name, - ) - - res := req.Do() - if res.Error() != nil { - glog.Errorf("executing GET for %s/%s (%s)", ns, name, res.Error()) - } - var statusCode int - res.StatusCode(&statusCode) - if statusCode == http.StatusNotFound { - if ignoreNotFound { - return runtime.SetZeroValue(objPtr) - } - glog.Errorf("executing GET for %s/%s: not found", ns, name) - return storage.NewKeyNotFoundError(key, 0) - } - if statusCode != http.StatusOK { - return fmt.Errorf( - "executing GET for %s/%s, received response code %d", - ns, - name, - statusCode, - ) - } - - var unknown runtime.Unknown - if res.Into(&unknown); err != nil { - glog.Errorf("decoding response (%s)", err) - return err - } - - if err := decode(t.codec, unknown.Raw, objPtr); err != nil { - return nil - } - if !t.hasNamespace { - if err := removeNamespace(objPtr); err != nil { - glog.Errorf("removing namespace from %#v (%s)", objPtr, err) - return err - } - } - return nil -} - -// GetToList unmarshals json found at key and opaque it into *List api object -// (an object that satisfies the runtime.IsList definition). -// The returned contents may be delayed, but it is guaranteed that they will -// be have at least 'resourceVersion'. -func (t *store) GetToList( - ctx context.Context, - key string, - resourceVersion string, - p storage.SelectionPredicate, - listObj runtime.Object, -) error { - return t.List(ctx, key, resourceVersion, p, listObj) -} - -// List unmarshalls jsons found at directory defined by key and opaque them -// into *List api object (an object that satisfies runtime.IsList definition). -// The returned contents may be delayed, but it is guaranteed that they will -// be have at least 'resourceVersion'. -func (t *store) List( - ctx context.Context, - key string, - resourceVersion string, - p storage.SelectionPredicate, - listObj runtime.Object, -) error { - ns, _, err := t.decodeKey(key) - if err != nil { - glog.Errorf("decoding %s (%s)", key, err) - return err - } - - if t.hasNamespace && ns == t.defaultNamespace { - // if the resource is supposed to have a namespace, and the given one is the default, - // then assume that '--all-namespaces' was given on the kubectl command line. - // this assumption means that a kubectl command that specifies a namespace equal to - // the default namespace (i.e. '-n default-ns'), we will still list all resources. - // - // to list all resources, we get all namespaces, list all resources in each namespace, - // and then collect all resources into the single listObj - allNamespaces, err := getAllNamespaces(t.cl) - if err != nil { - glog.Errorf("listing all namespaces (%s)", err) - return err - } - var objList []runtime.Object - for _, ns := range allNamespaces.Items { - allObjs, err := listResource(t.cl, ns.Name, t.singularKind, listObj, t.codec) - if err != nil { - glog.Errorf("error listing resources (%s)", err) - return err - } - objList = append(objList, allObjs...) - } - if err := meta.SetList(listObj, objList); err != nil { - glog.Errorf("setting list items (%s)", err) - return err - } - return nil - } - - // otherwise, list all the resources in the given namespace. if the resource is not supposed - // to be namespaced, then ns will be the default namespace - objs, err := listResource(t.cl, ns, t.singularKind, listObj, t.codec) - if err != nil { - glog.Errorf("listing resources (%s)", err) - return err - } - if !t.hasNamespace { - for i, obj := range objs { - if err := removeNamespace(obj); err != nil { - glog.Errorf("removing namespace from obj %d (%s)", i, err) - return err - } - } - } - if err := meta.SetList(listObj, objs); err != nil { - glog.Errorf("setting list items (%s)", err) - return err - } - return nil -} - -// GuaranteedUpdate implements storage.Interface.GuaranteedUpdate. -func (t *store) GuaranteedUpdate( - ctx context.Context, - key string, - out runtime.Object, - ignoreNotFound bool, - precondtions *storage.Preconditions, - userUpdate storage.UpdateFunc, - suggestion ...runtime.Object, -) error { - // If a suggestion was passed, use that as the initial object, otherwise - // use Get() to retrieve it - var initObj runtime.Object - if len(suggestion) == 1 && suggestion[0] != nil { - initObj = suggestion[0] - } else { - initObj = t.singularShell("", "") - if err := t.Get(ctx, key, "", initObj, ignoreNotFound); err != nil { - glog.Errorf("getting initial object (%s)", err) - return err - } - } - // In either case, extract current state from the initial object - curState, err := t.getStateFromObject(initObj) - if err != nil { - glog.Errorf("getting state from initial object (%s)", err) - return err - } - // Loop until update succeeds or we get an error - for { - if err := checkPreconditions(key, precondtions, curState.obj); err != nil { - glog.Errorf("checking preconditions (%s)", err) - return err - } - // update the object by applying the userUpdate func & encode it - updated, _, err := userUpdate(curState.obj, *curState.meta) - if err != nil { - glog.Errorf("applying user update: (%s)", err) - return err - } - updatedData, err := runtime.Encode(t.codec, updated) - if err != nil { - glog.Errorf("encoding candidate obj (%s)", err) - return err - } - - // figure out what the new "current state" of the object is for this loop iteration - var newCurState *objState - if bytes.Equal(updatedData, curState.data) { - // If the candidate matches what we already have, then all we need to do is - // decode into the out object - err := decode(t.codec, updatedData, out) - if err != nil { - glog.Errorf("decoding to output object (%s)", err) - } - newCurState = curState - } else { - // If the candidate doesn't match what we already have, then get an up-to-date copy - // of the resource we're trying to update - // (because it may have changed if we're looping and in a race) - newCurObj := t.singularShell("", "") - if err := t.Get(ctx, key, "", newCurObj, ignoreNotFound); err != nil { - glog.Errorf("getting new current object (%s)", err) - return err - } - updatedObj, _, err := userUpdate(newCurObj, *curState.meta) - ncs, err := t.getStateFromObject(updatedObj) - if err != nil { - glog.Errorf("getting state from new current object (%s)", err) - return err - } - newCurState = ncs - } - newCurObjData, err := runtime.Encode(t.codec, newCurState.obj) - if err != nil { - glog.Errorf("encoding new obj (%s)", err) - return err - } - // If the new current revision of the object is the same as the last loop iteration, - // proceed with trying to update the object on the core API server - if newCurState.rev == curState.rev { - ns, name, err := t.decodeKey(key) - if err != nil { - glog.Errorf("decoding key %s (%s)", key, err) - return err - } - newStateDTExists, err := getDeletionInfo(newCurState.obj) - if err != nil { - glog.Errorf("getting deletion info (%s)", err) - return err - } - finalizers, err := scmeta.GetFinalizers(newCurState.obj) - if err != nil { - glog.Errorf("getting finalizers (%s)", err) - return err - } - if newStateDTExists && len(finalizers) > 0 { - // if the deletion timestamp is set but there are still finalizers, then send - // a DELETE to the upstream server. - // The upstream server will do a soft delete and set the deletion timestamp - if err := delete(t.cl, t.singularKind, key, ns, name, http.StatusOK); err != nil { - glog.Errorf("executing DELETE on %s (%s)", key, err) - return err - } - return nil - } - // otherwise, the deletion timestamp and deletion grace period are not set, so - // do the actual update - if err := put(t.cl, t.codec, t.singularKind, ns, name, newCurObjData, out); err != nil { - glog.Errorf("PUTting object %s (%s)", key, err) - return err - } - } else { - glog.V(4).Infof( - "GuaranteedUpdate of %s failed because of a conflict, going to retry", - key, - ) - curState = newCurState - continue - } - return nil - } -} - -func decode( - codec runtime.Codec, - value []byte, - objPtr runtime.Object, -) error { - if _, err := conversion.EnforcePtr(objPtr); err != nil { - panic("unable to convert output object to pointer") - } - _, _, err := codec.Decode(value, nil, objPtr) - if err != nil { - return err - } - return nil -} - -func removeNamespace(obj runtime.Object) error { - if err := scmeta.GetAccessor().SetNamespace(obj, ""); err != nil { - glog.Errorf("removing namespace from %#v (%s)", obj, err) - return err - } - return nil -} - -func checkPreconditions( - key string, - preconditions *storage.Preconditions, - out runtime.Object, -) error { - if preconditions == nil { - return nil - } - objMeta, err := meta.Accessor(out) - if err != nil { - return storage.NewInternalErrorf( - "can't enforce preconditions %v on un-introspectable object %v, got error: %v", - *preconditions, out, err, - ) - } - if preconditions.UID != nil && *preconditions.UID != objMeta.GetUID() { - errMsg := fmt.Sprintf( - "Precondition failed: UID in precondition: %v, UID in object meta: %v", - *preconditions.UID, objMeta.GetUID(), - ) - return storage.NewInvalidObjError(key, errMsg) - } - return nil -} - -// getDeletionInfo returns whether the deletion timestsamp exists on obj -// if there was an error determining whether it exists, returns a non-nil error -func getDeletionInfo(obj runtime.Object) (bool, error) { - dtExists, err := scmeta.DeletionTimestampExists(obj) - if err != nil { - glog.Errorf("determining whether the deletion timestamp exists (%s)", err) - return false, err - } - return dtExists, nil -} diff --git a/pkg/storage/tpr/storage_interface_test.go b/pkg/storage/tpr/storage_interface_test.go deleted file mode 100644 index 58f64b14940..00000000000 --- a/pkg/storage/tpr/storage_interface_test.go +++ /dev/null @@ -1,1224 +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 tpr - -import ( - "context" - "errors" - "fmt" - "reflect" - "testing" - - scmeta "github.com/kubernetes-incubator/service-catalog/pkg/api/meta" - "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog" - sc "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog" - _ "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/install" - "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/testapi" - "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1alpha1" - "github.com/kubernetes-incubator/service-catalog/pkg/rest/core/fake" - "k8s.io/apimachinery/pkg/api/equality" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/diff" - "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/storage" - "k8s.io/apiserver/pkg/storage/etcd" -) - -const ( - globalNamespace = "globalns" - namespace = "testns" - name = "testthing" -) - -func TestCreateExistingWithNoNamespace(t *testing.T) { - keyer := getServiceBrokerKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceBroker{} - }) - iface := getServiceBrokerTPRStorageIFace(t, keyer, fakeCl) - // Ensure an existing broker - fakeCl.Storage.Set(globalNamespace, ServiceBrokerKind.URLName(), name, &sc.ServiceBroker{ - ObjectMeta: metav1.ObjectMeta{Name: name}, - }) - inputServiceBroker := &sc.ServiceBroker{ - ObjectMeta: metav1.ObjectMeta{Name: name}, - } - key, err := keyer.Key(request.NewContext(), name) - if err != nil { - t.Fatalf("error constructing key (%s)", err) - } - createdServiceBroker := &sc.ServiceBroker{} - err = iface.Create( - context.Background(), - key, - inputServiceBroker, - createdServiceBroker, - uint64(0), - ) - if err = verifyStorageError(err, storage.ErrCodeKeyExists); err != nil { - t.Fatal(err) - } - // Object should remain unmodified-- i.e. deeply equal to a new broker - if err = deepCompare( - "output", - createdServiceBroker, - "new broker", - &sc.ServiceBroker{}, - ); err != nil { - t.Fatal(err) - } -} - -func TestCreateExistingWithNamespace(t *testing.T) { - keyer := getServiceInstanceKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceInstance{} - }) - iface := getServiceInstanceTPRStorageIFace(t, keyer, fakeCl) - // Ensure an existing instance - fakeCl.Storage.Set(namespace, ServiceInstanceKind.URLName(), name, &sc.ServiceInstance{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - }) - inputServiceInstance := &sc.ServiceInstance{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - } - ctx := request.NewContext() - ctx = request.WithNamespace(ctx, namespace) - key, err := keyer.Key(ctx, name) - if err != nil { - t.Fatalf("error constructing key (%s)", err) - } - createdServiceInstance := &sc.ServiceInstance{} - err = iface.Create( - context.Background(), - key, - inputServiceInstance, - createdServiceInstance, - uint64(0), - ) - if err = verifyStorageError(err, storage.ErrCodeKeyExists); err != nil { - t.Fatal(err) - } - // Object should remain unmodified-- i.e. deeply equal to a new instance - if err = deepCompare( - "output", - createdServiceInstance, - "new instance", - &sc.ServiceInstance{}, - ); err != nil { - t.Fatal(err) - } -} - -func TestCreateWithNoNamespace(t *testing.T) { - keyer := getServiceBrokerKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceBroker{} - }) - iface := getServiceBrokerTPRStorageIFace(t, keyer, fakeCl) - inputServiceBroker := &sc.ServiceBroker{ - ObjectMeta: metav1.ObjectMeta{Name: name}, - Spec: sc.ServiceBrokerSpec{ - URL: "http://my-awesome-broker.io", - RelistBehavior: sc.ServiceBrokerRelistBehaviorManual, - }, - } - key, err := keyer.Key(request.NewContext(), name) - if err != nil { - t.Fatalf("error constructing key (%s)", err) - } - createdServiceBroker := &sc.ServiceBroker{} - if err := iface.Create( - context.Background(), - key, - inputServiceBroker, - createdServiceBroker, - uint64(0), - ); err != nil { - t.Fatalf("error on create (%s)", err) - } - // Confirm resource version got set during the create operation - if createdServiceBroker.ResourceVersion == "" { - t.Fatalf("resource version was not set as expected") - } - // Confirm the output is identical to what is in storage (nothing funny - // happened during encoding / decoding the response). - obj := fakeCl.Storage.Get(globalNamespace, ServiceBrokerKind.URLName(), name) - if obj == nil { - t.Fatal("no broker was in storage") - } - err = deepCompare("output", createdServiceBroker, "object in storage", obj) - if err != nil { - t.Fatal(err) - } - // Output and what's in storage should be known to be deeply equal at this - // point. Compare either of those to what was passed in. The only diff should - // be resource version, so we will set that first. - inputServiceBroker.ResourceVersion = createdServiceBroker.ResourceVersion - err = deepCompare("input", inputServiceBroker, "output", createdServiceBroker) - if err != nil { - t.Fatal(err) - } -} - -func TestCreateWithNamespace(t *testing.T) { - keyer := getServiceInstanceKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceInstance{} - }) - iface := getServiceInstanceTPRStorageIFace(t, keyer, fakeCl) - inputServiceInstance := &sc.ServiceInstance{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - Spec: sc.ServiceInstanceSpec{ - ExternalID: "e6a8edad-145a-47f1-aaba-c0eb20b233a3", - ExternalServicePlanName: "some-awesome-plan", - }, - } - ctx := request.NewContext() - ctx = request.WithNamespace(ctx, namespace) - key, err := keyer.Key(ctx, name) - if err != nil { - t.Fatalf("error constructing key (%s)", err) - } - createdServiceInstance := &sc.ServiceInstance{} - if err := iface.Create( - context.Background(), - key, - inputServiceInstance, - createdServiceInstance, - uint64(0), - ); err != nil { - t.Fatalf("error on create (%s)", err) - } - // Confirm resource version got set during the create operation - if createdServiceInstance.ResourceVersion == "" { - t.Fatalf("resource version was not set as expected") - } - // Confirm the output is identical to what is in storage (nothing funny - // happened during encoding / decoding the response). - obj := fakeCl.Storage.Get(namespace, ServiceInstanceKind.URLName(), name) - if obj == nil { - t.Fatal("no instance was in storage") - } - err = deepCompare("output", createdServiceInstance, "object in storage", obj) - if err != nil { - t.Fatal(err) - } - // Output and what's in storage should be known to be deeply equal at this - // point. Compare either of those to what was passed in. The only diff should - // be resource version, so we will set that first. - inputServiceInstance.ResourceVersion = createdServiceInstance.ResourceVersion - err = deepCompare("input", inputServiceInstance, "output", createdServiceInstance) - if err != nil { - t.Fatal(err) - } -} - -func TestGetNonExistentWithNoNamespace(t *testing.T) { - keyer := getServiceBrokerKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceBroker{} - }) - iface := getServiceBrokerTPRStorageIFace(t, keyer, fakeCl) - key, err := keyer.Key(request.NewContext(), name) - if err != nil { - t.Fatalf("error constructing key (%s)", err) - } - outServiceBroker := &sc.ServiceBroker{} - // Ignore not found - if err := iface.Get( - context.Background(), - key, - "", // TODO: Current impl ignores resource version-- may be wrong - outServiceBroker, - true, - ); err != nil { - t.Fatalf("expected no error, but received one (%s)", err) - } - // Object should remain unmodified-- i.e. deeply equal to a new broker - err = deepCompare("output", outServiceBroker, "new broker", &sc.ServiceBroker{}) - if err != nil { - t.Fatal(err) - } - // Do not ignore not found - err = iface.Get( - context.Background(), - key, - "", // TODO: Current impl ignores resource version-- may be wrong - outServiceBroker, - false, - ) - if err = verifyStorageError(err, storage.ErrCodeKeyNotFound); err != nil { - t.Fatal(err) - } - // Object should remain unmodified-- i.e. deeply equal to a new broker - err = deepCompare("output", outServiceBroker, "new broker", &sc.ServiceBroker{}) - if err != nil { - t.Fatal(err) - } -} - -func TestGetNonExistentWithNamespace(t *testing.T) { - keyer := getServiceInstanceKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceInstance{} - }) - iface := getServiceInstanceTPRStorageIFace(t, keyer, fakeCl) - ctx := request.NewContext() - ctx = request.WithNamespace(ctx, namespace) - key, err := keyer.Key(ctx, name) - if err != nil { - t.Fatalf("error constructing key (%s)", err) - } - outServiceInstance := &sc.ServiceInstance{} - // Ignore not found - if err := iface.Get( - context.Background(), - key, - "", // TODO: Current impl ignores resource version-- may be wrong - outServiceInstance, - true, - ); err != nil { - t.Fatalf("expected no error, but received one (%s)", err) - } - // Object should remain unmodified-- i.e. deeply equal to a new instance - err = deepCompare("output", outServiceInstance, "new instance", &sc.ServiceInstance{}) - if err != nil { - t.Fatal(err) - } - // Do not ignore not found - err = iface.Get( - context.Background(), - key, - "", // TODO: Current impl ignores resource version-- may be wrong - outServiceInstance, - false, - ) - if err = verifyStorageError(err, storage.ErrCodeKeyNotFound); err != nil { - t.Fatal(err) - } - // Object should remain unmodified-- i.e. deeply equal to a new instance - err = deepCompare("output", outServiceInstance, "new instance", &sc.ServiceInstance{}) - if err != nil { - t.Fatal(err) - } -} - -func TestGetWithNoNamespace(t *testing.T) { - keyer := getServiceBrokerKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceBroker{} - }) - iface := getServiceBrokerTPRStorageIFace(t, keyer, fakeCl) - // Ensure an existing broker - fakeCl.Storage.Set(globalNamespace, ServiceBrokerKind.URLName(), name, &sc.ServiceBroker{ - ObjectMeta: metav1.ObjectMeta{Name: name}, - Spec: sc.ServiceBrokerSpec{ - RelistBehavior: sc.ServiceBrokerRelistBehaviorManual, - }, - }) - key, err := keyer.Key(request.NewContext(), name) - if err != nil { - t.Fatalf("error constructing key (%s)", err) - } - broker := &sc.ServiceBroker{} - if err := iface.Get( - context.Background(), - key, - "", // TODO: Current impl ignores resource version-- may be wrong - broker, - false, // Do not ignore if not found; error instead - ); err != nil { - t.Fatalf("error getting object (%s)", err) - } - // Confirm the output is identical to what is in storage (nothing funny - // happened during encoding / decoding the response). - obj := fakeCl.Storage.Get(globalNamespace, ServiceBrokerKind.URLName(), name) - if obj == nil { - t.Fatal("no broker was in storage") - } - err = deepCompare("output", broker, "object in storage", obj) - if err != nil { - t.Fatal(err) - } -} - -func TestGetWithNamespace(t *testing.T) { - keyer := getServiceInstanceKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceInstance{} - }) - iface := getServiceInstanceTPRStorageIFace(t, keyer, fakeCl) - // Ensure an existing instance - fakeCl.Storage.Set(namespace, ServiceInstanceKind.URLName(), name, &sc.ServiceInstance{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - Spec: sc.ServiceInstanceSpec{ - ExternalID: "e6a8edad-145a-47f1-aaba-c0eb20b233a3", - ExternalServicePlanName: "some-awesome-plan", - }, - }) - ctx := request.NewContext() - ctx = request.WithNamespace(ctx, namespace) - key, err := keyer.Key(ctx, name) - if err != nil { - t.Fatalf("error constructing key (%s)", err) - } - instance := &sc.ServiceInstance{} - if err := iface.Get( - context.Background(), - key, - "", // TODO: Current impl ignores resource version-- may be wrong - instance, - false, // Do not ignore if not found; error instead - ); err != nil { - t.Fatalf("error getting object (%s)", err) - } - // Confirm the output is identical to what is in storage (nothing funny - // happened during encoding / decoding the response). - obj := fakeCl.Storage.Get(namespace, ServiceInstanceKind.URLName(), name) - if obj == nil { - t.Fatal("no instance was in storage") - } - err = deepCompare("output", instance, "object in storage", obj) - if err != nil { - t.Fatal(err) - } -} - -func TestGetEmptyListWithNoNamespace(t *testing.T) { - keyer := getServiceBrokerKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceBrokerList{} - }) - iface := getServiceBrokerTPRStorageIFace(t, keyer, fakeCl) - key := keyer.KeyRoot(request.NewContext()) - outServiceBrokerList := &sc.ServiceBrokerList{} - if err := iface.List( - context.Background(), - key, - "", // TODO: Current impl ignores resource version-- may be wrong - // TODO: Current impl ignores selection predicate-- may be wrong - storage.SelectionPredicate{}, - outServiceBrokerList, - ); err != nil { - t.Fatalf("error listing objects (%s)", err) - } - if len(outServiceBrokerList.Items) != 0 { - t.Fatalf( - "expected an empty list, but got %d items", - len(outServiceBrokerList.Items), - ) - } - // Repeat using GetToList - if err := iface.GetToList( - context.Background(), - key, - "", // TODO: Current impl ignores resource version-- may be wrong - // TODO: Current impl ignores selection predicate-- may be wrong - storage.SelectionPredicate{}, - outServiceBrokerList, - ); err != nil { - t.Fatalf("error listing objects (%s)", err) - } - if len(outServiceBrokerList.Items) != 0 { - t.Fatalf( - "expected an empty list, but got %d items", - len(outServiceBrokerList.Items), - ) - } -} - -func TestGetEmptyListWithNamespace(t *testing.T) { - keyer := getServiceInstanceKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceInstanceList{} - }) - iface := getServiceInstanceTPRStorageIFace(t, keyer, fakeCl) - ctx := request.NewContext() - ctx = request.WithNamespace(ctx, namespace) - key := keyer.KeyRoot(ctx) - outServiceInstanceList := &sc.ServiceInstanceList{} - if err := iface.List( - context.Background(), - key, - "", // TODO: Current impl ignores resource version-- may be wrong - // TODO: Current impl ignores selection predicate-- may be wrong - storage.SelectionPredicate{}, - outServiceInstanceList, - ); err != nil { - t.Fatalf("error listing objects (%s)", err) - } - if len(outServiceInstanceList.Items) != 0 { - t.Fatalf( - "expected an empty list, but got %d items", - len(outServiceInstanceList.Items), - ) - } - // Repeat using GetToList - if err := iface.GetToList( - context.Background(), - key, - "", // TODO: Current impl ignores resource version-- may be wrong - // TODO: Current impl ignores selection predicate-- may be wrong - storage.SelectionPredicate{}, - outServiceInstanceList, - ); err != nil { - t.Fatalf("error listing objects (%s)", err) - } - if len(outServiceInstanceList.Items) != 0 { - t.Fatalf( - "expected an empty list, but got %d items", - len(outServiceInstanceList.Items), - ) - } -} - -func TestGetListWithNoNamespace(t *testing.T) { - keyer := getServiceBrokerKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceBrokerList{} - }) - iface := getServiceBrokerTPRStorageIFace(t, keyer, fakeCl) - // Ensure an existing broker - fakeCl.Storage.Set(globalNamespace, ServiceBrokerKind.URLName(), name, &sc.ServiceBroker{ - ObjectMeta: metav1.ObjectMeta{Name: name}, - Spec: sc.ServiceBrokerSpec{ - RelistBehavior: sc.ServiceBrokerRelistBehaviorManual, - }, - }) - list := &sc.ServiceBrokerList{} - if err := iface.List( - context.Background(), - keyer.KeyRoot(request.NewContext()), - "", // TODO: Current impl ignores resource version-- may be wrong - // TODO: Current impl ignores selection predicate-- may be wrong - storage.SelectionPredicate{}, - list, - ); err != nil { - t.Fatalf("error listing objects (%s)", err) - } - // List should contain precisely one item - if len(list.Items) != 1 { - t.Fatalf( - "expected list to contain exactly one item, but got %d items", - len(list.Items), - ) - } - // That one list item should be deeply equal to what's in storage - obj := fakeCl.Storage.Get(globalNamespace, ServiceBrokerKind.URLName(), name) - if obj == nil { - t.Fatal("no broker was in storage") - } - if err := deepCompare( - "retrieved list item", - &list.Items[0], - "object in storage", - obj, - ); err != nil { - t.Fatal(err) - } -} - -func TestGetListWithNamespace(t *testing.T) { - keyer := getServiceInstanceKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceInstanceList{} - }) - iface := getServiceInstanceTPRStorageIFace(t, keyer, fakeCl) - // Ensure an existing instance - fakeCl.Storage.Set(globalNamespace, ServiceInstanceKind.URLName(), name, &sc.ServiceInstance{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - Spec: sc.ServiceInstanceSpec{ - ExternalID: "e6a8edad-145a-47f1-aaba-c0eb20b233a3", - ExternalServicePlanName: "some-awesome-plan", - }, - }) - list := &sc.ServiceInstanceList{} - if err := iface.List( - context.Background(), - keyer.KeyRoot(request.NewContext()), - "", // TODO: Current impl ignores resource version-- may be wrong - // TODO: Current impl ignores selection predicate-- may be wrong - storage.SelectionPredicate{}, - list, - ); err != nil { - t.Fatalf("error listing objects (%s)", err) - } - // List should contain precisely one item - if len(list.Items) != 1 { - t.Fatalf( - "expected list to contain exactly one item, but got %d items", - len(list.Items), - ) - } - // That one list item should be deeply equal to what's in storage - obj := fakeCl.Storage.Get(globalNamespace, ServiceInstanceKind.URLName(), name) - if obj == nil { - t.Fatal("no instance was in storage") - } - if err := deepCompare( - "retrieved list item", - &list.Items[0], - "object in storage", - obj, - ); err != nil { - t.Fatal(err) - } -} - -func TestUpdateNonExistentWithNoNamespace(t *testing.T) { - keyer := getServiceBrokerKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceBroker{} - }) - iface := getServiceBrokerTPRStorageIFace(t, keyer, fakeCl) - key, err := keyer.Key(request.NewContext(), name) - newURL := "http://your-incredible-broker.io" - if err != nil { - t.Fatalf("error constructing key (%s)", err) - } - updatedServiceBroker := &sc.ServiceBroker{} - // Ignore not found - err = iface.GuaranteedUpdate( - context.Background(), - key, - updatedServiceBroker, - true, // Ignore not found - nil, // No preconditions for the update - storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) { - broker := obj.(*sc.ServiceBroker) - broker.Spec.URL = newURL - return broker, nil - }), - ) - // Object should remain unmodified-- i.e. deeply equal to a new broker - err = deepCompare("updated broker", updatedServiceBroker, "new broker", &sc.ServiceBroker{}) - if err != nil { - t.Fatal(err) - } - // Do not ignore not found - err = iface.GuaranteedUpdate( - context.Background(), - key, - updatedServiceBroker, - false, // Do not ignore not found - nil, // No preconditions for the update - storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) { - broker := obj.(*sc.ServiceBroker) - broker.Spec.URL = newURL - return broker, nil - }), - ) - if err = verifyStorageError(err, storage.ErrCodeKeyNotFound); err != nil { - t.Fatal(err) - } - // Object should remain unmodified-- i.e. deeply equal to a new broker - err = deepCompare("updated broker", updatedServiceBroker, "new broker", &sc.ServiceBroker{}) - if err != nil { - t.Fatal(err) - } -} - -func TestUpdateNonExistentWithNamespace(t *testing.T) { - keyer := getServiceInstanceKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceInstance{} - }) - iface := getServiceInstanceTPRStorageIFace(t, keyer, fakeCl) - ctx := request.NewContext() - ctx = request.WithNamespace(ctx, namespace) - key, err := keyer.Key(ctx, name) - if err != nil { - t.Fatalf("error constructing key (%s)", err) - } - newPlanName := "my-really-awesome-plan" - updatedServiceInstance := &sc.ServiceInstance{} - // Ignore not found - err = iface.GuaranteedUpdate( - context.Background(), - key, - updatedServiceInstance, - true, // Ignore not found - nil, // No preconditions for the update - storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) { - instance := obj.(*sc.ServiceInstance) - instance.Spec.ExternalServicePlanName = newPlanName - return instance, nil - }), - ) - // Object should remain unmodified-- i.e. deeply equal to a new instance - err = deepCompare("updated instance", updatedServiceInstance, "new instance", &sc.ServiceInstance{}) - if err != nil { - t.Fatal(err) - } - // Do not ignore not found - err = iface.GuaranteedUpdate( - context.Background(), - key, - updatedServiceInstance, - false, // Do not ignore not found - nil, // No preconditions for the update - storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) { - instance := obj.(*sc.ServiceInstance) - instance.Spec.ExternalServicePlanName = newPlanName - return instance, nil - }), - ) - if err = verifyStorageError(err, storage.ErrCodeKeyNotFound); err != nil { - t.Fatal(err) - } - // Object should remain unmodified-- i.e. deeply equal to a new broker - err = deepCompare("updated instance", updatedServiceInstance, "new broker", &sc.ServiceInstance{}) - if err != nil { - t.Fatal(err) - } -} - -func TestUpdateWithNoNamespace(t *testing.T) { - keyer := getServiceBrokerKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceBroker{} - }) - iface := getServiceBrokerTPRStorageIFace(t, keyer, fakeCl) - var origRev uint64 = 1 - newURL := "http://your-incredible-broker.io" - fakeCl.Storage.Set(globalNamespace, ServiceBrokerKind.URLName(), name, &sc.ServiceBroker{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - ResourceVersion: fmt.Sprintf("%d", origRev), - }, - Spec: sc.ServiceBrokerSpec{ - URL: "http://my-awesome-broker.io", - }, - }) - key, err := keyer.Key(request.NewContext(), name) - if err != nil { - t.Fatalf("error constructing key (%s)", err) - } - updatedServiceBroker := &sc.ServiceBroker{} - err = iface.GuaranteedUpdate( - context.Background(), - key, - updatedServiceBroker, - false, // Don't ignore not found - nil, // No preconditions for the update - storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) { - broker := obj.(*sc.ServiceBroker) - broker.Spec.URL = newURL - return broker, nil - }), - ) - if err != nil { - t.Fatalf("unexpected error updating object (%s)", err) - } - updatedRev, err := iface.versioner.ObjectResourceVersion(updatedServiceBroker) - if err != nil { - t.Fatalf("error extracting resource version (%s)", err) - } - if updatedRev <= origRev { - t.Fatalf( - "expected a new resource version > %d; got %d", - origRev, - updatedRev, - ) - } - if updatedServiceBroker.Spec.URL != newURL { - t.Fatal("expectd url to have been updated, but it was not") - } -} - -func TestUpdateWithNamespace(t *testing.T) { - keyer := getServiceInstanceKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceInstance{} - }) - iface := getServiceInstanceTPRStorageIFace(t, keyer, fakeCl) - var origRev uint64 = 1 - newPlanName := "my-really-awesome-plan" - fakeCl.Storage.Set(namespace, ServiceInstanceKind.URLName(), name, &sc.ServiceInstance{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - ResourceVersion: fmt.Sprintf("%d", origRev), - }, - Spec: sc.ServiceInstanceSpec{ - ExternalServicePlanName: "my-awesome-plan", - }, - }) - ctx := request.NewContext() - ctx = request.WithNamespace(ctx, namespace) - key, err := keyer.Key(ctx, name) - if err != nil { - t.Fatalf("error constructing key (%s)", err) - } - updatedServiceInstance := &sc.ServiceInstance{} - err = iface.GuaranteedUpdate( - context.Background(), - key, - updatedServiceInstance, - false, // Don't ignore not found - nil, // No preconditions for the update - storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) { - instance := obj.(*sc.ServiceInstance) - instance.Spec.ExternalServicePlanName = newPlanName - return instance, nil - }), - ) - if err != nil { - t.Fatalf("unexpected error updating object (%s)", err) - } - updatedRev, err := iface.versioner.ObjectResourceVersion(updatedServiceInstance) - if err != nil { - t.Fatalf("error extracting resource version (%s)", err) - } - if updatedRev <= origRev { - t.Fatalf( - "expected a new resource version > %d; got %d", - origRev, - updatedRev, - ) - } - if updatedServiceInstance.Spec.ExternalServicePlanName != newPlanName { - t.Fatal("expectd plan name to have been updated, but it was not") - } -} - -func TestDeleteNonExistentWithNoNamespace(t *testing.T) { - keyer := getServiceBrokerKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceBroker{} - }) - iface := getServiceBrokerTPRStorageIFace(t, keyer, fakeCl) - key, err := keyer.Key(request.NewContext(), name) - if err != nil { - t.Fatalf("error constructing key (%s)", err) - } - outServiceBroker := &sc.ServiceBroker{} - err = iface.Delete( - context.Background(), - key, - outServiceBroker, - nil, // TODO: Current impl ignores preconditions-- may be wrong - ) - if err = verifyStorageError(err, storage.ErrCodeKeyNotFound); err != nil { - t.Fatal(err) - } - // Object should remain unmodified-- i.e. deeply equal to a new broker - err = deepCompare("output", outServiceBroker, "new broker", &sc.ServiceBroker{}) - if err != nil { - t.Fatal(err) - } -} - -func TestDeleteNonExistentWithNamespace(t *testing.T) { - keyer := getServiceInstanceKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceInstance{} - }) - iface := getServiceInstanceTPRStorageIFace(t, keyer, fakeCl) - ctx := request.NewContext() - ctx = request.WithNamespace(ctx, namespace) - key, err := keyer.Key(ctx, name) - if err != nil { - t.Fatalf("error constructing key (%s)", err) - } - outServiceInstance := &sc.ServiceInstance{} - err = iface.Delete( - context.Background(), - key, - outServiceInstance, - nil, // TODO: Current impl ignores preconditions-- may be wrong - ) - if err = verifyStorageError(err, storage.ErrCodeKeyNotFound); err != nil { - t.Fatal(err) - } - // Object should remain unmodified-- i.e. deeply equal to a new instance - err = deepCompare("output", outServiceInstance, "new instance", &sc.ServiceInstance{}) - if err != nil { - t.Fatal(err) - } -} - -func TestDeleteWithNoNamespace(t *testing.T) { - keyer := getServiceBrokerKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceBroker{} - }) - iface := getServiceBrokerTPRStorageIFace(t, keyer, fakeCl) - var origRev uint64 = 1 - brokerNoFinalizers := &sc.ServiceBroker{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - ResourceVersion: fmt.Sprintf("%d", origRev), - }, - Spec: sc.ServiceBrokerSpec{ - RelistBehavior: sc.ServiceBrokerRelistBehaviorManual, - }, - } - brokerWithFinalizers := *brokerNoFinalizers - brokerWithFinalizers.Finalizers = append(brokerWithFinalizers.Finalizers, v1alpha1.FinalizerServiceCatalog) - fakeCl.Storage.Set(globalNamespace, ServiceBrokerKind.URLName(), name, &brokerWithFinalizers) - key, err := keyer.Key(request.NewContext(), name) - if err != nil { - t.Fatalf("error constructing key (%s)", err) - } - outServiceBroker := &sc.ServiceBroker{} - err = iface.Delete( - context.Background(), - key, - outServiceBroker, - nil, // TODO: Current impl ignores preconditions-- may be wrong - ) - if err != nil { - t.Fatalf("unexpected error deleting object (%s)", err) - } - // Object should be removed from underlying storage - obj := fakeCl.Storage.Get(globalNamespace, ServiceBrokerKind.URLName(), name) - finalizers, err := scmeta.GetFinalizers(obj) - if err != nil { - t.Fatalf("error getting finalizers (%s)", err) - } - if len(finalizers) != 0 { - t.Fatalf("expected no finalizers, got %#v", finalizers) - } - // the delete call does a PUT, which increments the resource version. brokerNoFinalizers - // and obj should match exactly except for the resource version, so do the increment here - brokerNoFinalizers.ResourceVersion = fmt.Sprintf("%d", origRev+1) - if err := deepCompare("expected", brokerNoFinalizers, "actual", obj); err != nil { - t.Fatal(err) - } -} - -func TestDeleteWithNamespace(t *testing.T) { - keyer := getServiceInstanceKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceInstance{} - }) - iface := getServiceInstanceTPRStorageIFace(t, keyer, fakeCl) - var origRev uint64 = 1 - instanceNoFinalizers := &sc.ServiceInstance{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - ResourceVersion: fmt.Sprintf("%d", origRev), - }, - Spec: sc.ServiceInstanceSpec{ - ExternalID: "76026cec-f601-487f-b6bd-6d6f8240d620", - }, - } - instanceWithFinalizers := *instanceNoFinalizers - instanceWithFinalizers.Finalizers = append(instanceWithFinalizers.Finalizers, v1alpha1.FinalizerServiceCatalog) - fakeCl.Storage.Set(namespace, ServiceInstanceKind.URLName(), name, &instanceWithFinalizers) - ctx := request.NewContext() - ctx = request.WithNamespace(ctx, namespace) - key, err := keyer.Key(ctx, name) - if err != nil { - t.Fatalf("error constructing key (%s)", err) - } - outServiceInstance := &sc.ServiceInstance{} - err = iface.Delete( - context.Background(), - key, - outServiceInstance, - nil, // TODO: Current impl ignores preconditions-- may be wrong - ) - if err != nil { - t.Fatalf("unexpected error deleting object (%s)", err) - } - // Object should be removed from underlying storage - obj := fakeCl.Storage.Get(namespace, ServiceInstanceKind.URLName(), name) - finalizers, err := scmeta.GetFinalizers(obj) - if err != nil { - t.Fatalf("error getting finalizers (%s)", err) - } - if len(finalizers) != 0 { - t.Fatalf("expected no finalizers, got %#v", finalizers) - } - // the delete call does a PUT, which increments the resource version. brokerNoFinalizers - // and obj should match exactly except for the resource version, so do the increment here - instanceNoFinalizers.ResourceVersion = fmt.Sprintf("%d", origRev+1) - if err := deepCompare("expected", instanceNoFinalizers, "actual", obj); err != nil { - t.Fatal(err) - } -} - -func TestWatchWithNamespace(t *testing.T) { - keyer := getServiceInstanceKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceInstance{} - }) - iface := getServiceInstanceTPRStorageIFace(t, keyer, fakeCl) - obj := &sc.ServiceInstance{ - TypeMeta: metav1.TypeMeta{Kind: ServiceInstanceKind.String()}, - ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace}, - Spec: sc.ServiceInstanceSpec{ - ExternalID: "9ac07e7d-6c32-48f6-96ef-5a215f69df36", - }, - } - // send an unversioned object into the watch test. it sends this object to the - // fake REST client, which encodes the unversioned object into bytes & sends them - // to the storage interface's client. The storage interface's watchFilterer - // function calls singularShell to get the object to decode into, and singularShell returns - // an unversioned object. After watchFilterer decodes into the unversioned object, - // it simply returns it back to the watch stream - if err := runWatchTest(keyer, fakeCl, iface, obj); err != nil { - t.Fatal(err) - } -} - -func TestWatchWithNoNamespace(t *testing.T) { - keyer := getServiceBrokerKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceBroker{} - }) - iface := getServiceBrokerTPRStorageIFace(t, keyer, fakeCl) - obj := &sc.ServiceBroker{ - TypeMeta: metav1.TypeMeta{Kind: ServiceBrokerKind.String()}, - ObjectMeta: metav1.ObjectMeta{Name: name}, - Spec: sc.ServiceBrokerSpec{ - RelistBehavior: sc.ServiceBrokerRelistBehaviorManual, - }, - } - // send an unversioned object into the watch test. it sends this object to the - // fake REST client, which encodes the unversioned object into bytes & sends them - // to the storage interface's client. The storage interface's watchFilterer - // function calls singularShell to get the object to decode into, and singularShell returns - // an unversioned object. After watchFilterer decodes into the unversioned object, - // it does the necessary processing to strip out the namespace and return the new - // object back into the watch stream - if err := runWatchTest(keyer, fakeCl, iface, obj); err != nil { - t.Fatal(err) - } -} - -func TestWatchListWithNamespace(t *testing.T) { - keyer := getServiceInstanceKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceInstanceList{} - }) - iface := getServiceInstanceTPRStorageIFace(t, keyer, fakeCl) - - obj := &sc.ServiceInstanceList{ - Items: []sc.ServiceInstance{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s1", name), - Namespace: namespace, - }, - Spec: sc.ServiceInstanceSpec{ExternalID: "b13843f9-aea7-4ef6-b276-771a5ced2c65"}, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s2", name), - Namespace: namespace, - }, - Spec: sc.ServiceInstanceSpec{ExternalID: "b23843f9-aea7-4ef6-b276-771a5ced2c65"}, - }, - }, - } - // send an unversioned object into the watchList test. it sends this object to the - // fake REST client, which encodes the unversioned object into bytes & sends them - // to the storage interface's client. The storage interface's watchFilterer - // function calls listShell to get the object to decode into, and listShell returns - // an unversioned object. After watchFilterer decodes into the unversioned object, - // it simply returns it - if err := runWatchListTest(keyer, fakeCl, iface, obj); err != nil { - t.Fatal(err) - } -} - -func TestWatchListWithNoNamespace(t *testing.T) { - keyer := getServiceBrokerKeyer() - fakeCl := fake.NewRESTClient(func() runtime.Object { - return &sc.ServiceBrokerList{} - }) - iface := getServiceBrokerTPRStorageIFace(t, keyer, fakeCl) - obj := &sc.ServiceBrokerList{ - Items: []sc.ServiceBroker{ - { - ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s1", name)}, - Spec: sc.ServiceBrokerSpec{ - RelistBehavior: sc.ServiceBrokerRelistBehaviorManual, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s2", name)}, - Spec: sc.ServiceBrokerSpec{ - RelistBehavior: sc.ServiceBrokerRelistBehaviorManual, - }, - }, - }, - } - // send an unversioned object into the watchList test. it sends this object to the - // fake REST client, which encodes the unversioned object into bytes & sends them - // to the storage interface's client. The storage interface's watchFilterer - // function calls listShell to get the object to decode into, and listShell returns - // an unversioned object. After watchFilterer decodes into the unversioned object, - // it does necessary processing to strip the namespaces out of each object. - if err := runWatchListTest(keyer, fakeCl, iface, obj); err != nil { - t.Fatal(err) - } -} - -func TestRemoveNamespace(t *testing.T) { - obj := &servicecatalog.ServiceClass{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "testns", - }, - } - if err := removeNamespace(obj); err != nil { - t.Fatalf("couldn't remove namespace (%s", err) - } - if obj.Namespace != "" { - t.Fatalf( - "couldn't remove namespace from object. it is still %s", - obj.Namespace, - ) - } -} -func getServiceBrokerKeyer() Keyer { - return Keyer{ - DefaultNamespace: globalNamespace, - ResourceName: ServiceBrokerKind.String(), - Separator: "/", - } -} - -func getServiceInstanceKeyer() Keyer { - return Keyer{ - ResourceName: ServiceInstanceKind.String(), - Separator: "/", - } -} - -func getServiceBrokerTPRStorageIFace( - t *testing.T, - keyer Keyer, - restCl *fake.RESTClient, -) *store { - codec, err := testapi.GetCodecForObject(&sc.ServiceBroker{}) - if err != nil { - t.Fatalf("error getting codec (%s)", err) - } - return &store{ - decodeKey: keyer.NamespaceAndNameFromKey, - codec: codec, - cl: restCl, - singularKind: ServiceBrokerKind, - versioner: etcd.APIObjectVersioner{}, - singularShell: func(ns, name string) runtime.Object { - return &servicecatalog.ServiceBroker{ - TypeMeta: metav1.TypeMeta{ - Kind: ServiceBrokerKind.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: ns, - Name: name, - }, - } - }, - listShell: func() runtime.Object { - return &servicecatalog.ServiceBrokerList{} - }, - } -} - -func getServiceInstanceTPRStorageIFace( - t *testing.T, - keyer Keyer, - restCl *fake.RESTClient, -) *store { - codec, err := testapi.GetCodecForObject(&sc.ServiceInstance{}) - if err != nil { - t.Fatalf("error getting codec (%s)", err) - } - return &store{ - hasNamespace: true, - decodeKey: keyer.NamespaceAndNameFromKey, - codec: codec, - cl: restCl, - singularKind: ServiceInstanceKind, - versioner: etcd.APIObjectVersioner{}, - singularShell: func(ns, name string) runtime.Object { - return &servicecatalog.ServiceInstance{ - TypeMeta: metav1.TypeMeta{ - Kind: ServiceInstanceKind.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: ns, - Name: name, - }, - } - }, - listShell: func() runtime.Object { - return &servicecatalog.ServiceInstanceList{} - }, - } -} - -func verifyStorageError(err error, errorCode int) error { - if err == nil { - return errors.New("expected an error, but did not receive one") - } - storageErr, ok := err.(*storage.StorageError) - if !ok { - return fmt.Errorf( - "expected a storage.StorageError, but got a %s", - reflect.TypeOf(err), - ) - } - if storageErr.Code != errorCode { - return fmt.Errorf( - "expected error code %d, but got %d", - errorCode, - storageErr.Code, - ) - } - return nil -} - -func deepCompare( - obj1Name string, - obj1 runtime.Object, - obj2Name string, - obj2 runtime.Object, -) error { - if !equality.Semantic.DeepEqual(obj1, obj2) { - return fmt.Errorf( - "%s and %s are different: %s", - obj1Name, - obj2Name, - // TODO: It's probably not an equivalent to semantic DeepEqual, is there semantic diff implementation? - diff.ObjectReflectDiff(obj1, obj2), - ) - } - return nil -} diff --git a/pkg/storage/tpr/storage_interface_watch_utils_test.go b/pkg/storage/tpr/storage_interface_watch_utils_test.go deleted file mode 100644 index aed8ee22d4c..00000000000 --- a/pkg/storage/tpr/storage_interface_watch_utils_test.go +++ /dev/null @@ -1,136 +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 tpr - -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/kubernetes-incubator/service-catalog/pkg/rest/core/fake" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/storage" -) - -const watchTimeout = 1 * time.Second - -func runWatchTest(keyer Keyer, fakeCl *fake.RESTClient, iface storage.Interface, obj runtime.Object) error { - key, err := keyer.Key(request.NewContext(), name) - if err != nil { - return fmt.Errorf("error creating new key from Keyer (%s)", err) - } - resourceVsn := "1234" - predicate := storage.SelectionPredicate{} - - sendObjErrCh := make(chan error) - go func() { - if err := fakeCl.Watcher.SendObject(watch.Added, obj, 1*time.Second); err != nil { - sendObjErrCh <- err - return - } - fakeCl.Watcher.Close() - }() - - watchIface, err := iface.Watch(context.Background(), key, resourceVsn, predicate) - if err != nil { - return err - } - if watchIface == nil { - return err - } - defer watchIface.Stop() - ch := watchIface.ResultChan() - select { - case err := <-sendObjErrCh: - return fmt.Errorf("error sending object (%s)", err) - case evt, ok := <-ch: - if !ok { - return errors.New("watch channel was closed") - } - if evt.Type != watch.Added { - return errors.New("event type was not ADDED") - } - if err := deepCompare("expected", obj, "actual", evt.Object); err != nil { - return fmt.Errorf("received objects aren't the same (%s)", err) - } - case <-time.After(watchTimeout): - return fmt.Errorf("didn't receive an event within %s", watchTimeout) - } - select { - case _, ok := <-ch: - if ok { - return errors.New("watch channel was not closed") - } - case <-time.After(watchTimeout): - return fmt.Errorf("watch channel didn't receive after %s", watchTimeout) - } - return nil -} - -func runWatchListTest(keyer Keyer, fakeCl *fake.RESTClient, iface storage.Interface, obj runtime.Object) error { - const timeout = 1 * time.Second - reqCtx := request.NewContext() - reqCtx = request.WithNamespace(reqCtx, namespace) - key := keyer.KeyRoot(reqCtx) - resourceVsn := "1234" - predicate := storage.SelectionPredicate{} - sendObjErrCh := make(chan error) - go func() { - defer fakeCl.Watcher.Close() - if err := fakeCl.Watcher.SendObject(watch.Added, obj, 1*time.Second); err != nil { - sendObjErrCh <- err - return - } - }() - watchIface, err := iface.WatchList(context.Background(), key, resourceVsn, predicate) - if err != nil { - return err - } - if watchIface == nil { - return errors.New("expected non-nil watch interface") - } - defer watchIface.Stop() - ch := watchIface.ResultChan() - select { - case err := <-sendObjErrCh: - return fmt.Errorf("error sending object (%s)", err) - case evt, ok := <-ch: - if !ok { - return errors.New("watch channel was closed") - } - if evt.Type != watch.Added { - return errors.New("event type was not ADDED") - } - if err := deepCompare("expected", obj, "actual", evt.Object); err != nil { - return fmt.Errorf("received objects aren't the same (%s)", err) - } - case <-time.After(watchTimeout): - return fmt.Errorf("didn't receive after %s", watchTimeout) - } - select { - case _, ok := <-ch: - if ok { - return errors.New("watch channel was not closed") - } - case <-time.After(watchTimeout): - return fmt.Errorf("watch channel didn't receive after %s", watchTimeout) - } - return nil -} diff --git a/pkg/storage/tpr/tpr_converter.go b/pkg/storage/tpr/tpr_converter.go deleted file mode 100644 index 1050de90b32..00000000000 --- a/pkg/storage/tpr/tpr_converter.go +++ /dev/null @@ -1,50 +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 tpr - -import ( - "encoding/json" - - "k8s.io/apimachinery/pkg/runtime" -) - -// FromUnstructured converts o, a Kubernetes Third Party Resource type, into a -// *runtime.Unstructured and writes it to object. Returns a non-nil error is there were any issues -// with the conversion -func FromUnstructured(in runtime.Unstructured, out runtime.Object) error { - b, err := json.Marshal(in.UnstructuredContent()) - if err != nil { - return err - } - - return json.Unmarshal(b, out) -} - -// ToUnstructured converts in (which should be a Service Catalog third party object type) into a -// runtime.Unstructured for use in writing to Kubernetes -func ToUnstructured(in runtime.Object) (*runtime.Unstructured, error) { - m, err := json.Marshal(in) - if err != nil { - return nil, err - } - var ret runtime.Unstructured - err = json.Unmarshal(m, &ret) - if err != nil { - return nil, err - } - return &ret, nil -} diff --git a/test/integration/clientset_test.go b/test/integration/clientset_test.go index 18978ba6127..97adc34d486 100644 --- a/test/integration/clientset_test.go +++ b/test/integration/clientset_test.go @@ -74,7 +74,6 @@ const ( var storageTypes = []server.StorageType{ server.StorageTypeEtcd, - server.StorageTypeTPR, } // Used for testing binding parameters @@ -392,7 +391,7 @@ func TestServiceClassClient(t *testing.T) { } } } - // TODO: Fix this for TPR. + // TODO: Fix this for CRD. // https://github.com/kubernetes-incubator/service-catalog/issues/1256 // for _, sType := range storageTypes { // if !t.Run(sType.String(), rootTestFunc(sType)) { @@ -584,7 +583,7 @@ func TestServicePlanClient(t *testing.T) { } } } - // TODO: Fix this for TPR. + // TODO: Fix this for CRD. // https://github.com/kubernetes-incubator/service-catalog/issues/1256 // for _, sType := range storageTypes { // if !t.Run(sType.String(), rootTestFunc(sType)) { diff --git a/test/integration/framework.go b/test/integration/framework.go index 6b251e202b7..9066e98c8b5 100644 --- a/test/integration/framework.go +++ b/test/integration/framework.go @@ -29,7 +29,6 @@ import ( "k8s.io/apimachinery/pkg/util/wait" - fake "github.com/kubernetes-incubator/service-catalog/pkg/rest/core/fake" restclient "k8s.io/client-go/rest" genericserveroptions "k8s.io/apiserver/pkg/server/options" @@ -43,10 +42,6 @@ import ( _ "k8s.io/client-go/pkg/apis/extensions/install" ) -const ( - globalTPRNamespace = "globalTPRNamespace" -) - func init() { rand.Seed(time.Now().UnixNano()) } @@ -85,18 +80,10 @@ func withConfigGetFreshApiserverAndClient( secureServingOptions := genericserveroptions.NewSecureServingOptions() // start the server in the background go func() { - var tprOptions *server.TPROptions var etcdOptions *server.EtcdOptions if serverstorage.StorageTypeEtcd == serverConfig.storageType { etcdOptions = server.NewEtcdOptions() etcdOptions.StorageConfig.ServerList = serverConfig.etcdServerList - } else if serverstorage.StorageTypeTPR == serverConfig.storageType { - tprOptions = server.NewTPROptions() - tprOptions.RESTClient = fake.NewRESTClient(serverConfig.emptyObjFunc) - tprOptions.InstallTPRsFunc = func() error { - return nil - } - tprOptions.GlobalNamespace = globalTPRNamespace } else { t.Fatal("no storage type specified") } @@ -107,7 +94,6 @@ func withConfigGetFreshApiserverAndClient( AdmissionOptions: genericserveroptions.NewAdmissionOptions(), SecureServingOptions: secureServingOptions, EtcdOptions: etcdOptions, - TPROptions: tprOptions, AuthenticationOptions: genericserveroptions.NewDelegatingAuthenticationOptions(), AuthorizationOptions: genericserveroptions.NewDelegatingAuthorizationOptions(), AuditOptions: genericserveroptions.NewAuditOptions(),