diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 414fc09d57..78d424faba 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -38,6 +38,7 @@ import ( "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apiserver" "k8s.io/kubernetes/pkg/apiserver/authenticator" "k8s.io/kubernetes/pkg/capabilities" @@ -185,7 +186,7 @@ func Run(s *options.APIServer) error { if err != nil { glog.Fatalf("Unable to get serviceaccounts storage: %v", err) } - serviceAccountGetter = serviceaccountcontroller.NewGetterFromStorageInterface(storage) + serviceAccountGetter = serviceaccountcontroller.NewGetterFromStorageInterface(storage, storageFactory.ResourcePrefix(api.Resource("serviceaccounts")), storageFactory.ResourcePrefix(api.Resource("secrets"))) } authenticator, err := authenticator.New(authenticator.AuthenticatorConfig{ @@ -222,11 +223,11 @@ func Run(s *options.APIServer) error { if modeEnabled(apiserver.ModeRBAC) { mustGetRESTOptions := func(resource string) generic.RESTOptions { - s, err := storageFactory.New(api.Resource(resource)) + s, err := storageFactory.New(rbac.Resource(resource)) if err != nil { glog.Fatalf("Unable to get %s storage: %v", resource, err) } - return generic.RESTOptions{Storage: s, Decorator: generic.UndecoratedStorage} + return generic.RESTOptions{Storage: s, Decorator: generic.UndecoratedStorage, ResourcePrefix: storageFactory.ResourcePrefix(rbac.Resource(resource))} } // For initial bootstrapping go directly to etcd to avoid privillege escalation check. diff --git a/federation/cmd/federation-apiserver/app/server.go b/federation/cmd/federation-apiserver/app/server.go index 605e106555..ef40f664a7 100644 --- a/federation/cmd/federation-apiserver/app/server.go +++ b/federation/cmd/federation-apiserver/app/server.go @@ -168,5 +168,6 @@ func createRESTOptionsOrDie(s *genericoptions.ServerRunOptions, g *genericapiser Storage: storage, Decorator: g.StorageDecorator(), DeleteCollectionWorkers: s.DeleteCollectionWorkers, + ResourcePrefix: f.ResourcePrefix(resource), } } diff --git a/pkg/controller/serviceaccount/tokengetter.go b/pkg/controller/serviceaccount/tokengetter.go index bf5e8891b5..dd0c66ef34 100644 --- a/pkg/controller/serviceaccount/tokengetter.go +++ b/pkg/controller/serviceaccount/tokengetter.go @@ -69,9 +69,9 @@ func (r *registryGetter) GetSecret(namespace, name string) (*api.Secret, error) // NewGetterFromStorageInterface returns a ServiceAccountTokenGetter that // uses the specified storage to retrieve service accounts and secrets. -func NewGetterFromStorageInterface(s storage.Interface) serviceaccount.ServiceAccountTokenGetter { +func NewGetterFromStorageInterface(s storage.Interface, saPrefix, secretPrefix string) serviceaccount.ServiceAccountTokenGetter { return NewGetterFromRegistries( - serviceaccountregistry.NewRegistry(serviceaccountetcd.NewREST(generic.RESTOptions{Storage: s, Decorator: generic.UndecoratedStorage})), - secret.NewRegistry(secretetcd.NewREST(generic.RESTOptions{Storage: s, Decorator: generic.UndecoratedStorage})), + serviceaccountregistry.NewRegistry(serviceaccountetcd.NewREST(generic.RESTOptions{Storage: s, Decorator: generic.UndecoratedStorage, ResourcePrefix: saPrefix})), + secret.NewRegistry(secretetcd.NewREST(generic.RESTOptions{Storage: s, Decorator: generic.UndecoratedStorage, ResourcePrefix: secretPrefix})), ) } diff --git a/pkg/genericapiserver/storage_factory.go b/pkg/genericapiserver/storage_factory.go index 6835718ebe..58117eda74 100644 --- a/pkg/genericapiserver/storage_factory.go +++ b/pkg/genericapiserver/storage_factory.go @@ -19,6 +19,7 @@ package genericapiserver import ( "fmt" "mime" + "strings" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" @@ -37,6 +38,12 @@ type StorageFactory interface { // New finds the storage destination for the given group and resource. It will // return an error if the group has no storage destination configured. New(groupResource unversioned.GroupResource) (storage.Interface, error) + + // ResourcePrefix returns the overridden resource prefix for the GroupResource + // This allows for cohabitation of resources with different native types and provides + // centralized control over the shape of etcd directories + ResourcePrefix(groupResource unversioned.GroupResource) string + // Backends gets all backends for all registered storage destinations. // Used for getting all instances for health validations. Backends() []string @@ -78,8 +85,12 @@ type groupResourceOverrides struct { // etcdLocation contains the list of "special" locations that are used for particular GroupResources // These are merged on top of the StorageConfig when requesting the storage.Interface for a given GroupResource etcdLocation []string - // etcdPrefix contains the list of "special" prefixes for a GroupResource. Resource=* means for the entire group + // etcdPrefix is the base location for a GroupResource. etcdPrefix string + // etcdResourcePrefix is the location to use to store a particular type under the `etcdPrefix` location + // If empty, the default mapping is used. If the default mapping doesn't contain an entry, it will use + // the ToLowered name of the resource, not including the group. + etcdResourcePrefix string // mediaType is the desired serializer to choose. If empty, the default is chosen. mediaType string // serializer contains the list of "special" serializers for a GroupResource. Resource=* means for the entire group @@ -123,6 +134,13 @@ func (s *DefaultStorageFactory) SetEtcdPrefix(groupResource unversioned.GroupRes s.Overrides[groupResource] = overrides } +// SetResourceEtcdPrefix sets the prefix for a resource, but not the base-dir. You'll end up in `etcdPrefix/resourceEtcdPrefix`. +func (s *DefaultStorageFactory) SetResourceEtcdPrefix(groupResource unversioned.GroupResource, prefix string) { + overrides := s.Overrides[groupResource] + overrides.etcdResourcePrefix = prefix + s.Overrides[groupResource] = overrides +} + func (s *DefaultStorageFactory) SetSerializer(groupResource unversioned.GroupResource, mediaType string, serializer runtime.StorageSerializer) { overrides := s.Overrides[groupResource] overrides.mediaType = mediaType @@ -268,3 +286,30 @@ func NewStorageCodec(storageMediaType string, ns runtime.StorageSerializer, stor } return runtime.NewCodec(encoder, decoder), nil } + +var specialDefaultResourcePrefixes = map[unversioned.GroupResource]string{ + unversioned.GroupResource{Group: "", Resource: "replicationControllers"}: "controllers", + unversioned.GroupResource{Group: "", Resource: "replicationcontrollers"}: "controllers", + unversioned.GroupResource{Group: "", Resource: "endpoints"}: "services/endpoints", + unversioned.GroupResource{Group: "", Resource: "nodes"}: "minions", + unversioned.GroupResource{Group: "", Resource: "services"}: "services/specs", +} + +func (s *DefaultStorageFactory) ResourcePrefix(groupResource unversioned.GroupResource) string { + chosenStorageResource := s.getStorageGroupResource(groupResource) + groupOverride := s.Overrides[getAllResourcesAlias(chosenStorageResource)] + exactResourceOverride := s.Overrides[chosenStorageResource] + + etcdResourcePrefix := specialDefaultResourcePrefixes[chosenStorageResource] + if len(groupOverride.etcdResourcePrefix) > 0 { + etcdResourcePrefix = groupOverride.etcdResourcePrefix + } + if len(exactResourceOverride.etcdResourcePrefix) > 0 { + etcdResourcePrefix = exactResourceOverride.etcdResourcePrefix + } + if len(etcdResourcePrefix) == 0 { + etcdResourcePrefix = strings.ToLower(chosenStorageResource.Resource) + } + + return etcdResourcePrefix +} diff --git a/pkg/master/master.go b/pkg/master/master.go index 3636d92280..d05a2770fc 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -731,6 +731,7 @@ func (m *Master) GetRESTOptionsOrDie(c *Config, resource unversioned.GroupResour Storage: storage, Decorator: m.StorageDecorator(), DeleteCollectionWorkers: m.deleteCollectionWorkers, + ResourcePrefix: c.StorageFactory.ResourcePrefix(resource), } } diff --git a/pkg/registry/clusterrole/etcd/etcd.go b/pkg/registry/clusterrole/etcd/etcd.go index df3f2e5b8a..86b39dcfb9 100644 --- a/pkg/registry/clusterrole/etcd/etcd.go +++ b/pkg/registry/clusterrole/etcd/etcd.go @@ -34,7 +34,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against ClusterRole objects. func NewREST(opts generic.RESTOptions) *REST { - prefix := "/clusterroles" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &rbac.ClusterRoleList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/clusterrolebinding/etcd/etcd.go b/pkg/registry/clusterrolebinding/etcd/etcd.go index 8d053ea121..50c621800b 100644 --- a/pkg/registry/clusterrolebinding/etcd/etcd.go +++ b/pkg/registry/clusterrolebinding/etcd/etcd.go @@ -34,7 +34,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against ClusterRoleBinding objects. func NewREST(opts generic.RESTOptions) *REST { - prefix := "/clusterrolebindings" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &rbac.ClusterRoleBindingList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/configmap/etcd/etcd.go b/pkg/registry/configmap/etcd/etcd.go index 83c7f7aaaf..66fd775d20 100644 --- a/pkg/registry/configmap/etcd/etcd.go +++ b/pkg/registry/configmap/etcd/etcd.go @@ -32,7 +32,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work with ConfigMap objects. func NewREST(opts generic.RESTOptions) *REST { - prefix := "/configmaps" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &api.ConfigMapList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/controller/etcd/etcd.go b/pkg/registry/controller/etcd/etcd.go index b600118b52..fed90eaf0a 100644 --- a/pkg/registry/controller/etcd/etcd.go +++ b/pkg/registry/controller/etcd/etcd.go @@ -59,7 +59,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against replication controllers. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) { - prefix := "/controllers" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &api.ReplicationControllerList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/daemonset/etcd/etcd.go b/pkg/registry/daemonset/etcd/etcd.go index 42fb837d30..408e56c2c3 100644 --- a/pkg/registry/daemonset/etcd/etcd.go +++ b/pkg/registry/daemonset/etcd/etcd.go @@ -35,7 +35,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against DaemonSets. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) { - prefix := "/daemonsets" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &extensions.DaemonSetList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/deployment/etcd/etcd.go b/pkg/registry/deployment/etcd/etcd.go index 42de475acb..298bcceede 100644 --- a/pkg/registry/deployment/etcd/etcd.go +++ b/pkg/registry/deployment/etcd/etcd.go @@ -59,7 +59,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against deployments. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST, *RollbackREST) { - prefix := "/deployments" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &extensions.DeploymentList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/deployment/etcd/etcd_test.go b/pkg/registry/deployment/etcd/etcd_test.go index b311f90cc0..4e11bf921c 100644 --- a/pkg/registry/deployment/etcd/etcd_test.go +++ b/pkg/registry/deployment/etcd/etcd_test.go @@ -41,7 +41,7 @@ const defaultReplicas = 100 func newStorage(t *testing.T) (*DeploymentStorage, *etcdtesting.EtcdTestServer) { etcdStorage, server := registrytest.NewEtcdStorage(t, extensions.GroupName) - restOptions := generic.RESTOptions{Storage: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1} + restOptions := generic.RESTOptions{Storage: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1, ResourcePrefix: "deployments"} deploymentStorage := NewStorage(restOptions) return &deploymentStorage, server } diff --git a/pkg/registry/endpoint/etcd/etcd.go b/pkg/registry/endpoint/etcd/etcd.go index c90074dcf3..cd4d45cfc1 100644 --- a/pkg/registry/endpoint/etcd/etcd.go +++ b/pkg/registry/endpoint/etcd/etcd.go @@ -32,7 +32,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against endpoints. func NewREST(opts generic.RESTOptions) *REST { - prefix := "/services/endpoints" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &api.EndpointsList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/event/etcd/etcd.go b/pkg/registry/event/etcd/etcd.go index fd04e3ad88..fb5d3de697 100644 --- a/pkg/registry/event/etcd/etcd.go +++ b/pkg/registry/event/etcd/etcd.go @@ -30,7 +30,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against events. func NewREST(opts generic.RESTOptions, ttl uint64) *REST { - prefix := "/events" + prefix := "/" + opts.ResourcePrefix // We explicitly do NOT do any decoration here - switching on Cacher // for events will lead to too high memory consumption. diff --git a/pkg/registry/experimental/controller/etcd/etcd_test.go b/pkg/registry/experimental/controller/etcd/etcd_test.go index 70ea39e5f3..6fedb8cf9d 100644 --- a/pkg/registry/experimental/controller/etcd/etcd_test.go +++ b/pkg/registry/experimental/controller/etcd/etcd_test.go @@ -32,7 +32,7 @@ import ( func newStorage(t *testing.T) (*ScaleREST, *etcdtesting.EtcdTestServer, storage.Interface) { etcdStorage, server := registrytest.NewEtcdStorage(t, "") - restOptions := generic.RESTOptions{Storage: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1} + restOptions := generic.RESTOptions{Storage: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1, ResourcePrefix: "controllers"} return NewStorage(restOptions).Scale, server, etcdStorage } diff --git a/pkg/registry/generic/options.go b/pkg/registry/generic/options.go index b67f3a06af..39965731bc 100644 --- a/pkg/registry/generic/options.go +++ b/pkg/registry/generic/options.go @@ -25,4 +25,6 @@ type RESTOptions struct { Storage pkgstorage.Interface Decorator StorageDecorator DeleteCollectionWorkers int + + ResourcePrefix string } diff --git a/pkg/registry/horizontalpodautoscaler/etcd/etcd.go b/pkg/registry/horizontalpodautoscaler/etcd/etcd.go index d4d224d172..3b304d1043 100644 --- a/pkg/registry/horizontalpodautoscaler/etcd/etcd.go +++ b/pkg/registry/horizontalpodautoscaler/etcd/etcd.go @@ -34,7 +34,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against horizontal pod autoscalers. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) { - prefix := "/horizontalpodautoscalers" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &autoscaling.HorizontalPodAutoscalerList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/ingress/etcd/etcd.go b/pkg/registry/ingress/etcd/etcd.go index b3fab226cc..9c1b7f9bd4 100644 --- a/pkg/registry/ingress/etcd/etcd.go +++ b/pkg/registry/ingress/etcd/etcd.go @@ -35,7 +35,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against replication controllers. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) { - prefix := "/ingress" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &extensions.IngressList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/job/etcd/etcd.go b/pkg/registry/job/etcd/etcd.go index 5d9313d85d..dbef9a38c2 100644 --- a/pkg/registry/job/etcd/etcd.go +++ b/pkg/registry/job/etcd/etcd.go @@ -35,7 +35,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against Jobs. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) { - prefix := "/jobs" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &batch.JobList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/limitrange/etcd/etcd.go b/pkg/registry/limitrange/etcd/etcd.go index 9765e48cdf..4aac57ca1d 100644 --- a/pkg/registry/limitrange/etcd/etcd.go +++ b/pkg/registry/limitrange/etcd/etcd.go @@ -32,7 +32,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against horizontal pod autoscalers. func NewREST(opts generic.RESTOptions) *REST { - prefix := "/limitranges" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &api.LimitRangeList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/namespace/etcd/etcd.go b/pkg/registry/namespace/etcd/etcd.go index 4d722231c8..eaf1293cee 100644 --- a/pkg/registry/namespace/etcd/etcd.go +++ b/pkg/registry/namespace/etcd/etcd.go @@ -50,7 +50,7 @@ type FinalizeREST struct { // NewREST returns a RESTStorage object that will work against namespaces. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST, *FinalizeREST) { - prefix := "/namespaces" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &api.NamespaceList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/namespace/etcd/etcd_test.go b/pkg/registry/namespace/etcd/etcd_test.go index 27d01d93ed..a6953426b9 100644 --- a/pkg/registry/namespace/etcd/etcd_test.go +++ b/pkg/registry/namespace/etcd/etcd_test.go @@ -31,7 +31,7 @@ import ( func newStorage(t *testing.T) (*REST, *etcdtesting.EtcdTestServer) { etcdStorage, server := registrytest.NewEtcdStorage(t, "") - restOptions := generic.RESTOptions{Storage: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1} + restOptions := generic.RESTOptions{Storage: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1, ResourcePrefix: "namespaces"} namespaceStorage, _, _ := NewREST(restOptions) return namespaceStorage, server } diff --git a/pkg/registry/networkpolicy/etcd/etcd.go b/pkg/registry/networkpolicy/etcd/etcd.go index acc8818c32..fc33c9283c 100644 --- a/pkg/registry/networkpolicy/etcd/etcd.go +++ b/pkg/registry/networkpolicy/etcd/etcd.go @@ -34,7 +34,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against network policies. func NewREST(opts generic.RESTOptions) *REST { - prefix := "/networkpolicies" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &extensionsapi.NetworkPolicyList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/node/etcd/etcd.go b/pkg/registry/node/etcd/etcd.go index ce1472aa9f..e360e8a741 100644 --- a/pkg/registry/node/etcd/etcd.go +++ b/pkg/registry/node/etcd/etcd.go @@ -66,7 +66,7 @@ func (r *StatusREST) Update(ctx api.Context, name string, objInfo rest.UpdatedOb // NewStorage returns a NodeStorage object that will work against nodes. func NewStorage(opts generic.RESTOptions, connection client.ConnectionInfoGetter, proxyTransport http.RoundTripper) NodeStorage { - prefix := "/minions" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &api.NodeList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/persistentvolume/etcd/etcd.go b/pkg/registry/persistentvolume/etcd/etcd.go index bdc7361185..217563c609 100644 --- a/pkg/registry/persistentvolume/etcd/etcd.go +++ b/pkg/registry/persistentvolume/etcd/etcd.go @@ -33,7 +33,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against persistent volumes. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) { - prefix := "/persistentvolumes" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &api.PersistentVolumeList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/persistentvolumeclaim/etcd/etcd.go b/pkg/registry/persistentvolumeclaim/etcd/etcd.go index e78c0c83f2..b7cf36c3cb 100644 --- a/pkg/registry/persistentvolumeclaim/etcd/etcd.go +++ b/pkg/registry/persistentvolumeclaim/etcd/etcd.go @@ -33,7 +33,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against persistent volume claims. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) { - prefix := "/persistentvolumeclaims" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &api.PersistentVolumeClaimList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/petset/etcd/etcd.go b/pkg/registry/petset/etcd/etcd.go index bec07543a3..f3ca06d72a 100644 --- a/pkg/registry/petset/etcd/etcd.go +++ b/pkg/registry/petset/etcd/etcd.go @@ -35,7 +35,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against replication controllers. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) { - prefix := "/petsets" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &appsapi.PetSetList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/petset/etcd/etcd_test.go b/pkg/registry/petset/etcd/etcd_test.go index 963d97e2d5..b892550f48 100644 --- a/pkg/registry/petset/etcd/etcd_test.go +++ b/pkg/registry/petset/etcd/etcd_test.go @@ -33,7 +33,7 @@ import ( func newStorage(t *testing.T) (*REST, *StatusREST, *etcdtesting.EtcdTestServer) { etcdStorage, server := registrytest.NewEtcdStorage(t, apps.GroupName) - restOptions := generic.RESTOptions{Storage: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1} + restOptions := generic.RESTOptions{Storage: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1, ResourcePrefix: "petsets"} petSetStorage, statusStorage := NewREST(restOptions) return petSetStorage, statusStorage, server } diff --git a/pkg/registry/pod/etcd/etcd.go b/pkg/registry/pod/etcd/etcd.go index 98599594ad..fbc201b74d 100644 --- a/pkg/registry/pod/etcd/etcd.go +++ b/pkg/registry/pod/etcd/etcd.go @@ -57,7 +57,7 @@ type REST struct { // NewStorage returns a RESTStorage object that will work against pods. func NewStorage(opts generic.RESTOptions, k client.ConnectionInfoGetter, proxyTransport http.RoundTripper) PodStorage { - prefix := "/pods" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &api.PodList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/poddisruptionbudget/etcd/etcd.go b/pkg/registry/poddisruptionbudget/etcd/etcd.go index 3fd434828a..3151f7229d 100644 --- a/pkg/registry/poddisruptionbudget/etcd/etcd.go +++ b/pkg/registry/poddisruptionbudget/etcd/etcd.go @@ -35,7 +35,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against pod disruption budgets. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) { - prefix := "/poddisruptionbudgets" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &policyapi.PodDisruptionBudgetList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/poddisruptionbudget/etcd/etcd_test.go b/pkg/registry/poddisruptionbudget/etcd/etcd_test.go index d63c0ff3a8..56c3b67e31 100644 --- a/pkg/registry/poddisruptionbudget/etcd/etcd_test.go +++ b/pkg/registry/poddisruptionbudget/etcd/etcd_test.go @@ -34,7 +34,7 @@ import ( func newStorage(t *testing.T) (*REST, *StatusREST, *etcdtesting.EtcdTestServer) { etcdStorage, server := registrytest.NewEtcdStorage(t, policy.GroupName) - restOptions := generic.RESTOptions{Storage: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1} + restOptions := generic.RESTOptions{Storage: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1, ResourcePrefix: "poddisruptionbudgets"} podDisruptionBudgetStorage, statusStorage := NewREST(restOptions) return podDisruptionBudgetStorage, statusStorage, server } diff --git a/pkg/registry/podsecuritypolicy/etcd/etcd.go b/pkg/registry/podsecuritypolicy/etcd/etcd.go index dbabceb7a2..33ed738cec 100644 --- a/pkg/registry/podsecuritypolicy/etcd/etcd.go +++ b/pkg/registry/podsecuritypolicy/etcd/etcd.go @@ -31,16 +31,16 @@ type REST struct { *registry.Store } -const Prefix = "/podsecuritypolicies" - // NewREST returns a RESTStorage object that will work against PodSecurityPolicy objects. func NewREST(opts generic.RESTOptions) *REST { + prefix := "/" + opts.ResourcePrefix + newListFunc := func() runtime.Object { return &extensions.PodSecurityPolicyList{} } storageInterface := opts.Decorator( opts.Storage, 100, &extensions.PodSecurityPolicy{}, - Prefix, + prefix, podsecuritypolicy.Strategy, newListFunc, storage.NoTriggerPublisher, @@ -50,10 +50,10 @@ func NewREST(opts generic.RESTOptions) *REST { NewFunc: func() runtime.Object { return &extensions.PodSecurityPolicy{} }, NewListFunc: newListFunc, KeyRootFunc: func(ctx api.Context) string { - return Prefix + return prefix }, KeyFunc: func(ctx api.Context, name string) (string, error) { - return registry.NoNamespaceKeyFunc(ctx, Prefix, name) + return registry.NoNamespaceKeyFunc(ctx, prefix, name) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*extensions.PodSecurityPolicy).Name, nil diff --git a/pkg/registry/podtemplate/etcd/etcd.go b/pkg/registry/podtemplate/etcd/etcd.go index b3e29eee55..b863df24f9 100644 --- a/pkg/registry/podtemplate/etcd/etcd.go +++ b/pkg/registry/podtemplate/etcd/etcd.go @@ -32,7 +32,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against pod templates. func NewREST(opts generic.RESTOptions) *REST { - prefix := "/podtemplates" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &api.PodTemplateList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/replicaset/etcd/etcd.go b/pkg/registry/replicaset/etcd/etcd.go index 46ba79bab1..ab5e744951 100644 --- a/pkg/registry/replicaset/etcd/etcd.go +++ b/pkg/registry/replicaset/etcd/etcd.go @@ -58,7 +58,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against ReplicaSet. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) { - prefix := "/replicasets" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &extensions.ReplicaSetList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/replicaset/etcd/etcd_test.go b/pkg/registry/replicaset/etcd/etcd_test.go index a325a0b31b..671d01cd02 100644 --- a/pkg/registry/replicaset/etcd/etcd_test.go +++ b/pkg/registry/replicaset/etcd/etcd_test.go @@ -38,7 +38,7 @@ const defaultReplicas = 100 func newStorage(t *testing.T) (*ReplicaSetStorage, *etcdtesting.EtcdTestServer) { etcdStorage, server := registrytest.NewEtcdStorage(t, "extensions") - restOptions := generic.RESTOptions{Storage: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1} + restOptions := generic.RESTOptions{Storage: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1, ResourcePrefix: "replicasets"} replicaSetStorage := NewStorage(restOptions) return &replicaSetStorage, server } diff --git a/pkg/registry/resourcequota/etcd/etcd.go b/pkg/registry/resourcequota/etcd/etcd.go index d635749a51..d78ab27ba8 100644 --- a/pkg/registry/resourcequota/etcd/etcd.go +++ b/pkg/registry/resourcequota/etcd/etcd.go @@ -33,7 +33,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against resource quotas. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) { - prefix := "/resourcequotas" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &api.ResourceQuotaList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/role/etcd/etcd.go b/pkg/registry/role/etcd/etcd.go index af9a427952..e5e13a3e69 100644 --- a/pkg/registry/role/etcd/etcd.go +++ b/pkg/registry/role/etcd/etcd.go @@ -34,7 +34,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against Role objects. func NewREST(opts generic.RESTOptions) *REST { - prefix := "/roles" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &rbac.RoleList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/rolebinding/etcd/etcd.go b/pkg/registry/rolebinding/etcd/etcd.go index 5383bc1136..01d913a737 100644 --- a/pkg/registry/rolebinding/etcd/etcd.go +++ b/pkg/registry/rolebinding/etcd/etcd.go @@ -34,7 +34,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against RoleBinding objects. func NewREST(opts generic.RESTOptions) *REST { - prefix := "/rolebindings" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &rbac.RoleBindingList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/scheduledjob/etcd/etcd.go b/pkg/registry/scheduledjob/etcd/etcd.go index 4f6eb37539..9c66b4c70d 100644 --- a/pkg/registry/scheduledjob/etcd/etcd.go +++ b/pkg/registry/scheduledjob/etcd/etcd.go @@ -35,7 +35,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against ScheduledJobs. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) { - prefix := "/scheduledjobs" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &batch.ScheduledJobList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/secret/etcd/etcd.go b/pkg/registry/secret/etcd/etcd.go index d191457e67..c1a8876005 100644 --- a/pkg/registry/secret/etcd/etcd.go +++ b/pkg/registry/secret/etcd/etcd.go @@ -32,7 +32,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against secrets. func NewREST(opts generic.RESTOptions) *REST { - prefix := "/secrets" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &api.SecretList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/service/etcd/etcd.go b/pkg/registry/service/etcd/etcd.go index e8b24fb366..cb532eb769 100644 --- a/pkg/registry/service/etcd/etcd.go +++ b/pkg/registry/service/etcd/etcd.go @@ -33,7 +33,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against services. func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) { - prefix := "/services/specs" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &api.ServiceList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/serviceaccount/etcd/etcd.go b/pkg/registry/serviceaccount/etcd/etcd.go index d2cb94aa6f..4f595cc1bc 100644 --- a/pkg/registry/serviceaccount/etcd/etcd.go +++ b/pkg/registry/serviceaccount/etcd/etcd.go @@ -32,7 +32,7 @@ type REST struct { // NewREST returns a RESTStorage object that will work against service accounts. func NewREST(opts generic.RESTOptions) *REST { - prefix := "/serviceaccounts" + prefix := "/" + opts.ResourcePrefix newListFunc := func() runtime.Object { return &api.ServiceAccountList{} } storageInterface := opts.Decorator( diff --git a/pkg/registry/thirdpartyresource/etcd/etcd.go b/pkg/registry/thirdpartyresource/etcd/etcd.go index 6ea315e18b..b59531b858 100644 --- a/pkg/registry/thirdpartyresource/etcd/etcd.go +++ b/pkg/registry/thirdpartyresource/etcd/etcd.go @@ -32,7 +32,7 @@ type REST struct { // NewREST returns a registry which will store ThirdPartyResource in the given helper func NewREST(opts generic.RESTOptions) *REST { - prefix := "/thirdpartyresources" + prefix := "/" + opts.ResourcePrefix // We explicitly do NOT do any decoration here yet. storageInterface := opts.Storage diff --git a/test/integration/auth/rbac_test.go b/test/integration/auth/rbac_test.go index 60abc4ded9..7ac6b23cf8 100644 --- a/test/integration/auth/rbac_test.go +++ b/test/integration/auth/rbac_test.go @@ -81,7 +81,7 @@ func newRBACAuthorizer(t *testing.T, superUser string, config *master.Config) au if err != nil { t.Fatalf("failed to get storage: %v", err) } - return generic.RESTOptions{Storage: storageInterface, Decorator: generic.UndecoratedStorage} + return generic.RESTOptions{Storage: storageInterface, Decorator: generic.UndecoratedStorage, ResourcePrefix: resource} } roleRegistry := role.NewRegistry(roleetcd.NewREST(newRESTOptions("roles")))