Merge pull request #48498 from deads2k/tpr-18-delete-02

Automatic merge from submit-queue (batch tested with PRs 45467, 48091, 48033, 48498)

bulk delete of tpr packages

related to https://github.com/kubernetes/kubernetes/issues/48152

Bulk delete of the TPR code.  I made the minimal changes outside the delete to try to keep it easy to review.
pull/6/head
Kubernetes Submit Queue 2017-07-05 12:37:42 -07:00 committed by GitHub
commit 96d8ab725b
29 changed files with 3 additions and 2989 deletions

View File

@ -238,8 +238,6 @@ pkg/registry/core/service/ipallocator/controller
pkg/registry/core/service/ipallocator/storage
pkg/registry/core/serviceaccount
pkg/registry/extensions/podsecuritypolicy/storage
pkg/registry/extensions/thirdpartyresource
pkg/registry/extensions/thirdpartyresource/storage
pkg/registry/rbac/clusterrole/storage
pkg/registry/rbac/clusterrolebinding/storage
pkg/registry/rbac/role/storage

View File

@ -43,7 +43,6 @@ go_library(
"//pkg/client/clientset_generated/clientset/typed/core/v1:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
"//pkg/kubelet/client:go_default_library",
"//pkg/master/thirdparty:go_default_library",
"//pkg/master/tunneler:go_default_library",
"//pkg/registry/admissionregistration/rest:go_default_library",
"//pkg/registry/apps/rest:go_default_library",

View File

@ -54,7 +54,6 @@ import (
corev1client "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
"k8s.io/kubernetes/pkg/master/thirdparty"
"k8s.io/kubernetes/pkg/master/tunneler"
"k8s.io/kubernetes/pkg/routes"
nodeutil "k8s.io/kubernetes/pkg/util/node"
@ -259,7 +258,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget,
batchrest.RESTStorageProvider{},
certificatesrest.RESTStorageProvider{},
// TODO(enisoc): Remove crdRESTOptionsGetter input argument when TPR code is removed.
extensionsrest.RESTStorageProvider{ResourceInterface: thirdparty.NewThirdPartyResourceServer(s, s.DiscoveryGroupManager, c.StorageFactory, crdRESTOptionsGetter)},
extensionsrest.RESTStorageProvider{},
networkingrest.RESTStorageProvider{},
policyrest.RESTStorageProvider{},
rbacrest.RESTStorageProvider{Authorizer: c.GenericConfig.Authorizer},

View File

@ -10,44 +10,19 @@ load(
go_library(
name = "go_default_library",
srcs = [
"thirdparty.go",
"tprregistration_controller.go",
],
srcs = ["tprregistration_controller.go"],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/registry/extensions/rest:go_default_library",
"//pkg/registry/extensions/thirdpartyresourcedata:go_default_library",
"//pkg/registry/extensions/thirdpartyresourcedata/storage:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
"//vendor/k8s.io/apiextensions-apiserver/pkg/apiserver:go_default_library",
"//vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/internalclientset/typed/apiextensions/internalversion:go_default_library",
"//vendor/k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/apiextensions/internalversion:go_default_library",
"//vendor/k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion:go_default_library",
"//vendor/k8s.io/apiextensions-apiserver/pkg/registry/customresource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/discovery:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/storage:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/storagebackend:go_default_library",
"//vendor/k8s.io/client-go/discovery:go_default_library",
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
"//vendor/k8s.io/client-go/util/workqueue:go_default_library",
"//vendor/k8s.io/kube-aggregator/pkg/apis/apiregistration:go_default_library",

View File

@ -1,462 +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 thirdparty
import (
"fmt"
"strings"
"sync"
"github.com/golang/glog"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
apiextensionsserver "k8s.io/apiextensions-apiserver/pkg/apiserver"
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/internalclientset/typed/apiextensions/internalversion"
"k8s.io/apiextensions-apiserver/pkg/registry/customresource"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/json"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
genericapi "k8s.io/apiserver/pkg/endpoints"
"k8s.io/apiserver/pkg/endpoints/discovery"
"k8s.io/apiserver/pkg/endpoints/request"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
serverstorgage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/apiserver/pkg/storage/storagebackend"
discoveryclient "k8s.io/client-go/discovery"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
extensionsrest "k8s.io/kubernetes/pkg/registry/extensions/rest"
"k8s.io/kubernetes/pkg/registry/extensions/thirdpartyresourcedata"
thirdpartyresourcedatastore "k8s.io/kubernetes/pkg/registry/extensions/thirdpartyresourcedata/storage"
)
// dynamicLister is used to list resources for dynamic third party
// apis. It implements the genericapihandlers.APIResourceLister interface
type dynamicLister struct {
m *ThirdPartyResourceServer
path string
}
func (d dynamicLister) ListAPIResources() []metav1.APIResource {
return d.m.getExistingThirdPartyResources(d.path)
}
var _ discovery.APIResourceLister = &dynamicLister{}
type ThirdPartyResourceServer struct {
genericAPIServer *genericapiserver.GenericAPIServer
availableGroupManager discovery.GroupManager
deleteCollectionWorkers int
// storage for third party objects
thirdPartyStorageConfig *storagebackend.Config
// map from api path to a tuple of (storage for the objects, APIGroup)
thirdPartyResources map[string]*thirdPartyEntry
// protects the map
thirdPartyResourcesLock sync.RWMutex
// Useful for reliable testing. Shouldn't be used otherwise.
disableThirdPartyControllerForTesting bool
crdRESTOptionsGetter generic.RESTOptionsGetter
}
func NewThirdPartyResourceServer(genericAPIServer *genericapiserver.GenericAPIServer, availableGroupManager discovery.GroupManager, storageFactory serverstorgage.StorageFactory, crdRESTOptionsGetter generic.RESTOptionsGetter) *ThirdPartyResourceServer {
ret := &ThirdPartyResourceServer{
genericAPIServer: genericAPIServer,
thirdPartyResources: map[string]*thirdPartyEntry{},
availableGroupManager: availableGroupManager,
crdRESTOptionsGetter: crdRESTOptionsGetter,
}
var err error
ret.thirdPartyStorageConfig, err = storageFactory.NewConfig(extensions.Resource("thirdpartyresources"))
if err != nil {
glog.Fatalf("Error building third party storage: %v", err)
}
return ret
}
// thirdPartyEntry combines objects storage and API group into one struct
// for easy lookup.
type thirdPartyEntry struct {
// Map from plural resource name to entry
storage map[string]*thirdpartyresourcedatastore.REST
group metav1.APIGroup
}
// HasThirdPartyResource returns true if a particular third party resource currently installed.
func (m *ThirdPartyResourceServer) HasThirdPartyResource(rsrc *extensions.ThirdPartyResource) (bool, error) {
kind, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(rsrc)
if err != nil {
return false, err
}
path := extensionsrest.MakeThirdPartyPath(group)
m.thirdPartyResourcesLock.Lock()
defer m.thirdPartyResourcesLock.Unlock()
entry := m.thirdPartyResources[path]
if entry == nil {
return false, nil
}
plural, _ := meta.UnsafeGuessKindToResource(schema.GroupVersionKind{
Group: group,
Version: rsrc.Versions[0].Name,
Kind: kind,
})
_, found := entry.storage[plural.Resource]
return found, nil
}
func (m *ThirdPartyResourceServer) removeThirdPartyStorage(path, resource string) error {
m.thirdPartyResourcesLock.Lock()
defer m.thirdPartyResourcesLock.Unlock()
entry, found := m.thirdPartyResources[path]
if !found {
return nil
}
storage, found := entry.storage[resource]
if !found {
return nil
}
if err := m.removeThirdPartyResourceData(&entry.group, resource, storage); err != nil {
return err
}
delete(entry.storage, resource)
if len(entry.storage) == 0 {
delete(m.thirdPartyResources, path)
m.availableGroupManager.RemoveGroup(extensionsrest.GetThirdPartyGroupName(path))
} else {
m.thirdPartyResources[path] = entry
}
return nil
}
// RemoveThirdPartyResource removes all resources matching `path`. Also deletes any stored data
func (m *ThirdPartyResourceServer) RemoveThirdPartyResource(path string) error {
ix := strings.LastIndex(path, "/")
if ix == -1 {
return fmt.Errorf("expected <api-group>/<resource-plural-name>, saw: %s", path)
}
resource := path[ix+1:]
path = path[0:ix]
if err := m.removeThirdPartyStorage(path, resource); err != nil {
return err
}
services := m.genericAPIServer.Handler.GoRestfulContainer.RegisteredWebServices()
for ix := range services {
root := services[ix].RootPath()
if root == path || strings.HasPrefix(root, path+"/") {
m.genericAPIServer.Handler.GoRestfulContainer.Remove(services[ix])
}
}
return nil
}
func (m *ThirdPartyResourceServer) removeThirdPartyResourceData(group *metav1.APIGroup, resource string, registry *thirdpartyresourcedatastore.REST) error {
// Freeze TPR data to prevent new writes via this apiserver process.
// Other apiservers can still write. This is best-effort because there
// are worse problems with TPR data than the possibility of going back
// in time when migrating to CRD [citation needed].
registry.Freeze()
ctx := genericapirequest.NewContext()
existingData, err := registry.List(ctx, nil)
if err != nil {
return err
}
list, ok := existingData.(*extensions.ThirdPartyResourceDataList)
if !ok {
return fmt.Errorf("expected a *ThirdPartyResourceDataList, got %T", existingData)
}
// Migrate TPR data to CRD if requested.
gvk := schema.GroupVersionKind{Group: group.Name, Version: group.PreferredVersion.Version, Kind: registry.Kind()}
migrationRequested, err := m.migrateThirdPartyResourceData(gvk, resource, list)
if err != nil {
// Migration is best-effort. Log and continue.
utilruntime.HandleError(fmt.Errorf("failed to migrate TPR data: %v", err))
}
// Skip deletion of TPR data if migration was requested (whether or not it succeeded).
// This leaves the etcd data around for rollback, and to avoid sending DELETE watch events.
if migrationRequested {
return nil
}
for i := range list.Items {
item := &list.Items[i]
// Use registry.Store.Delete() to bypass the frozen registry.Delete().
if _, _, err := registry.Store.Delete(genericapirequest.WithNamespace(ctx, item.Namespace), item.Name, nil); err != nil {
return err
}
}
return nil
}
func (m *ThirdPartyResourceServer) findMatchingCRD(gvk schema.GroupVersionKind, resource string) (*apiextensions.CustomResourceDefinition, error) {
// CustomResourceDefinitionList does not implement the protobuf marshalling interface.
config := *m.genericAPIServer.LoopbackClientConfig
config.ContentType = "application/json"
crdClient, err := apiextensionsclient.NewForConfig(&config)
if err != nil {
return nil, fmt.Errorf("can't create apiextensions client: %v", err)
}
crdList, err := crdClient.CustomResourceDefinitions().List(metav1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("can't list CustomResourceDefinitions: %v", err)
}
for i := range crdList.Items {
item := &crdList.Items[i]
if item.Spec.Scope == apiextensions.NamespaceScoped &&
item.Spec.Group == gvk.Group && item.Spec.Version == gvk.Version &&
item.Status.AcceptedNames.Kind == gvk.Kind && item.Status.AcceptedNames.Plural == resource {
return item, nil
}
}
return nil, nil
}
func (m *ThirdPartyResourceServer) migrateThirdPartyResourceData(gvk schema.GroupVersionKind, resource string, dataList *extensions.ThirdPartyResourceDataList) (bool, error) {
// A matching CustomResourceDefinition implies migration is requested.
crd, err := m.findMatchingCRD(gvk, resource)
if err != nil {
return false, fmt.Errorf("can't determine if TPR should migrate: %v", err)
}
if crd == nil {
// No migration requested.
return false, nil
}
// Talk directly to CustomResource storage.
// We have to bypass the API server because TPR is shadowing CRD at this point.
storage := customresource.NewREST(
schema.GroupResource{Group: crd.Spec.Group, Resource: crd.Spec.Names.Plural},
schema.GroupVersionKind{Group: crd.Spec.Group, Version: crd.Spec.Version, Kind: crd.Spec.Names.ListKind},
apiextensionsserver.UnstructuredCopier{},
customresource.NewStrategy(discoveryclient.NewUnstructuredObjectTyper(nil), true, gvk),
m.crdRESTOptionsGetter,
)
// Copy TPR data to CustomResource.
var errs []error
ctx := request.NewContext()
for i := range dataList.Items {
item := &dataList.Items[i]
// Convert TPR data to Unstructured.
objMap := make(map[string]interface{})
if err := json.Unmarshal(item.Data, &objMap); err != nil {
errs = append(errs, fmt.Errorf("can't unmarshal TPR data %q: %v", item.Name, err))
continue
}
// Convert metadata to Unstructured and merge with data.
// cf. thirdpartyresourcedata.encodeToJSON()
metaMap := make(map[string]interface{})
buf, err := json.Marshal(&item.ObjectMeta)
if err != nil {
errs = append(errs, fmt.Errorf("can't marshal metadata for TPR data %q: %v", item.Name, err))
continue
}
if err := json.Unmarshal(buf, &metaMap); err != nil {
errs = append(errs, fmt.Errorf("can't unmarshal TPR data %q: %v", item.Name, err))
continue
}
// resourceVersion cannot be set when creating objects.
delete(metaMap, "resourceVersion")
objMap["metadata"] = metaMap
// Store CustomResource.
obj := &unstructured.Unstructured{Object: objMap}
createCtx := request.WithNamespace(ctx, obj.GetNamespace())
if _, err := storage.Create(createCtx, obj, false); err != nil {
errs = append(errs, fmt.Errorf("can't create CustomResource for TPR data %q: %v", item.Name, err))
continue
}
}
return true, utilerrors.NewAggregate(errs)
}
// ListThirdPartyResources lists all currently installed third party resources
// The format is <path>/<resource-plural-name>
func (m *ThirdPartyResourceServer) ListThirdPartyResources() []string {
m.thirdPartyResourcesLock.RLock()
defer m.thirdPartyResourcesLock.RUnlock()
result := []string{}
for key := range m.thirdPartyResources {
for rsrc := range m.thirdPartyResources[key].storage {
result = append(result, key+"/"+rsrc)
}
}
return result
}
func (m *ThirdPartyResourceServer) getExistingThirdPartyResources(path string) []metav1.APIResource {
result := []metav1.APIResource{}
m.thirdPartyResourcesLock.Lock()
defer m.thirdPartyResourcesLock.Unlock()
entry := m.thirdPartyResources[path]
if entry != nil {
for key, obj := range entry.storage {
result = append(result, metav1.APIResource{
Name: key,
Namespaced: true,
Kind: obj.Kind(),
Verbs: metav1.Verbs([]string{
"delete", "deletecollection", "get", "list", "patch", "create", "update", "watch",
}),
})
}
}
return result
}
func (m *ThirdPartyResourceServer) hasThirdPartyGroupStorage(path string) bool {
m.thirdPartyResourcesLock.Lock()
defer m.thirdPartyResourcesLock.Unlock()
_, found := m.thirdPartyResources[path]
return found
}
func (m *ThirdPartyResourceServer) addThirdPartyResourceStorage(path, resource string, storage *thirdpartyresourcedatastore.REST, apiGroup metav1.APIGroup) {
m.thirdPartyResourcesLock.Lock()
defer m.thirdPartyResourcesLock.Unlock()
entry, found := m.thirdPartyResources[path]
if entry == nil {
entry = &thirdPartyEntry{
group: apiGroup,
storage: map[string]*thirdpartyresourcedatastore.REST{},
}
m.thirdPartyResources[path] = entry
}
entry.storage[resource] = storage
if !found {
m.availableGroupManager.AddGroup(apiGroup)
}
}
// InstallThirdPartyResource installs a third party resource specified by 'rsrc'. When a resource is
// installed a corresponding RESTful resource is added as a valid path in the web service provided by
// the master.
//
// For example, if you install a resource ThirdPartyResource{ Name: "foo.company.com", Versions: {"v1"} }
// then the following RESTful resource is created on the server:
// http://<host>/apis/company.com/v1/foos/...
func (m *ThirdPartyResourceServer) InstallThirdPartyResource(rsrc *extensions.ThirdPartyResource) error {
kind, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(rsrc)
if err != nil {
return err
}
if len(rsrc.Versions) == 0 {
return fmt.Errorf("ThirdPartyResource %s has no defined versions", rsrc.Name)
}
plural, _ := meta.UnsafeGuessKindToResource(schema.GroupVersionKind{
Group: group,
Version: rsrc.Versions[0].Name,
Kind: kind,
})
path := extensionsrest.MakeThirdPartyPath(group)
groupVersion := metav1.GroupVersionForDiscovery{
GroupVersion: group + "/" + rsrc.Versions[0].Name,
Version: rsrc.Versions[0].Name,
}
apiGroup := metav1.APIGroup{
Name: group,
Versions: []metav1.GroupVersionForDiscovery{groupVersion},
PreferredVersion: groupVersion,
}
thirdparty := m.thirdpartyapi(group, kind, rsrc.Versions[0].Name, plural.Resource)
// If storage exists, this group has already been added, just update
// the group with the new API
if m.hasThirdPartyGroupStorage(path) {
m.addThirdPartyResourceStorage(path, plural.Resource, thirdparty.Storage[plural.Resource].(*thirdpartyresourcedatastore.REST), apiGroup)
return thirdparty.UpdateREST(m.genericAPIServer.Handler.GoRestfulContainer)
}
if err := thirdparty.InstallREST(m.genericAPIServer.Handler.GoRestfulContainer); err != nil {
glog.Errorf("Unable to setup thirdparty api: %v", err)
}
m.genericAPIServer.Handler.GoRestfulContainer.Add(discovery.NewAPIGroupHandler(api.Codecs, apiGroup, m.genericAPIServer.RequestContextMapper()).WebService())
m.addThirdPartyResourceStorage(path, plural.Resource, thirdparty.Storage[plural.Resource].(*thirdpartyresourcedatastore.REST), apiGroup)
api.Registry.AddThirdPartyAPIGroupVersions(schema.GroupVersion{Group: group, Version: rsrc.Versions[0].Name})
return nil
}
func (m *ThirdPartyResourceServer) thirdpartyapi(group, kind, version, pluralResource string) *genericapi.APIGroupVersion {
resourceStorage := thirdpartyresourcedatastore.NewREST(
generic.RESTOptions{
StorageConfig: m.thirdPartyStorageConfig,
Decorator: generic.UndecoratedStorage,
DeleteCollectionWorkers: m.deleteCollectionWorkers,
},
group,
kind,
)
storage := map[string]rest.Storage{
pluralResource: resourceStorage,
}
optionsExternalVersion := api.Registry.GroupOrDie(api.GroupName).GroupVersion
internalVersion := schema.GroupVersion{Group: group, Version: runtime.APIVersionInternal}
externalVersion := schema.GroupVersion{Group: group, Version: version}
apiRoot := extensionsrest.MakeThirdPartyPath("")
return &genericapi.APIGroupVersion{
Root: apiRoot,
GroupVersion: externalVersion,
Creater: thirdpartyresourcedata.NewObjectCreator(group, version, api.Scheme),
Convertor: api.Scheme,
Copier: api.Scheme,
Defaulter: api.Scheme,
Typer: api.Scheme,
UnsafeConvertor: api.Scheme,
Mapper: thirdpartyresourcedata.NewMapper(api.Registry.GroupOrDie(extensions.GroupName).RESTMapper, kind, version, group),
Linker: api.Registry.GroupOrDie(extensions.GroupName).SelfLinker,
Storage: storage,
OptionsExternalVersion: &optionsExternalVersion,
Serializer: thirdpartyresourcedata.NewNegotiatedSerializer(api.Codecs, kind, externalVersion, internalVersion),
ParameterCodec: thirdpartyresourcedata.NewThirdPartyParameterCodec(api.ParameterCodec),
Context: m.genericAPIServer.RequestContextMapper(),
MinRequestTimeout: m.genericAPIServer.MinRequestTimeout(),
ResourceLister: dynamicLister{m, extensionsrest.MakeThirdPartyPath(group)},
}
}

View File

@ -71,8 +71,6 @@ filegroup(
"//pkg/registry/extensions/podsecuritypolicy:all-srcs",
"//pkg/registry/extensions/replicaset:all-srcs",
"//pkg/registry/extensions/rest:all-srcs",
"//pkg/registry/extensions/thirdpartyresource:all-srcs",
"//pkg/registry/extensions/thirdpartyresourcedata:all-srcs",
"//pkg/registry/networking/networkpolicy:all-srcs",
"//pkg/registry/networking/rest:all-srcs",
"//pkg/registry/policy/poddisruptionbudget:all-srcs",

View File

@ -5,33 +5,15 @@ licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["thirdparty_controller_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/apis/extensions:go_default_library",
"//pkg/registry/extensions/thirdpartyresourcedata:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"storage_extensions.go",
"thirdparty_controller.go",
],
srcs = ["storage_extensions.go"],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library",
"//pkg/registry/extensions/controller/storage:go_default_library",
"//pkg/registry/extensions/daemonset/storage:go_default_library",
"//pkg/registry/extensions/deployment/storage:go_default_library",
@ -39,11 +21,7 @@ go_library(
"//pkg/registry/extensions/networkpolicy/storage:go_default_library",
"//pkg/registry/extensions/podsecuritypolicy/storage:go_default_library",
"//pkg/registry/extensions/replicaset/storage:go_default_library",
"//pkg/registry/extensions/thirdpartyresourcedata:go_default_library",
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",

View File

@ -34,7 +34,6 @@ import (
)
type RESTStorageProvider struct {
ResourceInterface ResourceInterface
}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {

View File

@ -1,131 +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 rest
import (
"fmt"
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/apis/extensions"
extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion"
"k8s.io/kubernetes/pkg/registry/extensions/thirdpartyresourcedata"
)
// ResourceInterface is the interface for the parts of the master that know how to add/remove
// third party resources. Extracted into an interface for injection for testing.
type ResourceInterface interface {
// Remove a third party resource based on the RESTful path for that resource, the path is <api-group-path>/<resource-plural-name>
RemoveThirdPartyResource(path string) error
// Install a third party resource described by 'rsrc'
InstallThirdPartyResource(rsrc *extensions.ThirdPartyResource) error
// Is a particular third party resource currently installed?
HasThirdPartyResource(rsrc *extensions.ThirdPartyResource) (bool, error)
// List all currently installed third party resources, the returned
// names are of the form <api-group-path>/<resource-plural-name>
ListThirdPartyResources() []string
}
const thirdpartyprefix = "/apis"
// ThirdPartyController is a control loop that knows how to synchronize ThirdPartyResource objects with
// RESTful resources which are present in the API server.
type ThirdPartyController struct {
master ResourceInterface
client extensionsclient.ThirdPartyResourcesGetter
}
// SyncOneResource synchronizes a single resource with RESTful resources on the master
func (t *ThirdPartyController) SyncOneResource(rsrc *extensions.ThirdPartyResource) error {
// TODO: we also need to test if the existing installed resource matches the resource we are sync-ing.
// Currently, if there is an older, incompatible resource installed, we won't remove it. We should detect
// older, incompatible resources and remove them before testing if the resource exists.
hasResource, err := t.master.HasThirdPartyResource(rsrc)
if err != nil {
return err
}
if !hasResource {
return t.master.InstallThirdPartyResource(rsrc)
}
return nil
}
// Synchronize all resources with RESTful resources on the master
func (t *ThirdPartyController) SyncResources() error {
list, err := t.client.ThirdPartyResources().List(metav1.ListOptions{})
if err != nil {
return err
}
return t.syncResourceList(list)
}
func (t *ThirdPartyController) syncResourceList(list runtime.Object) error {
existing := sets.String{}
switch list := list.(type) {
case *extensions.ThirdPartyResourceList:
// Loop across all schema objects for third party resources
for ix := range list.Items {
item := &list.Items[ix]
// extract the api group and resource kind from the schema
_, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(item)
if err != nil {
return err
}
// place it in the set of resources that we expect, so that we don't delete it in the delete pass
existing.Insert(MakeThirdPartyPath(group))
// ensure a RESTful resource for this schema exists on the master
if err := t.SyncOneResource(item); err != nil {
return err
}
}
default:
return fmt.Errorf("expected a *ThirdPartyResourceList, got %#v", list)
}
// deletion phase, get all installed RESTful resources
installed := t.master.ListThirdPartyResources()
for _, installedAPI := range installed {
found := false
// search across the expected restful resources to see if this resource belongs to one of the expected ones
for _, apiPath := range existing.List() {
if installedAPI == apiPath || strings.HasPrefix(installedAPI, apiPath+"/") {
found = true
break
}
}
// not expected, delete the resource
if !found {
if err := t.master.RemoveThirdPartyResource(installedAPI); err != nil {
return err
}
}
}
return nil
}
func MakeThirdPartyPath(group string) string {
if len(group) == 0 {
return thirdpartyprefix
}
return thirdpartyprefix + "/" + group
}
func GetThirdPartyGroupName(path string) string {
return strings.TrimPrefix(strings.TrimPrefix(path, thirdpartyprefix), "/")
}

View File

@ -1,177 +0,0 @@
/*
Copyright 2014 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 rest
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
expapi "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/registry/extensions/thirdpartyresourcedata"
)
type FakeAPIInterface struct {
removed []string
installed []*expapi.ThirdPartyResource
apis []string
t *testing.T
}
func (f *FakeAPIInterface) RemoveThirdPartyResource(path string) error {
f.removed = append(f.removed, path)
return nil
}
func (f *FakeAPIInterface) InstallThirdPartyResource(rsrc *expapi.ThirdPartyResource) error {
f.installed = append(f.installed, rsrc)
_, group, _ := thirdpartyresourcedata.ExtractApiGroupAndKind(rsrc)
f.apis = append(f.apis, MakeThirdPartyPath(group))
return nil
}
func (f *FakeAPIInterface) HasThirdPartyResource(rsrc *expapi.ThirdPartyResource) (bool, error) {
if f.apis == nil {
return false, nil
}
_, group, _ := thirdpartyresourcedata.ExtractApiGroupAndKind(rsrc)
path := MakeThirdPartyPath(group)
for _, api := range f.apis {
if api == path {
return true, nil
}
}
return false, nil
}
func (f *FakeAPIInterface) ListThirdPartyResources() []string {
return f.apis
}
func TestSyncAPIs(t *testing.T) {
resourcesNamed := func(names ...string) []expapi.ThirdPartyResource {
result := []expapi.ThirdPartyResource{}
for _, name := range names {
result = append(result, expapi.ThirdPartyResource{ObjectMeta: metav1.ObjectMeta{Name: name}})
}
return result
}
tests := []struct {
list *expapi.ThirdPartyResourceList
apis []string
expectedInstalled []string
expectedRemoved []string
name string
}{
{
list: &expapi.ThirdPartyResourceList{
Items: resourcesNamed("foo.example.com"),
},
expectedInstalled: []string{"foo.example.com"},
name: "simple add",
},
{
list: &expapi.ThirdPartyResourceList{
Items: resourcesNamed("foo.example.com"),
},
apis: []string{
"/apis/example.com",
"/apis/example.com/v1",
},
name: "does nothing",
},
{
list: &expapi.ThirdPartyResourceList{
Items: resourcesNamed("foo.example.com"),
},
apis: []string{
"/apis/example.com",
"/apis/example.com/v1",
"/apis/example.co",
"/apis/example.co/v1",
},
name: "deletes substring API",
expectedRemoved: []string{
"/apis/example.co",
"/apis/example.co/v1",
},
},
{
list: &expapi.ThirdPartyResourceList{
Items: resourcesNamed("foo.example.com", "foo.company.com"),
},
apis: []string{
"/apis/company.com",
"/apis/company.com/v1",
},
expectedInstalled: []string{"foo.example.com"},
name: "adds with existing",
},
{
list: &expapi.ThirdPartyResourceList{
Items: resourcesNamed("foo.example.com"),
},
apis: []string{
"/apis/company.com",
"/apis/company.com/v1",
},
expectedInstalled: []string{"foo.example.com"},
expectedRemoved: []string{"/apis/company.com", "/apis/company.com/v1"},
name: "removes with existing",
},
}
for _, test := range tests {
fake := FakeAPIInterface{
apis: test.apis,
t: t,
}
cntrl := ThirdPartyController{master: &fake}
if err := cntrl.syncResourceList(test.list); err != nil {
t.Errorf("[%s] unexpected error: %v", test.name, err)
}
if len(test.expectedInstalled) != len(fake.installed) {
t.Errorf("[%s] unexpected installed APIs: %d, expected %d (%#v)", test.name, len(fake.installed), len(test.expectedInstalled), fake.installed[0])
continue
} else {
names := sets.String{}
for ix := range fake.installed {
names.Insert(fake.installed[ix].Name)
}
for _, name := range test.expectedInstalled {
if !names.Has(name) {
t.Errorf("[%s] missing installed API: %s", test.name, name)
}
}
}
if len(test.expectedRemoved) != len(fake.removed) {
t.Errorf("[%s] unexpected installed APIs: %d, expected %d", test.name, len(fake.removed), len(test.expectedRemoved))
continue
} else {
names := sets.String{}
names.Insert(fake.removed...)
for _, name := range test.expectedRemoved {
if !names.Has(name) {
t.Errorf("[%s] missing removed API: %s (%s)", test.name, name, names)
}
}
}
}
}

View File

@ -1,59 +0,0 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"strategy.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/extensions/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["strategy_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/testing:go_default_library",
"//pkg/apis/extensions:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/extensions/thirdpartyresource/storage:all-srcs",
],
tags = ["automanaged"],
)

View File

@ -1,19 +0,0 @@
/*
Copyright 2015 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 thirdpartyresource provides Registry interface and its REST
// implementation for storing ThirdPartyResource api objects.
package thirdpartyresource // import "k8s.io/kubernetes/pkg/registry/extensions/thirdpartyresource"

View File

@ -1,55 +0,0 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["storage_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/apis/extensions:go_default_library",
"//pkg/registry/registrytest:go_default_library",
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/registry/cachesize:go_default_library",
"//pkg/registry/extensions/thirdpartyresource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -1,62 +0,0 @@
/*
Copyright 2015 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 storage
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/registry/cachesize"
"k8s.io/kubernetes/pkg/registry/extensions/thirdpartyresource"
)
// REST implements a RESTStorage for ThirdPartyResources
type REST struct {
*genericregistry.Store
}
// NewREST returns a registry which will store ThirdPartyResource in the given helper
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
resource := extensions.Resource("thirdpartyresources")
opts, err := optsGetter.GetRESTOptions(resource)
if err != nil {
panic(err) // TODO: Propagate error up
}
// We explicitly do NOT do any decoration here yet. // TODO determine why we do not want to cache here
opts.Decorator = generic.UndecoratedStorage
store := &genericregistry.Store{
Copier: api.Scheme,
NewFunc: func() runtime.Object { return &extensions.ThirdPartyResource{} },
NewListFunc: func() runtime.Object { return &extensions.ThirdPartyResourceList{} },
PredicateFunc: thirdpartyresource.Matcher,
QualifiedResource: resource,
WatchCacheSize: cachesize.GetWatchCacheSizeByResource(resource.Resource),
CreateStrategy: thirdpartyresource.Strategy,
UpdateStrategy: thirdpartyresource.Strategy,
DeleteStrategy: thirdpartyresource.Strategy,
}
options := &generic.StoreOptions{RESTOptions: opts, AttrFunc: thirdpartyresource.GetAttrs} // Pass in opts to use UndecoratedStorage
if err := store.CompleteWithOptions(options); err != nil {
panic(err) // TODO: Propagate error up
}
return &REST{store}
}

View File

@ -1,143 +0,0 @@
/*
Copyright 2015 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 storage
import (
"fmt"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/apis/extensions"
// Ensure that extensions/v1beta1 package is initialized.
_ "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
"k8s.io/kubernetes/pkg/registry/registrytest"
)
func newStorage(t *testing.T) (*REST, *etcdtesting.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorage(t, extensions.GroupName)
restOptions := generic.RESTOptions{
StorageConfig: etcdStorage,
Decorator: generic.UndecoratedStorage,
DeleteCollectionWorkers: 1,
ResourcePrefix: "thirdpartyresources",
}
return NewREST(restOptions), server
}
func validNewThirdPartyResource(name string) *extensions.ThirdPartyResource {
return &extensions.ThirdPartyResource{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Versions: []extensions.APIVersion{
{
Name: "v1",
},
},
}
}
func namer(i int) string {
return fmt.Sprintf("kind%d.example.com", i)
}
func TestCreate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store).ClusterScope().Namer(namer).GeneratesName()
rsrc := validNewThirdPartyResource("kind.domain.tld")
test.TestCreate(
// valid
rsrc,
// invalid
&extensions.ThirdPartyResource{},
&extensions.ThirdPartyResource{ObjectMeta: metav1.ObjectMeta{Name: "kind"}, Versions: []extensions.APIVersion{{Name: "v1"}}},
&extensions.ThirdPartyResource{ObjectMeta: metav1.ObjectMeta{Name: "kind.tld"}, Versions: []extensions.APIVersion{{Name: "v1"}}},
&extensions.ThirdPartyResource{ObjectMeta: metav1.ObjectMeta{Name: "kind.domain.tld"}, Versions: []extensions.APIVersion{{Name: "v.1"}}},
&extensions.ThirdPartyResource{ObjectMeta: metav1.ObjectMeta{Name: "kind.domain.tld"}, Versions: []extensions.APIVersion{{Name: "stable/v1"}}},
)
}
func TestUpdate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store).ClusterScope().Namer(namer)
test.TestUpdate(
// valid
validNewThirdPartyResource("kind.domain.tld"),
// updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*extensions.ThirdPartyResource)
object.Description = "new description"
return object
},
)
}
func TestDelete(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store).ClusterScope().Namer(namer)
test.TestDelete(validNewThirdPartyResource("kind.domain.tld"))
}
func TestGet(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store).ClusterScope().Namer(namer)
test.TestGet(validNewThirdPartyResource("kind.domain.tld"))
}
func TestList(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store).ClusterScope().Namer(namer)
test.TestList(validNewThirdPartyResource("kind.domain.tld"))
}
func TestWatch(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store).ClusterScope().Namer(namer)
test.TestWatch(
validNewThirdPartyResource("kind.domain.tld"),
// matching labels
[]labels.Set{},
// not matching labels
[]labels.Set{
{"foo": "bar"},
},
// matching fields
[]fields.Set{},
// not matching fields
[]fields.Set{
{"metadata.name": "bar"},
{"name": "foo"},
},
)
}

View File

@ -1,102 +0,0 @@
/*
Copyright 2015 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 thirdpartyresource
import (
"fmt"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/extensions/validation"
)
// strategy implements behavior for ThirdPartyResource objects
type strategy struct {
runtime.ObjectTyper
}
// Strategy is the default logic that applies when creating and updating ThirdPartyResource
// objects via the REST API.
var Strategy = strategy{api.Scheme}
var _ = rest.RESTCreateStrategy(Strategy)
var _ = rest.RESTUpdateStrategy(Strategy)
func (strategy) NamespaceScoped() bool {
return false
}
func (strategy) GenerateName(base string) string {
return ""
}
func (strategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
}
func (strategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
return validation.ValidateThirdPartyResource(obj.(*extensions.ThirdPartyResource))
}
// Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) {
}
func (strategy) AllowCreateOnUpdate() bool {
return false
}
func (strategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
}
func (strategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateThirdPartyResourceUpdate(obj.(*extensions.ThirdPartyResource), old.(*extensions.ThirdPartyResource))
}
func (strategy) AllowUnconditionalUpdate() bool {
return true
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) {
tpr, ok := obj.(*extensions.ThirdPartyResource)
if !ok {
return nil, nil, false, fmt.Errorf("not a ThirdPartyResource")
}
return labels.Set(tpr.Labels), SelectableFields(tpr), tpr.Initializers != nil, nil
}
// Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}
// SelectableFields returns a field set that can be used for filter selection
func SelectableFields(obj *extensions.ThirdPartyResource) fields.Set {
return nil
}

View File

@ -1,35 +0,0 @@
/*
Copyright 2015 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 thirdpartyresource
import (
"testing"
_ "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
apitesting "k8s.io/kubernetes/pkg/api/testing"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestSelectableFieldLabelConversions(t *testing.T) {
apitesting.TestSelectableFieldLabelConversionsOfKind(t,
testapi.Extensions.GroupVersion().String(),
"ThirdPartyResource",
SelectableFields(&extensions.ThirdPartyResource{}),
nil,
)
}

View File

@ -1,80 +0,0 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"codec.go",
"doc.go",
"registry.go",
"strategy.go",
"util.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/util:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/extensions/validation:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"codec_test.go",
"strategy_test.go",
"util_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/testing:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/registry/extensions/thirdpartyresourcedata/storage:all-srcs",
],
tags = ["automanaged"],
)

View File

@ -1,593 +0,0 @@
/*
Copyright 2015 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 thirdpartyresourcedata
import (
"bytes"
gojson "encoding/json"
"fmt"
"io"
"net/url"
"strings"
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/kubernetes/pkg/api"
apiutil "k8s.io/kubernetes/pkg/api/util"
"k8s.io/kubernetes/pkg/apis/extensions"
)
type thirdPartyObjectConverter struct {
converter runtime.ObjectConvertor
}
func (t *thirdPartyObjectConverter) ConvertToVersion(in runtime.Object, outVersion runtime.GroupVersioner) (out runtime.Object, err error) {
switch in.(type) {
// This seems weird, but in this case the ThirdPartyResourceData is really just a wrapper on the raw 3rd party data.
// The actual thing printed/sent to server is the actual raw third party resource data, which only has one version.
case *extensions.ThirdPartyResourceData:
return in, nil
default:
return t.converter.ConvertToVersion(in, outVersion)
}
}
func (t *thirdPartyObjectConverter) Convert(in, out, context interface{}) error {
return t.converter.Convert(in, out, context)
}
func (t *thirdPartyObjectConverter) ConvertFieldLabel(version, kind, label, value string) (string, string, error) {
return t.converter.ConvertFieldLabel(version, kind, label, value)
}
func NewThirdPartyObjectConverter(converter runtime.ObjectConvertor) runtime.ObjectConvertor {
return &thirdPartyObjectConverter{converter}
}
type thirdPartyResourceDataMapper struct {
mapper meta.RESTMapper
kind string
version string
group string
}
var _ meta.RESTMapper = &thirdPartyResourceDataMapper{}
func (t *thirdPartyResourceDataMapper) getResource() schema.GroupVersionResource {
plural, _ := meta.UnsafeGuessKindToResource(t.getKind())
return plural
}
func (t *thirdPartyResourceDataMapper) getKind() schema.GroupVersionKind {
return schema.GroupVersionKind{Group: t.group, Version: t.version, Kind: t.kind}
}
func (t *thirdPartyResourceDataMapper) isThirdPartyResource(partialResource schema.GroupVersionResource) bool {
actualResource := t.getResource()
if strings.ToLower(partialResource.Resource) != strings.ToLower(actualResource.Resource) {
return false
}
if len(partialResource.Group) != 0 && partialResource.Group != actualResource.Group {
return false
}
if len(partialResource.Version) != 0 && partialResource.Version != actualResource.Version {
return false
}
return true
}
func (t *thirdPartyResourceDataMapper) ResourcesFor(resource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
if t.isThirdPartyResource(resource) {
return []schema.GroupVersionResource{t.getResource()}, nil
}
return t.mapper.ResourcesFor(resource)
}
func (t *thirdPartyResourceDataMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
if t.isThirdPartyResource(resource) {
return []schema.GroupVersionKind{t.getKind()}, nil
}
return t.mapper.KindsFor(resource)
}
func (t *thirdPartyResourceDataMapper) ResourceFor(resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
if t.isThirdPartyResource(resource) {
return t.getResource(), nil
}
return t.mapper.ResourceFor(resource)
}
func (t *thirdPartyResourceDataMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
if t.isThirdPartyResource(resource) {
return t.getKind(), nil
}
return t.mapper.KindFor(resource)
}
func (t *thirdPartyResourceDataMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
if len(versions) != 1 {
return nil, fmt.Errorf("unexpected set of versions: %v", versions)
}
if gk.Group != t.group {
return nil, fmt.Errorf("unknown group %q expected %s", gk.Group, t.group)
}
if gk.Kind != "ThirdPartyResourceData" {
return nil, fmt.Errorf("unknown kind %s expected %s", gk.Kind, t.kind)
}
if versions[0] != t.version {
return nil, fmt.Errorf("unknown version %q expected %q", versions[0], t.version)
}
// TODO figure out why we're doing this rewriting
extensionGK := schema.GroupKind{Group: extensions.GroupName, Kind: "ThirdPartyResourceData"}
mapping, err := t.mapper.RESTMapping(extensionGK, api.Registry.GroupOrDie(extensions.GroupName).GroupVersion.Version)
if err != nil {
return nil, err
}
mapping.ObjectConvertor = &thirdPartyObjectConverter{mapping.ObjectConvertor}
return mapping, nil
}
func (t *thirdPartyResourceDataMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
if gk.Group != t.group {
return nil, fmt.Errorf("unknown group %q expected %s", gk.Group, t.group)
}
if gk.Kind != "ThirdPartyResourceData" {
return nil, fmt.Errorf("unknown kind %s expected %s", gk.Kind, t.kind)
}
// TODO figure out why we're doing this rewriting
extensionGK := schema.GroupKind{Group: extensions.GroupName, Kind: "ThirdPartyResourceData"}
mappings, err := t.mapper.RESTMappings(extensionGK, versions...)
if err != nil {
return nil, err
}
for _, m := range mappings {
m.ObjectConvertor = &thirdPartyObjectConverter{m.ObjectConvertor}
}
return mappings, nil
}
func (t *thirdPartyResourceDataMapper) ResourceSingularizer(resource string) (singular string, err error) {
return t.mapper.ResourceSingularizer(resource)
}
func NewMapper(mapper meta.RESTMapper, kind, version, group string) meta.RESTMapper {
return &thirdPartyResourceDataMapper{
mapper: mapper,
kind: kind,
version: version,
group: group,
}
}
type thirdPartyResourceDataCodecFactory struct {
delegate runtime.NegotiatedSerializer
kind string
encodeGV schema.GroupVersion
decodeGV schema.GroupVersion
}
func NewNegotiatedSerializer(s runtime.NegotiatedSerializer, kind string, encodeGV, decodeGV schema.GroupVersion) runtime.NegotiatedSerializer {
return &thirdPartyResourceDataCodecFactory{
delegate: s,
kind: kind,
encodeGV: encodeGV,
decodeGV: decodeGV,
}
}
func (t *thirdPartyResourceDataCodecFactory) SupportedMediaTypes() []runtime.SerializerInfo {
for _, info := range t.delegate.SupportedMediaTypes() {
if info.MediaType == runtime.ContentTypeJSON {
return []runtime.SerializerInfo{info}
}
}
return nil
}
func (t *thirdPartyResourceDataCodecFactory) EncoderForVersion(s runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
return &thirdPartyResourceDataEncoder{delegate: t.delegate.EncoderForVersion(s, gv), gvk: t.encodeGV.WithKind(t.kind)}
}
func (t *thirdPartyResourceDataCodecFactory) DecoderToVersion(s runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
return NewDecoder(t.delegate.DecoderToVersion(s, gv), t.kind)
}
func NewCodec(delegate runtime.Codec, gvk schema.GroupVersionKind) runtime.Codec {
return runtime.NewCodec(NewEncoder(delegate, gvk), NewDecoder(delegate, gvk.Kind))
}
type thirdPartyResourceDataDecoder struct {
delegate runtime.Decoder
kind string
}
func NewDecoder(delegate runtime.Decoder, kind string) runtime.Decoder {
return &thirdPartyResourceDataDecoder{delegate: delegate, kind: kind}
}
var _ runtime.Decoder = &thirdPartyResourceDataDecoder{}
func parseObject(data []byte) (map[string]interface{}, error) {
var mapObj map[string]interface{}
if err := json.Unmarshal(data, &mapObj); err != nil {
return nil, err
}
return mapObj, nil
}
func (t *thirdPartyResourceDataDecoder) populate(data []byte) (runtime.Object, *schema.GroupVersionKind, error) {
mapObj, err := parseObject(data)
if err != nil {
return nil, nil, err
}
return t.populateFromObject(mapObj, data)
}
func (t *thirdPartyResourceDataDecoder) populateFromObject(mapObj map[string]interface{}, data []byte) (runtime.Object, *schema.GroupVersionKind, error) {
typeMeta := metav1.TypeMeta{}
if err := json.Unmarshal(data, &typeMeta); err != nil {
return nil, nil, err
}
gv, err := schema.ParseGroupVersion(typeMeta.APIVersion)
if err != nil {
return nil, nil, err
}
gvk := gv.WithKind(typeMeta.Kind)
isList := strings.HasSuffix(typeMeta.Kind, "List")
switch {
case !isList && (len(t.kind) == 0 || typeMeta.Kind == t.kind):
result := &extensions.ThirdPartyResourceData{}
if err := t.populateResource(result, mapObj, data); err != nil {
return nil, nil, err
}
return result, &gvk, nil
case isList && (len(t.kind) == 0 || typeMeta.Kind == t.kind+"List"):
list := &extensions.ThirdPartyResourceDataList{}
if err := t.populateListResource(list, mapObj); err != nil {
return nil, nil, err
}
return list, &gvk, nil
default:
return nil, nil, fmt.Errorf("unexpected kind: %s, expected %s", typeMeta.Kind, t.kind)
}
}
func (t *thirdPartyResourceDataDecoder) populateResource(objIn *extensions.ThirdPartyResourceData, mapObj map[string]interface{}, data []byte) error {
metadata, ok := mapObj["metadata"].(map[string]interface{})
if !ok {
return fmt.Errorf("unexpected object for metadata: %#v", mapObj["metadata"])
}
metadataData, err := json.Marshal(metadata)
if err != nil {
return err
}
if err := json.Unmarshal(metadataData, &objIn.ObjectMeta); err != nil {
return err
}
// Override API Version with the ThirdPartyResourceData value
// TODO: fix this hard code
objIn.APIVersion = v1beta1.SchemeGroupVersion.String()
objIn.Data = data
return nil
}
func IsThirdPartyObject(rawData []byte, gvk *schema.GroupVersionKind) (isThirdParty bool, gvkOut *schema.GroupVersionKind, err error) {
var gv schema.GroupVersion
if gvk == nil {
data, err := yaml.ToJSON(rawData)
if err != nil {
return false, nil, err
}
metadata := metav1.TypeMeta{}
if err = json.Unmarshal(data, &metadata); err != nil {
return false, nil, err
}
gv, err = schema.ParseGroupVersion(metadata.APIVersion)
if err != nil {
return false, nil, err
}
gvkOut = &schema.GroupVersionKind{
Group: gv.Group,
Version: gv.Version,
Kind: metadata.Kind,
}
} else {
gv = gvk.GroupVersion()
gvkOut = gvk
}
return api.Registry.IsThirdPartyAPIGroupVersion(gv), gvkOut, nil
}
func (t *thirdPartyResourceDataDecoder) Decode(data []byte, gvk *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
if into == nil {
if gvk == nil || gvk.Kind != t.kind {
if isThirdParty, _, err := IsThirdPartyObject(data, gvk); err != nil {
return nil, nil, err
} else if !isThirdParty {
return t.delegate.Decode(data, gvk, into)
}
}
return t.populate(data)
}
switch o := into.(type) {
case *extensions.ThirdPartyResourceData:
break
case *runtime.VersionedObjects:
// We're not sure that it's third party, we need to test
if gvk == nil || gvk.Kind != t.kind {
if isThirdParty, _, err := IsThirdPartyObject(data, gvk); err != nil {
return nil, nil, err
} else if !isThirdParty {
return t.delegate.Decode(data, gvk, into)
}
}
obj, outGVK, err := t.populate(data)
if err != nil {
return nil, nil, err
}
o.Objects = []runtime.Object{
obj,
}
return o, outGVK, nil
default:
if gvk != nil && api.Registry.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
// delegate won't recognize a thirdparty group version
gvk = nil
}
return t.delegate.Decode(data, gvk, into)
}
thirdParty := into.(*extensions.ThirdPartyResourceData)
var mapObj map[string]interface{}
if err := json.Unmarshal(data, &mapObj); err != nil {
return nil, nil, err
}
/*if gvk.Kind != "ThirdPartyResourceData" {
return nil, nil, fmt.Errorf("unexpected kind: %s", gvk.Kind)
}*/
actual := &schema.GroupVersionKind{}
if kindObj, found := mapObj["kind"]; !found {
if gvk == nil {
return nil, nil, runtime.NewMissingKindErr(string(data))
}
mapObj["kind"] = gvk.Kind
actual.Kind = gvk.Kind
} else {
kindStr, ok := kindObj.(string)
if !ok {
return nil, nil, fmt.Errorf("unexpected object for 'kind': %v", kindObj)
}
if len(t.kind) > 0 && kindStr != t.kind {
return nil, nil, fmt.Errorf("kind doesn't match, expecting: %s, got %s", t.kind, kindStr)
}
actual.Kind = kindStr
}
if versionObj, found := mapObj["apiVersion"]; !found {
if gvk == nil {
return nil, nil, runtime.NewMissingVersionErr(string(data))
}
mapObj["apiVersion"] = gvk.GroupVersion().String()
actual.Group, actual.Version = gvk.Group, gvk.Version
} else {
versionStr, ok := versionObj.(string)
if !ok {
return nil, nil, fmt.Errorf("unexpected object for 'apiVersion': %v", versionObj)
}
if gvk != nil && versionStr != gvk.GroupVersion().String() {
return nil, nil, fmt.Errorf("version doesn't match, expecting: %v, got %s", gvk.GroupVersion(), versionStr)
}
gv, err := schema.ParseGroupVersion(versionStr)
if err != nil {
return nil, nil, err
}
actual.Group, actual.Version = gv.Group, gv.Version
}
mapObj, err := parseObject(data)
if err != nil {
return nil, actual, err
}
if err := t.populateResource(thirdParty, mapObj, data); err != nil {
return nil, actual, err
}
return thirdParty, actual, nil
}
func (t *thirdPartyResourceDataDecoder) populateListResource(objIn *extensions.ThirdPartyResourceDataList, mapObj map[string]interface{}) error {
items, ok := mapObj["items"].([]interface{})
if !ok {
return fmt.Errorf("unexpected object for items: %#v", mapObj["items"])
}
objIn.Items = make([]extensions.ThirdPartyResourceData, len(items))
for ix := range items {
objData, err := json.Marshal(items[ix])
if err != nil {
return err
}
objMap, err := parseObject(objData)
if err != nil {
return err
}
if err := t.populateResource(&objIn.Items[ix], objMap, objData); err != nil {
return err
}
}
return nil
}
type thirdPartyResourceDataEncoder struct {
delegate runtime.Encoder
gvk schema.GroupVersionKind
}
func NewEncoder(delegate runtime.Encoder, gvk schema.GroupVersionKind) runtime.Encoder {
return &thirdPartyResourceDataEncoder{delegate: delegate, gvk: gvk}
}
var _ runtime.Encoder = &thirdPartyResourceDataEncoder{}
func encodeToJSON(obj *extensions.ThirdPartyResourceData, stream io.Writer) error {
var objMap map[string]interface{}
if err := json.Unmarshal(obj.Data, &objMap); err != nil {
return err
}
objMap["metadata"] = &obj.ObjectMeta
encoder := json.NewEncoder(stream)
return encoder.Encode(objMap)
}
func (t *thirdPartyResourceDataEncoder) Encode(obj runtime.Object, stream io.Writer) (err error) {
switch obj := obj.(type) {
case *extensions.ThirdPartyResourceData:
return encodeToJSON(obj, stream)
case *extensions.ThirdPartyResourceDataList:
// TODO: There are likely still better ways to do this...
listItems := make([]gojson.RawMessage, len(obj.Items))
for ix := range obj.Items {
buff := &bytes.Buffer{}
err := encodeToJSON(&obj.Items[ix], buff)
if err != nil {
return err
}
listItems[ix] = gojson.RawMessage(buff.Bytes())
}
if t.gvk.Empty() {
return fmt.Errorf("thirdPartyResourceDataEncoder was not given a target version")
}
encMap := struct {
// +optional
Kind string `json:"kind,omitempty"`
Items []gojson.RawMessage `json:"items"`
// +optional
Metadata metav1.ListMeta `json:"metadata,omitempty"`
// +optional
APIVersion string `json:"apiVersion,omitempty"`
}{
Kind: t.gvk.Kind + "List",
Items: listItems,
Metadata: obj.ListMeta,
APIVersion: t.gvk.GroupVersion().String(),
}
encBytes, err := json.Marshal(encMap)
if err != nil {
return err
}
_, err = stream.Write(encBytes)
return err
case *metav1.InternalEvent:
event := &metav1.WatchEvent{}
err := metav1.Convert_versioned_InternalEvent_to_versioned_Event(obj, event, nil)
if err != nil {
return err
}
enc := json.NewEncoder(stream)
err = enc.Encode(event)
if err != nil {
return err
}
return nil
case *metav1.WatchEvent:
// This is the same as the InternalEvent case above, except the caller
// already did the conversion for us (see #44350).
// In theory, we probably don't need the InternalEvent case anymore,
// but the test coverage for TPR is too low to risk removing it.
return json.NewEncoder(stream).Encode(obj)
case *metav1.Status, *metav1.APIResourceList:
return t.delegate.Encode(obj, stream)
default:
return fmt.Errorf("unexpected object to encode: %#v", obj)
}
}
func NewObjectCreator(group, version string, delegate runtime.ObjectCreater) runtime.ObjectCreater {
return &thirdPartyResourceDataCreator{group, version, delegate}
}
type thirdPartyResourceDataCreator struct {
group string
version string
delegate runtime.ObjectCreater
}
func (t *thirdPartyResourceDataCreator) New(kind schema.GroupVersionKind) (out runtime.Object, err error) {
switch kind.Kind {
case "ThirdPartyResourceData":
if apiutil.GetGroupVersion(t.group, t.version) != kind.GroupVersion().String() {
return nil, fmt.Errorf("unknown kind %v", kind)
}
return &extensions.ThirdPartyResourceData{}, nil
case "ThirdPartyResourceDataList":
if apiutil.GetGroupVersion(t.group, t.version) != kind.GroupVersion().String() {
return nil, fmt.Errorf("unknown kind %v", kind)
}
return &extensions.ThirdPartyResourceDataList{}, nil
// TODO: this list needs to be formalized higher in the chain
case "ListOptions", "WatchEvent":
if apiutil.GetGroupVersion(t.group, t.version) == kind.GroupVersion().String() {
// Translate third party group to external group.
gvk := api.Registry.EnabledVersionsForGroup(api.GroupName)[0].WithKind(kind.Kind)
return t.delegate.New(gvk)
}
return t.delegate.New(kind)
default:
return t.delegate.New(kind)
}
}
func NewThirdPartyParameterCodec(p runtime.ParameterCodec) runtime.ParameterCodec {
return &thirdPartyParameterCodec{p}
}
type thirdPartyParameterCodec struct {
delegate runtime.ParameterCodec
}
func (t *thirdPartyParameterCodec) DecodeParameters(parameters url.Values, from schema.GroupVersion, into runtime.Object) error {
return t.delegate.DecodeParameters(parameters, v1.SchemeGroupVersion, into)
}
func (t *thirdPartyParameterCodec) EncodeParameters(obj runtime.Object, to schema.GroupVersion) (url.Values, error) {
return t.delegate.EncodeParameters(obj, v1.SchemeGroupVersion)
}

View File

@ -1,329 +0,0 @@
/*
Copyright 2015 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 thirdpartyresourcedata
import (
"bytes"
"encoding/json"
"reflect"
"strings"
"testing"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/apis/extensions"
)
type Foo struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty" description:"standard object metadata"`
SomeField string `json:"someField"`
OtherField int `json:"otherField"`
}
func (*Foo) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind
}
type FooList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty" description:"standard list metadata; see https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"`
Items []Foo `json:"items"`
}
func TestCodec(t *testing.T) {
tests := []struct {
into runtime.Object
obj *Foo
expectErr bool
name string
}{
{
into: &runtime.VersionedObjects{},
obj: &Foo{
ObjectMeta: metav1.ObjectMeta{Name: "bar"},
TypeMeta: metav1.TypeMeta{APIVersion: "company.com/v1", Kind: "Foo"},
},
expectErr: false,
name: "versioned objects list",
},
{
obj: &Foo{ObjectMeta: metav1.ObjectMeta{Name: "bar"}},
expectErr: true,
name: "missing kind",
},
{
obj: &Foo{
ObjectMeta: metav1.ObjectMeta{Name: "bar"},
TypeMeta: metav1.TypeMeta{APIVersion: "company.com/v1", Kind: "Foo"},
},
name: "basic",
},
{
into: &extensions.ThirdPartyResourceData{},
obj: &Foo{
ObjectMeta: metav1.ObjectMeta{Name: "bar"},
TypeMeta: metav1.TypeMeta{Kind: "ThirdPartyResourceData"},
},
expectErr: true,
name: "broken kind",
},
{
obj: &Foo{
ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "baz"},
TypeMeta: metav1.TypeMeta{APIVersion: "company.com/v1", Kind: "Foo"},
},
name: "resource version",
},
{
obj: &Foo{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
CreationTimestamp: metav1.Time{Time: time.Unix(100, 0)},
},
TypeMeta: metav1.TypeMeta{
APIVersion: "company.com/v1",
Kind: "Foo",
},
},
name: "creation time",
},
{
obj: &Foo{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
ResourceVersion: "baz",
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
TypeMeta: metav1.TypeMeta{APIVersion: "company.com/v1", Kind: "Foo"},
},
name: "labels",
},
}
api.Registry.AddThirdPartyAPIGroupVersions(schema.GroupVersion{Group: "company.com", Version: "v1"})
for _, test := range tests {
d := &thirdPartyResourceDataDecoder{kind: "Foo", delegate: testapi.Extensions.Codec()}
e := &thirdPartyResourceDataEncoder{gvk: schema.GroupVersionKind{
Group: "company.com",
Version: "v1",
Kind: "Foo",
}, delegate: testapi.Extensions.Codec()}
data, err := json.Marshal(test.obj)
if err != nil {
t.Errorf("[%s] unexpected error: %v", test.name, err)
continue
}
var obj runtime.Object
if test.into != nil {
err = runtime.DecodeInto(d, data, test.into)
obj = test.into
} else {
obj, err = runtime.Decode(d, data)
}
if err != nil && !test.expectErr {
t.Errorf("[%s] unexpected error: %v", test.name, err)
continue
}
if test.expectErr {
if err == nil {
t.Errorf("[%s] unexpected non-error", test.name)
}
continue
}
var rsrcObj *extensions.ThirdPartyResourceData
switch o := obj.(type) {
case *extensions.ThirdPartyResourceData:
rsrcObj = o
case *runtime.VersionedObjects:
rsrcObj = o.First().(*extensions.ThirdPartyResourceData)
default:
t.Errorf("[%s] unexpected object: %v", test.name, obj)
continue
}
if !reflect.DeepEqual(rsrcObj.ObjectMeta, test.obj.ObjectMeta) {
t.Errorf("[%s]\nexpected\n%v\nsaw\n%v\n", test.name, rsrcObj.ObjectMeta, test.obj.ObjectMeta)
}
var output Foo
if err := json.Unmarshal(rsrcObj.Data, &output); err != nil {
t.Errorf("[%s] unexpected error: %v", test.name, err)
continue
}
if !reflect.DeepEqual(&output, test.obj) {
t.Errorf("[%s]\nexpected\n%v\nsaw\n%v\n", test.name, test.obj, &output)
}
data, err = runtime.Encode(e, rsrcObj)
if err != nil {
t.Errorf("[%s] unexpected error: %v", test.name, err)
}
var output2 Foo
if err := json.Unmarshal(data, &output2); err != nil {
t.Errorf("[%s] unexpected error: %v", test.name, err)
continue
}
if !reflect.DeepEqual(&output2, test.obj) {
t.Errorf("[%s]\nexpected\n%v\nsaw\n%v\n", test.name, test.obj, &output2)
}
}
}
func TestCreater(t *testing.T) {
creater := NewObjectCreator("creater group", "creater version", api.Scheme)
tests := []struct {
name string
kind schema.GroupVersionKind
expectedObj runtime.Object
expectErr bool
}{
{
name: "valid ThirdPartyResourceData creation",
kind: schema.GroupVersionKind{Group: "creater group", Version: "creater version", Kind: "ThirdPartyResourceData"},
expectedObj: &extensions.ThirdPartyResourceData{},
expectErr: false,
},
{
name: "invalid ThirdPartyResourceData creation",
kind: schema.GroupVersionKind{Version: "invalid version", Kind: "ThirdPartyResourceData"},
expectedObj: nil,
expectErr: true,
},
{
name: "valid ListOptions creation",
kind: schema.GroupVersionKind{Version: "v1", Kind: "ListOptions"},
expectedObj: &metav1.ListOptions{},
expectErr: false,
},
}
for _, test := range tests {
out, err := creater.New(test.kind)
if err != nil && !test.expectErr {
t.Errorf("[%s] unexpected error: %v", test.name, err)
}
if err == nil && test.expectErr {
t.Errorf("[%s] unexpected non-error", test.name)
}
if !reflect.DeepEqual(test.expectedObj, out) {
t.Errorf("[%s] unexpected error: expect: %v, got: %v", test.name, test.expectedObj, out)
}
}
}
func TestEncodeToStreamForInternalEvent(t *testing.T) {
e := &thirdPartyResourceDataEncoder{gvk: schema.GroupVersionKind{
Group: "company.com",
Version: "v1",
Kind: "Foo",
}, delegate: testapi.Extensions.Codec()}
buf := bytes.NewBuffer([]byte{})
expected := &metav1.WatchEvent{
Type: "Added",
}
err := e.Encode(&metav1.InternalEvent{
Type: "Added",
}, buf)
jBytes, _ := json.Marshal(expected)
if string(jBytes) == buf.String() {
t.Errorf("unexpected encoding expected %s got %s", string(jBytes), buf.String())
}
if err != nil {
t.Errorf("unexpected error encoding: %v", err)
}
}
func TestThirdPartyResourceDataListEncoding(t *testing.T) {
gv := schema.GroupVersion{Group: "stable.foo.faz", Version: "v1"}
gvk := gv.WithKind("Bar")
e := &thirdPartyResourceDataEncoder{delegate: testapi.Extensions.Codec(), gvk: gvk}
subject := &extensions.ThirdPartyResourceDataList{}
buf := bytes.NewBuffer([]byte{})
err := e.Encode(subject, buf)
if err != nil {
t.Errorf("encoding unexpected error: %v", err)
}
targetOutput := struct {
Kind string `json:"kind,omitempty"`
Items []json.RawMessage `json:"items"`
Metadata metav1.ListMeta `json:"metadata,omitempty"`
APIVersion string `json:"apiVersion,omitempty"`
}{}
err = json.Unmarshal(buf.Bytes(), &targetOutput)
if err != nil {
t.Errorf("unmarshal unexpected error: %v", err)
}
if expectedKind := gvk.Kind + "List"; expectedKind != targetOutput.Kind {
t.Errorf("unexpected kind on list got %s expected %s", targetOutput.Kind, expectedKind)
}
if targetOutput.Metadata != subject.ListMeta {
t.Errorf("metadata mismatch %v != %v", targetOutput.Metadata, subject.ListMeta)
}
if targetOutput.APIVersion != gv.String() {
t.Errorf("apiversion mismatch %v != %v", targetOutput.APIVersion, gv.String())
}
}
func TestDecodeNumbers(t *testing.T) {
gv := schema.GroupVersion{Group: "stable.foo.faz", Version: "v1"}
gvk := gv.WithKind("Foo")
e := &thirdPartyResourceDataEncoder{delegate: testapi.Extensions.Codec(), gvk: gvk}
d := &thirdPartyResourceDataDecoder{kind: "Foo", delegate: testapi.Extensions.Codec()}
// Use highest int64 number and 1000000.
subject := &extensions.ThirdPartyResourceDataList{
Items: []extensions.ThirdPartyResourceData{
{
Data: []byte(`{"num1": 9223372036854775807, "num2": 1000000}`),
},
},
}
// Encode to get original JSON.
originalJSON := bytes.NewBuffer([]byte{})
err := e.Encode(subject, originalJSON)
if err != nil {
t.Errorf("encoding unexpected error: %v", err)
}
// Decode original JSON.
var into runtime.Object
into, _, err = d.Decode(originalJSON.Bytes(), &gvk, into)
if err != nil {
t.Errorf("decoding unexpected error: %v", err)
}
// Check if int is preserved.
decodedJSON := into.(*extensions.ThirdPartyResourceDataList).Items[0].Data
if !strings.Contains(string(decodedJSON), `"num1":9223372036854775807,"num2":1000000`) {
t.Errorf("Expected %s, got %s", `"num1":9223372036854775807,"num2":1000000`, string(decodedJSON))
}
}

View File

@ -1,19 +0,0 @@
/*
Copyright 2015 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 thirdpartyresourcedata provides Registry interface and its REST
// implementation for storing ThirdPartyResourceData api objects.
package thirdpartyresourcedata // import "k8s.io/kubernetes/pkg/registry/extensions/thirdpartyresourcedata"

View File

@ -1,83 +0,0 @@
/*
Copyright 2015 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 thirdpartyresourcedata
import (
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
)
// Registry is an interface implemented by things that know how to store ThirdPartyResourceData objects.
type Registry interface {
ListThirdPartyResourceData(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*extensions.ThirdPartyResourceDataList, error)
WatchThirdPartyResourceData(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error)
GetThirdPartyResourceData(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*extensions.ThirdPartyResourceData, error)
CreateThirdPartyResourceData(ctx genericapirequest.Context, resource *extensions.ThirdPartyResourceData) (*extensions.ThirdPartyResourceData, error)
UpdateThirdPartyResourceData(ctx genericapirequest.Context, resource *extensions.ThirdPartyResourceData) (*extensions.ThirdPartyResourceData, error)
DeleteThirdPartyResourceData(ctx genericapirequest.Context, name string) error
}
// storage puts strong typing around storage calls
type storage struct {
rest.StandardStorage
}
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
// types will panic.
func NewRegistry(s rest.StandardStorage) Registry {
return &storage{s}
}
func (s *storage) ListThirdPartyResourceData(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*extensions.ThirdPartyResourceDataList, error) {
obj, err := s.List(ctx, options)
if err != nil {
return nil, err
}
return obj.(*extensions.ThirdPartyResourceDataList), nil
}
func (s *storage) WatchThirdPartyResourceData(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) {
return s.Watch(ctx, options)
}
func (s *storage) GetThirdPartyResourceData(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*extensions.ThirdPartyResourceData, error) {
obj, err := s.Get(ctx, name, options)
if err != nil {
return nil, err
}
return obj.(*extensions.ThirdPartyResourceData), nil
}
func (s *storage) CreateThirdPartyResourceData(ctx genericapirequest.Context, ThirdPartyResourceData *extensions.ThirdPartyResourceData) (*extensions.ThirdPartyResourceData, error) {
obj, err := s.Create(ctx, ThirdPartyResourceData, false)
return obj.(*extensions.ThirdPartyResourceData), err
}
func (s *storage) UpdateThirdPartyResourceData(ctx genericapirequest.Context, ThirdPartyResourceData *extensions.ThirdPartyResourceData) (*extensions.ThirdPartyResourceData, error) {
obj, _, err := s.Update(ctx, ThirdPartyResourceData.Name, rest.DefaultUpdatedObjectInfo(ThirdPartyResourceData, api.Scheme))
return obj.(*extensions.ThirdPartyResourceData), err
}
func (s *storage) DeleteThirdPartyResourceData(ctx genericapirequest.Context, name string) error {
_, _, err := s.Delete(ctx, name, nil)
return err
}

View File

@ -1,60 +0,0 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["storage_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/apis/extensions:go_default_library",
"//pkg/registry/registrytest:go_default_library",
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["storage.go"],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/registry/cachesize:go_default_library",
"//pkg/registry/extensions/thirdpartyresourcedata:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -1,127 +0,0 @@
/*
Copyright 2015 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 storage
import (
"strings"
"sync/atomic"
"k8s.io/apimachinery/pkg/api/errors"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/registry/cachesize"
"k8s.io/kubernetes/pkg/registry/extensions/thirdpartyresourcedata"
)
// errFrozen is a transient error to indicate that clients should retry with backoff.
var errFrozen = errors.NewServiceUnavailable("TPR data is temporarily frozen")
// REST implements a RESTStorage for ThirdPartyResourceData.
type REST struct {
*genericregistry.Store
kind string
frozen atomic.Value
}
// Freeze causes all future calls to Create/Update/Delete/DeleteCollection to return a transient error.
// This is irreversible and meant for use when the TPR data is being deleted or migrated/abandoned.
func (r *REST) Freeze() {
r.frozen.Store(true)
}
func (r *REST) isFrozen() bool {
return r.frozen.Load() != nil
}
// Create is a wrapper to support Freeze.
func (r *REST) Create(ctx genericapirequest.Context, obj runtime.Object, includeUninitialized bool) (runtime.Object, error) {
if r.isFrozen() {
return nil, errFrozen
}
return r.Store.Create(ctx, obj, includeUninitialized)
}
// Update is a wrapper to support Freeze.
func (r *REST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
if r.isFrozen() {
return nil, false, errFrozen
}
return r.Store.Update(ctx, name, objInfo)
}
// Delete is a wrapper to support Freeze.
func (r *REST) Delete(ctx genericapirequest.Context, name string, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
if r.isFrozen() {
return nil, false, errFrozen
}
return r.Store.Delete(ctx, name, options)
}
// DeleteCollection is a wrapper to support Freeze.
func (r *REST) DeleteCollection(ctx genericapirequest.Context, options *metav1.DeleteOptions, listOptions *metainternalversion.ListOptions) (runtime.Object, error) {
if r.isFrozen() {
return nil, errFrozen
}
return r.Store.DeleteCollection(ctx, options, listOptions)
}
// NewREST returns a registry which will store ThirdPartyResourceData in the given helper
func NewREST(optsGetter generic.RESTOptionsGetter, group, kind string) *REST {
resource := extensions.Resource("thirdpartyresourcedatas")
opts, err := optsGetter.GetRESTOptions(resource)
if err != nil {
panic(err) // TODO: Propagate error up
}
// We explicitly do NOT do any decoration here yet.
opts.Decorator = generic.UndecoratedStorage // TODO use watchCacheSize=-1 to signal UndecoratedStorage
opts.ResourcePrefix = "/ThirdPartyResourceData/" + group + "/" + strings.ToLower(kind) + "s"
store := &genericregistry.Store{
Copier: api.Scheme,
NewFunc: func() runtime.Object { return &extensions.ThirdPartyResourceData{} },
NewListFunc: func() runtime.Object { return &extensions.ThirdPartyResourceDataList{} },
PredicateFunc: thirdpartyresourcedata.Matcher,
QualifiedResource: resource,
WatchCacheSize: cachesize.GetWatchCacheSizeByResource(resource.Resource),
CreateStrategy: thirdpartyresourcedata.Strategy,
UpdateStrategy: thirdpartyresourcedata.Strategy,
DeleteStrategy: thirdpartyresourcedata.Strategy,
}
options := &generic.StoreOptions{RESTOptions: opts, AttrFunc: thirdpartyresourcedata.GetAttrs} // Pass in opts to use UndecoratedStorage and custom ResourcePrefix
if err := store.CompleteWithOptions(options); err != nil {
panic(err) // TODO: Propagate error up
}
return &REST{
Store: store,
kind: kind,
}
}
// Implements the rest.KindProvider interface
func (r *REST) Kind() string {
return r.kind
}

View File

@ -1,127 +0,0 @@
/*
Copyright 2015 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 storage
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/apis/extensions"
// Ensure that extensions/v1beta1 package is initialized.
_ "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
"k8s.io/kubernetes/pkg/registry/registrytest"
)
func newStorage(t *testing.T) (*REST, *etcdtesting.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorage(t, extensions.GroupName)
restOptions := generic.RESTOptions{StorageConfig: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1}
return NewREST(restOptions, "foo", "bar"), server
}
func validNewThirdPartyResourceData(name string) *extensions.ThirdPartyResourceData {
return &extensions.ThirdPartyResourceData{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: metav1.NamespaceDefault,
},
Data: []byte("foobarbaz"),
}
}
func TestCreate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
rsrc := validNewThirdPartyResourceData("foo")
rsrc.ObjectMeta = metav1.ObjectMeta{}
test.TestCreate(
// valid
rsrc,
// invalid
&extensions.ThirdPartyResourceData{},
)
}
func TestUpdate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestUpdate(
// valid
validNewThirdPartyResourceData("foo"),
// updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*extensions.ThirdPartyResourceData)
object.Data = []byte("new description")
return object
},
)
}
func TestDelete(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestDelete(validNewThirdPartyResourceData("foo"))
}
func TestGet(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestGet(validNewThirdPartyResourceData("foo"))
}
func TestList(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestList(validNewThirdPartyResourceData("foo"))
}
func TestWatch(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestWatch(
validNewThirdPartyResourceData("foo"),
// matching labels
[]labels.Set{},
// not matching labels
[]labels.Set{
{"foo": "bar"},
},
// matching fields
[]fields.Set{},
// not matching fields
[]fields.Set{
{"metadata.name": "bar"},
{"name": "foo"},
},
)
}

View File

@ -1,100 +0,0 @@
/*
Copyright 2015 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 thirdpartyresourcedata
import (
"fmt"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
apistorage "k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/extensions/validation"
)
// strategy implements behavior for ThirdPartyResource objects
type strategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating ThirdPartyResource
// objects via the REST API.
var Strategy = strategy{api.Scheme, names.SimpleNameGenerator}
var _ = rest.RESTCreateStrategy(Strategy)
var _ = rest.RESTUpdateStrategy(Strategy)
func (strategy) NamespaceScoped() bool {
return true
}
func (strategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
}
func (strategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
return validation.ValidateThirdPartyResourceData(obj.(*extensions.ThirdPartyResourceData))
}
// Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) {
}
func (strategy) AllowCreateOnUpdate() bool {
return false
}
func (strategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
}
func (strategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateThirdPartyResourceDataUpdate(obj.(*extensions.ThirdPartyResourceData), old.(*extensions.ThirdPartyResourceData))
}
func (strategy) AllowUnconditionalUpdate() bool {
return true
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) {
tprd, ok := obj.(*extensions.ThirdPartyResourceData)
if !ok {
return nil, nil, false, fmt.Errorf("not a ThirdPartyResourceData")
}
return labels.Set(tprd.Labels), SelectableFields(tprd), tprd.Initializers != nil, nil
}
// Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}
// SelectableFields returns a field set that can be used for filter selection
func SelectableFields(obj *extensions.ThirdPartyResourceData) fields.Set {
return nil
}

View File

@ -1,35 +0,0 @@
/*
Copyright 2015 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 thirdpartyresourcedata
import (
"testing"
_ "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
apitesting "k8s.io/kubernetes/pkg/api/testing"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestSelectableFieldLabelConversions(t *testing.T) {
apitesting.TestSelectableFieldLabelConversionsOfKind(t,
testapi.Extensions.GroupVersion().String(),
"ThirdPartyResourceData",
SelectableFields(&extensions.ThirdPartyResourceData{}),
nil,
)
}

View File

@ -1,68 +0,0 @@
/*
Copyright 2015 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 thirdpartyresourcedata
import (
"fmt"
"strings"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func ExtractGroupVersionKind(list *extensions.ThirdPartyResourceList) ([]schema.GroupVersion, []schema.GroupVersionKind, error) {
gvs := []schema.GroupVersion{}
gvks := []schema.GroupVersionKind{}
for ix := range list.Items {
rsrc := &list.Items[ix]
kind, group, err := ExtractApiGroupAndKind(rsrc)
if err != nil {
return nil, nil, err
}
for _, version := range rsrc.Versions {
gv := schema.GroupVersion{Group: group, Version: version.Name}
gvs = append(gvs, gv)
gvks = append(gvks, schema.GroupVersionKind{Group: group, Version: version.Name, Kind: kind})
}
}
return gvs, gvks, nil
}
func convertToCamelCase(input string) string {
result := ""
toUpper := true
for ix := range input {
char := input[ix]
if toUpper {
result = result + string([]byte{(char - 32)})
toUpper = false
} else if char == '-' {
toUpper = true
} else {
result = result + string([]byte{char})
}
}
return result
}
func ExtractApiGroupAndKind(rsrc *extensions.ThirdPartyResource) (kind string, group string, err error) {
parts := strings.Split(rsrc.Name, ".")
if len(parts) < 3 {
return "", "", fmt.Errorf("unexpectedly short resource name: %s, expected at least <kind>.<domain>.<tld>", rsrc.Name)
}
return convertToCamelCase(parts[0]), strings.Join(parts[1:], "."), nil
}

View File

@ -1,66 +0,0 @@
/*
Copyright 2015 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 thirdpartyresourcedata
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestExtractAPIGroupAndKind(t *testing.T) {
tests := []struct {
input string
expectedKind string
expectedGroup string
expectErr bool
}{
{
input: "foo.company.com",
expectedKind: "Foo",
expectedGroup: "company.com",
},
{
input: "cron-tab.company.com",
expectedKind: "CronTab",
expectedGroup: "company.com",
},
{
input: "foo",
expectErr: true,
},
}
for _, test := range tests {
kind, group, err := ExtractApiGroupAndKind(&extensions.ThirdPartyResource{ObjectMeta: metav1.ObjectMeta{Name: test.input}})
if err != nil && !test.expectErr {
t.Errorf("unexpected error: %v", err)
continue
}
if err == nil && test.expectErr {
t.Errorf("unexpected non-error")
continue
}
if kind != test.expectedKind {
t.Errorf("expected: %s, saw: %s", test.expectedKind, kind)
}
if group != test.expectedGroup {
t.Errorf("expected: %s, saw: %s", test.expectedGroup, group)
}
}
}