Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
cb604f75
Commit
cb604f75
authored
May 15, 2017
by
deads2k
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
start using customresourcedefinition.status
parent
f88c7725
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
879 additions
and
83 deletions
+879
-83
BUILD
staging/src/k8s.io/client-go/tools/cache/BUILD
+2
-0
noxu-resource-definition.yaml
...artifacts/customresource-01/noxu-resource-definition.yaml
+1
-1
BUILD
...io/kube-apiextensions-server/pkg/apis/apiextensions/BUILD
+1
-0
helpers.go
...be-apiextensions-server/pkg/apis/apiextensions/helpers.go
+78
-0
BUILD
.../src/k8s.io/kube-apiextensions-server/pkg/apiserver/BUILD
+1
-0
apiserver.go
...s.io/kube-apiextensions-server/pkg/apiserver/apiserver.go
+6
-1
customresource_discovery_controller.go
...rver/pkg/apiserver/customresource_discovery_controller.go
+24
-21
customresource_handler.go
...extensions-server/pkg/apiserver/customresource_handler.go
+52
-49
BUILD
....io/kube-apiextensions-server/pkg/controller/status/BUILD
+44
-0
naming_controller.go
...ensions-server/pkg/controller/status/naming_controller.go
+337
-0
naming_controller_test.go
...ns-server/pkg/controller/status/naming_controller_test.go
+256
-0
BUILD
...nsions-server/pkg/registry/customresourcedefinition/BUILD
+1
-0
etcd.go
...ions-server/pkg/registry/customresourcedefinition/etcd.go
+27
-0
strategy.go
...-server/pkg/registry/customresourcedefinition/strategy.go
+49
-11
No files found.
staging/src/k8s.io/client-go/tools/cache/BUILD
View file @
cb604f75
...
...
@@ -53,6 +53,7 @@ go_library(
"index.go",
"listers.go",
"listwatch.go",
"mutation_cache.go",
"mutation_detector.go",
"reflector.go",
"shared_informer.go",
...
...
@@ -63,6 +64,7 @@ go_library(
tags = ["automanaged"],
deps = [
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/hashicorp/golang-lru:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
...
...
staging/src/k8s.io/kube-apiextensions-server/artifacts/customresource-01/noxu-resource-definition.yaml
View file @
cb604f75
apiVersion
:
apiextensions.k8s.io/v1alpha1
kind
:
CustomResource
kind
:
CustomResource
Definition
metadata
:
name
:
noxus.mygroup.example.com
spec
:
...
...
staging/src/k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/BUILD
View file @
cb604f75
...
...
@@ -11,6 +11,7 @@ go_library(
name = "go_default_library",
srcs = [
"doc.go",
"helpers.go",
"register.go",
"types.go",
"zz_generated.deepcopy.go",
...
...
staging/src/k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/helpers.go
0 → 100644
View file @
cb604f75
/*
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
apiextensions
// SetCRDCondition sets the status condition. It either overwrites the existing one or
// creates a new one
func
SetCRDCondition
(
customResourceDefinition
*
CustomResourceDefinition
,
newCondition
CustomResourceDefinitionCondition
)
{
existingCondition
:=
FindCRDCondition
(
customResourceDefinition
,
newCondition
.
Type
)
if
existingCondition
==
nil
{
customResourceDefinition
.
Status
.
Conditions
=
append
(
customResourceDefinition
.
Status
.
Conditions
,
newCondition
)
return
}
if
existingCondition
.
Status
!=
newCondition
.
Status
{
existingCondition
.
Status
=
newCondition
.
Status
existingCondition
.
LastTransitionTime
=
newCondition
.
LastTransitionTime
}
existingCondition
.
Reason
=
newCondition
.
Reason
existingCondition
.
Message
=
newCondition
.
Message
}
// FindCRDCondition returns the condition you're looking for or nil
func
FindCRDCondition
(
customResourceDefinition
*
CustomResourceDefinition
,
conditionType
CustomResourceDefinitionConditionType
)
*
CustomResourceDefinitionCondition
{
for
i
:=
range
customResourceDefinition
.
Status
.
Conditions
{
if
customResourceDefinition
.
Status
.
Conditions
[
i
]
.
Type
==
conditionType
{
return
&
customResourceDefinition
.
Status
.
Conditions
[
i
]
}
}
return
nil
}
// IsCRDConditionTrue indicates if the condition is present and strictly true
func
IsCRDConditionTrue
(
customResourceDefinition
*
CustomResourceDefinition
,
conditionType
CustomResourceDefinitionConditionType
)
bool
{
return
IsCRDConditionPresentAndEqual
(
customResourceDefinition
,
conditionType
,
ConditionTrue
)
}
// IsCRDConditionFalse indicates if the condition is present and false true
func
IsCRDConditionFalse
(
customResourceDefinition
*
CustomResourceDefinition
,
conditionType
CustomResourceDefinitionConditionType
)
bool
{
return
IsCRDConditionPresentAndEqual
(
customResourceDefinition
,
conditionType
,
ConditionFalse
)
}
// IsCRDConditionPresentAndEqual indicates if the condition is present and equal to the arg
func
IsCRDConditionPresentAndEqual
(
customResourceDefinition
*
CustomResourceDefinition
,
conditionType
CustomResourceDefinitionConditionType
,
status
ConditionStatus
)
bool
{
for
_
,
condition
:=
range
customResourceDefinition
.
Status
.
Conditions
{
if
condition
.
Type
==
conditionType
{
return
condition
.
Status
==
status
}
}
return
false
}
// IsCRDConditionEquivalent returns true if the lhs and rhs are equivalent except for times
func
IsCRDConditionEquivalent
(
lhs
,
rhs
*
CustomResourceDefinitionCondition
)
bool
{
if
lhs
==
nil
&&
rhs
==
nil
{
return
true
}
if
lhs
==
nil
||
rhs
==
nil
{
return
false
}
return
lhs
.
Message
==
rhs
.
Message
&&
lhs
.
Reason
==
rhs
.
Reason
&&
lhs
.
Status
==
rhs
.
Status
&&
lhs
.
Type
==
rhs
.
Type
}
staging/src/k8s.io/kube-apiextensions-server/pkg/apiserver/BUILD
View file @
cb604f75
...
...
@@ -54,6 +54,7 @@ go_library(
"//vendor/k8s.io/kube-apiextensions-server/pkg/client/informers/internalversion:go_default_library",
"//vendor/k8s.io/kube-apiextensions-server/pkg/client/informers/internalversion/apiextensions/internalversion:go_default_library",
"//vendor/k8s.io/kube-apiextensions-server/pkg/client/listers/apiextensions/internalversion:go_default_library",
"//vendor/k8s.io/kube-apiextensions-server/pkg/controller/status:go_default_library",
"//vendor/k8s.io/kube-apiextensions-server/pkg/registry/customresource:go_default_library",
"//vendor/k8s.io/kube-apiextensions-server/pkg/registry/customresourcedefinition:go_default_library",
],
...
...
staging/src/k8s.io/kube-apiextensions-server/pkg/apiserver/apiserver.go
View file @
cb604f75
...
...
@@ -37,6 +37,7 @@ import (
"k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/v1alpha1"
"k8s.io/kube-apiextensions-server/pkg/client/clientset/internalclientset"
internalinformers
"k8s.io/kube-apiextensions-server/pkg/client/informers/internalversion"
"k8s.io/kube-apiextensions-server/pkg/controller/status"
"k8s.io/kube-apiextensions-server/pkg/registry/customresourcedefinition"
// make sure the generated client works
...
...
@@ -113,8 +114,10 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
apiGroupInfo
:=
genericapiserver
.
NewDefaultAPIGroupInfo
(
apiextensions
.
GroupName
,
registry
,
Scheme
,
metav1
.
ParameterCodec
,
Codecs
)
apiGroupInfo
.
GroupMeta
.
GroupVersion
=
v1alpha1
.
SchemeGroupVersion
customResourceDefintionStorage
:=
customresourcedefinition
.
NewREST
(
Scheme
,
c
.
GenericConfig
.
RESTOptionsGetter
)
v1alpha1storage
:=
map
[
string
]
rest
.
Storage
{}
v1alpha1storage
[
"customresourcedefinitions"
]
=
customresourcedefinition
.
NewREST
(
Scheme
,
c
.
GenericConfig
.
RESTOptionsGetter
)
v1alpha1storage
[
"customresourcedefinitions"
]
=
customResourceDefintionStorage
v1alpha1storage
[
"customresourcedefinitions/status"
]
=
customresourcedefinition
.
NewStatusREST
(
Scheme
,
customResourceDefintionStorage
)
apiGroupInfo
.
VersionedResourcesStorageMap
[
"v1alpha1"
]
=
v1alpha1storage
if
err
:=
s
.
GenericAPIServer
.
InstallAPIGroup
(
&
apiGroupInfo
);
err
!=
nil
{
...
...
@@ -153,6 +156,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
s
.
GenericAPIServer
.
Handler
.
PostGoRestfulMux
.
HandlePrefix
(
"/apis/"
,
customResourceDefinitionHandler
)
customResourceDefinitionController
:=
NewDiscoveryController
(
customResourceDefinitionInformers
.
Apiextensions
()
.
InternalVersion
()
.
CustomResourceDefinitions
(),
versionDiscoveryHandler
,
groupDiscoveryHandler
)
namingController
:=
status
.
NewNamingConditionController
(
customResourceDefinitionInformers
.
Apiextensions
()
.
InternalVersion
()
.
CustomResourceDefinitions
(),
customResourceDefinitionClient
)
s
.
GenericAPIServer
.
AddPostStartHook
(
"start-apiextensions-informers"
,
func
(
context
genericapiserver
.
PostStartHookContext
)
error
{
customResourceDefinitionInformers
.
Start
(
context
.
StopCh
)
...
...
@@ -160,6 +164,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
})
s
.
GenericAPIServer
.
AddPostStartHook
(
"start-apiextensions-controllers"
,
func
(
context
genericapiserver
.
PostStartHookContext
)
error
{
go
customResourceDefinitionController
.
Run
(
context
.
StopCh
)
go
namingController
.
Run
(
context
.
StopCh
)
return
nil
})
...
...
staging/src/k8s.io/kube-apiextensions-server/pkg/apiserver/customresource_discovery_controller.go
View file @
cb604f75
...
...
@@ -40,8 +40,8 @@ type DiscoveryController struct {
versionHandler
*
versionDiscoveryHandler
groupHandler
*
groupDiscoveryHandler
c
ustomResourceDefinition
Lister
listers
.
CustomResourceDefinitionLister
c
ustomResourceDefinition
sSynced
cache
.
InformerSynced
c
rd
Lister
listers
.
CustomResourceDefinitionLister
c
rd
sSynced
cache
.
InformerSynced
// To allow injection for testing.
syncFn
func
(
version
schema
.
GroupVersion
)
error
...
...
@@ -49,17 +49,17 @@ type DiscoveryController struct {
queue
workqueue
.
RateLimitingInterface
}
func
NewDiscoveryController
(
c
ustomResourceDefinition
Informer
informers
.
CustomResourceDefinitionInformer
,
versionHandler
*
versionDiscoveryHandler
,
groupHandler
*
groupDiscoveryHandler
)
*
DiscoveryController
{
func
NewDiscoveryController
(
c
rd
Informer
informers
.
CustomResourceDefinitionInformer
,
versionHandler
*
versionDiscoveryHandler
,
groupHandler
*
groupDiscoveryHandler
)
*
DiscoveryController
{
c
:=
&
DiscoveryController
{
versionHandler
:
versionHandler
,
groupHandler
:
groupHandler
,
c
ustomResourceDefinitionLister
:
customResourceDefinition
Informer
.
Lister
(),
c
ustomResourceDefinitionsSynced
:
customResourceDefinition
Informer
.
Informer
()
.
HasSynced
,
versionHandler
:
versionHandler
,
groupHandler
:
groupHandler
,
c
rdLister
:
crd
Informer
.
Lister
(),
c
rdsSynced
:
crd
Informer
.
Informer
()
.
HasSynced
,
queue
:
workqueue
.
NewNamedRateLimitingQueue
(
workqueue
.
DefaultControllerRateLimiter
(),
"DiscoveryController"
),
}
c
ustomResourceDefinition
Informer
.
Informer
()
.
AddEventHandler
(
cache
.
ResourceEventHandlerFuncs
{
c
rd
Informer
.
Informer
()
.
AddEventHandler
(
cache
.
ResourceEventHandlerFuncs
{
AddFunc
:
c
.
addCustomResourceDefinition
,
UpdateFunc
:
c
.
updateCustomResourceDefinition
,
DeleteFunc
:
c
.
deleteCustomResourceDefinition
,
...
...
@@ -75,36 +75,39 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error {
apiVersionsForDiscovery
:=
[]
metav1
.
GroupVersionForDiscovery
{}
apiResourcesForDiscovery
:=
[]
metav1
.
APIResource
{}
c
ustomResourceDefinitions
,
err
:=
c
.
customResourceDefinition
Lister
.
List
(
labels
.
Everything
())
c
rds
,
err
:=
c
.
crd
Lister
.
List
(
labels
.
Everything
())
if
err
!=
nil
{
return
err
}
foundVersion
:=
false
foundGroup
:=
false
for
_
,
customResourceDefinition
:=
range
customResourceDefinitions
{
// TODO add status checking
for
_
,
crd
:=
range
crds
{
// if we can't definitively determine that our names are good, don't serve it
if
!
apiextensions
.
IsCRDConditionFalse
(
crd
,
apiextensions
.
NameConflict
)
{
continue
}
if
c
ustomResourceDefinition
.
Spec
.
Group
!=
version
.
Group
{
if
c
rd
.
Spec
.
Group
!=
version
.
Group
{
continue
}
foundGroup
=
true
apiVersionsForDiscovery
=
append
(
apiVersionsForDiscovery
,
metav1
.
GroupVersionForDiscovery
{
GroupVersion
:
c
ustomResourceDefinition
.
Spec
.
Group
+
"/"
+
customResourceDefinition
.
Spec
.
Version
,
Version
:
c
ustomResourceDefinition
.
Spec
.
Version
,
GroupVersion
:
c
rd
.
Spec
.
Group
+
"/"
+
crd
.
Spec
.
Version
,
Version
:
c
rd
.
Spec
.
Version
,
})
if
c
ustomResourceDefinition
.
Spec
.
Version
!=
version
.
Version
{
if
c
rd
.
Spec
.
Version
!=
version
.
Version
{
continue
}
foundVersion
=
true
apiResourcesForDiscovery
=
append
(
apiResourcesForDiscovery
,
metav1
.
APIResource
{
Name
:
c
ustomResourceDefinition
.
Spec
.
Names
.
Plural
,
SingularName
:
c
ustomResourceDefinition
.
Spec
.
Names
.
Singular
,
Namespaced
:
c
ustomResourceDefinition
.
Spec
.
Scope
==
apiextensions
.
NamespaceScoped
,
Kind
:
c
ustomResourceDefinition
.
Spec
.
Names
.
Kind
,
Name
:
c
rd
.
Status
.
Accepted
Names
.
Plural
,
SingularName
:
c
rd
.
Status
.
Accepted
Names
.
Singular
,
Namespaced
:
c
rd
.
Spec
.
Scope
==
apiextensions
.
NamespaceScoped
,
Kind
:
c
rd
.
Status
.
Accepted
Names
.
Kind
,
Verbs
:
metav1
.
Verbs
([]
string
{
"delete"
,
"deletecollection"
,
"get"
,
"list"
,
"patch"
,
"create"
,
"update"
,
"watch"
}),
ShortNames
:
c
ustomResourceDefinition
.
Spec
.
Names
.
ShortNames
,
ShortNames
:
c
rd
.
Status
.
Accepted
Names
.
ShortNames
,
})
}
...
...
@@ -140,7 +143,7 @@ func (c *DiscoveryController) Run(stopCh <-chan struct{}) {
glog
.
Infof
(
"Starting DiscoveryController"
)
if
!
cache
.
WaitForCacheSync
(
stopCh
,
c
.
c
ustomResourceDefinition
sSynced
)
{
if
!
cache
.
WaitForCacheSync
(
stopCh
,
c
.
c
rd
sSynced
)
{
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"timed out waiting for caches to sync"
))
return
}
...
...
staging/src/k8s.io/kube-apiextensions-server/pkg/apiserver/customresource_handler.go
View file @
cb604f75
...
...
@@ -48,58 +48,58 @@ import (
"k8s.io/kube-apiextensions-server/pkg/registry/customresource"
)
// c
ustomResourceDefinition
Handler serves the `/apis` endpoint.
// c
rd
Handler serves the `/apis` endpoint.
// This is registered as a filter so that it never collides with any explictly registered endpoints
type
c
ustomResourceDefinition
Handler
struct
{
type
c
rd
Handler
struct
{
versionDiscoveryHandler
*
versionDiscoveryHandler
groupDiscoveryHandler
*
groupDiscoveryHandler
customStorageLock
sync
.
Mutex
// customStorage contains a c
ustomResourceDefinition
StorageMap
// customStorage contains a c
rd
StorageMap
customStorage
atomic
.
Value
requestContextMapper
apirequest
.
RequestContextMapper
c
ustomResourceDefinition
Lister
listers
.
CustomResourceDefinitionLister
c
rd
Lister
listers
.
CustomResourceDefinitionLister
delegate
http
.
Handler
restOptionsGetter
generic
.
RESTOptionsGetter
admission
admission
.
Interface
}
// c
ustomResourceDefinition
Info stores enough information to serve the storage for the custom resource
type
c
ustomResourceDefinition
Info
struct
{
// c
rd
Info stores enough information to serve the storage for the custom resource
type
c
rd
Info
struct
{
storage
*
customresource
.
REST
requestScope
handlers
.
RequestScope
}
// c
ustomResourceDefinition
StorageMap goes from customresourcedefinition to its storage
type
c
ustomResourceDefinitionStorageMap
map
[
types
.
UID
]
*
customResourceDefinition
Info
// c
rd
StorageMap goes from customresourcedefinition to its storage
type
c
rdStorageMap
map
[
types
.
UID
]
*
crd
Info
func
NewCustomResourceDefinitionHandler
(
versionDiscoveryHandler
*
versionDiscoveryHandler
,
groupDiscoveryHandler
*
groupDiscoveryHandler
,
requestContextMapper
apirequest
.
RequestContextMapper
,
c
ustomResourceDefinition
Lister
listers
.
CustomResourceDefinitionLister
,
c
rd
Lister
listers
.
CustomResourceDefinitionLister
,
delegate
http
.
Handler
,
restOptionsGetter
generic
.
RESTOptionsGetter
,
admission
admission
.
Interface
)
*
c
ustomResourceDefinition
Handler
{
ret
:=
&
c
ustomResourceDefinition
Handler
{
versionDiscoveryHandler
:
versionDiscoveryHandler
,
groupDiscoveryHandler
:
groupDiscoveryHandler
,
customStorage
:
atomic
.
Value
{},
requestContextMapper
:
requestContextMapper
,
c
ustomResourceDefinitionLister
:
customResourceDefinition
Lister
,
delegate
:
delegate
,
restOptionsGetter
:
restOptionsGetter
,
admission
:
admission
,
admission
admission
.
Interface
)
*
c
rd
Handler
{
ret
:=
&
c
rd
Handler
{
versionDiscoveryHandler
:
versionDiscoveryHandler
,
groupDiscoveryHandler
:
groupDiscoveryHandler
,
customStorage
:
atomic
.
Value
{},
requestContextMapper
:
requestContextMapper
,
c
rdLister
:
crd
Lister
,
delegate
:
delegate
,
restOptionsGetter
:
restOptionsGetter
,
admission
:
admission
,
}
ret
.
customStorage
.
Store
(
c
ustomResourceDefinition
StorageMap
{})
ret
.
customStorage
.
Store
(
c
rd
StorageMap
{})
return
ret
}
func
(
r
*
c
ustomResourceDefinition
Handler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
)
{
func
(
r
*
c
rd
Handler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
)
{
ctx
,
ok
:=
r
.
requestContextMapper
.
Get
(
req
)
if
!
ok
{
// programmer error
...
...
@@ -134,8 +134,8 @@ func (r *customResourceDefinitionHandler) ServeHTTP(w http.ResponseWriter, req *
return
}
c
ustomResourceDefinition
Name
:=
requestInfo
.
Resource
+
"."
+
requestInfo
.
APIGroup
c
ustomResourceDefinition
,
err
:=
r
.
customResourceDefinitionLister
.
Get
(
customResourceDefinition
Name
)
c
rd
Name
:=
requestInfo
.
Resource
+
"."
+
requestInfo
.
APIGroup
c
rd
,
err
:=
r
.
crdLister
.
Get
(
crd
Name
)
if
apierrors
.
IsNotFound
(
err
)
{
r
.
delegate
.
ServeHTTP
(
w
,
req
)
return
...
...
@@ -144,15 +144,18 @@ func (r *customResourceDefinitionHandler) ServeHTTP(w http.ResponseWriter, req *
http
.
Error
(
w
,
err
.
Error
(),
http
.
StatusInternalServerError
)
return
}
if
c
ustomResourceDefinition
.
Spec
.
Version
!=
requestInfo
.
APIVersion
{
if
c
rd
.
Spec
.
Version
!=
requestInfo
.
APIVersion
{
r
.
delegate
.
ServeHTTP
(
w
,
req
)
return
}
// TODO this is the point to do the condition checks
// if we can't definitively determine that our names are good, delegate
if
!
apiextensions
.
IsCRDConditionFalse
(
crd
,
apiextensions
.
NameConflict
)
{
r
.
delegate
.
ServeHTTP
(
w
,
req
)
}
c
ustomResourceDefinitionInfo
:=
r
.
getServingInfoFor
(
customResourceDefinition
)
storage
:=
c
ustomResourceDefinition
Info
.
storage
requestScope
:=
c
ustomResourceDefinition
Info
.
requestScope
c
rdInfo
:=
r
.
getServingInfoFor
(
crd
)
storage
:=
c
rd
Info
.
storage
requestScope
:=
c
rd
Info
.
requestScope
minRequestTimeout
:=
1
*
time
.
Minute
switch
requestInfo
.
Verb
{
...
...
@@ -195,12 +198,12 @@ func (r *customResourceDefinitionHandler) ServeHTTP(w http.ResponseWriter, req *
}
// removeDeadStorage removes REST storage that isn't being used
func
(
r
*
c
ustomResourceDefinition
Handler
)
removeDeadStorage
()
{
func
(
r
*
c
rd
Handler
)
removeDeadStorage
()
{
// these don't have to be live. A snapshot is fine
// if we wrongly delete, that's ok. The rest storage will be recreated on the next request
// if we wrongly miss one, that's ok. We'll get it next time
storageMap
:=
r
.
customStorage
.
Load
()
.
(
c
ustomResourceDefinition
StorageMap
)
allCustomResourceDefinitions
,
err
:=
r
.
c
ustomResourceDefinition
Lister
.
List
(
labels
.
Everything
())
storageMap
:=
r
.
customStorage
.
Load
()
.
(
c
rd
StorageMap
)
allCustomResourceDefinitions
,
err
:=
r
.
c
rd
Lister
.
List
(
labels
.
Everything
())
if
err
!=
nil
{
utilruntime
.
HandleError
(
err
)
return
...
...
@@ -208,8 +211,8 @@ func (r *customResourceDefinitionHandler) removeDeadStorage() {
for
uid
:=
range
storageMap
{
found
:=
false
for
_
,
c
ustomResourceDefinition
:=
range
allCustomResourceDefinitions
{
if
c
ustomResourceDefinition
.
UID
==
uid
{
for
_
,
c
rd
:=
range
allCustomResourceDefinitions
{
if
c
rd
.
UID
==
uid
{
found
=
true
break
}
...
...
@@ -225,9 +228,9 @@ func (r *customResourceDefinitionHandler) removeDeadStorage() {
r
.
customStorage
.
Store
(
storageMap
)
}
func
(
r
*
c
ustomResourceDefinitionHandler
)
getServingInfoFor
(
customResourceDefinition
*
apiextensions
.
CustomResourceDefinition
)
*
customResourceDefinition
Info
{
storageMap
:=
r
.
customStorage
.
Load
()
.
(
c
ustomResourceDefinition
StorageMap
)
ret
,
ok
:=
storageMap
[
c
ustomResourceDefinition
.
UID
]
func
(
r
*
c
rdHandler
)
getServingInfoFor
(
crd
*
apiextensions
.
CustomResourceDefinition
)
*
crd
Info
{
storageMap
:=
r
.
customStorage
.
Load
()
.
(
c
rd
StorageMap
)
ret
,
ok
:=
storageMap
[
c
rd
.
UID
]
if
ok
{
return
ret
}
...
...
@@ -235,21 +238,21 @@ func (r *customResourceDefinitionHandler) getServingInfoFor(customResourceDefini
r
.
customStorageLock
.
Lock
()
defer
r
.
customStorageLock
.
Unlock
()
ret
,
ok
=
storageMap
[
c
ustomResourceDefinition
.
UID
]
ret
,
ok
=
storageMap
[
c
rd
.
UID
]
if
ok
{
return
ret
}
storage
:=
customresource
.
NewREST
(
schema
.
GroupResource
{
Group
:
c
ustomResourceDefinition
.
Spec
.
Group
,
Resource
:
customResourceDefinition
.
Spec
.
Names
.
Plural
},
schema
.
GroupVersionKind
{
Group
:
c
ustomResourceDefinition
.
Spec
.
Group
,
Version
:
customResourceDefinition
.
Spec
.
Version
,
Kind
:
customResourceDefinition
.
Spec
.
Names
.
ListKind
},
schema
.
GroupResource
{
Group
:
c
rd
.
Spec
.
Group
,
Resource
:
crd
.
Spec
.
Names
.
Plural
},
schema
.
GroupVersionKind
{
Group
:
c
rd
.
Spec
.
Group
,
Version
:
crd
.
Spec
.
Version
,
Kind
:
crd
.
Spec
.
Names
.
ListKind
},
UnstructuredCopier
{},
customresource
.
NewStrategy
(
discovery
.
NewUnstructuredObjectTyper
(
nil
),
c
ustomResourceDefinition
.
Spec
.
Scope
==
apiextensions
.
NamespaceScoped
),
customresource
.
NewStrategy
(
discovery
.
NewUnstructuredObjectTyper
(
nil
),
c
rd
.
Spec
.
Scope
==
apiextensions
.
NamespaceScoped
),
r
.
restOptionsGetter
,
)
parameterScheme
:=
runtime
.
NewScheme
()
parameterScheme
.
AddUnversionedTypes
(
schema
.
GroupVersion
{
Group
:
c
ustomResourceDefinition
.
Spec
.
Group
,
Version
:
customResourceDefinition
.
Spec
.
Version
},
parameterScheme
.
AddUnversionedTypes
(
schema
.
GroupVersion
{
Group
:
c
rd
.
Spec
.
Group
,
Version
:
crd
.
Spec
.
Version
},
&
metav1
.
ListOptions
{},
&
metav1
.
ExportOptions
{},
&
metav1
.
GetOptions
{},
...
...
@@ -259,11 +262,11 @@ func (r *customResourceDefinitionHandler) getServingInfoFor(customResourceDefini
parameterCodec
:=
runtime
.
NewParameterCodec
(
parameterScheme
)
selfLinkPrefix
:=
""
switch
c
ustomResourceDefinition
.
Spec
.
Scope
{
switch
c
rd
.
Spec
.
Scope
{
case
apiextensions
.
ClusterScoped
:
selfLinkPrefix
=
"/"
+
path
.
Join
(
"apis"
,
c
ustomResourceDefinition
.
Spec
.
Group
,
customResourceDefinition
.
Spec
.
Version
)
+
"/"
selfLinkPrefix
=
"/"
+
path
.
Join
(
"apis"
,
c
rd
.
Spec
.
Group
,
crd
.
Spec
.
Version
)
+
"/"
case
apiextensions
.
NamespaceScoped
:
selfLinkPrefix
=
"/"
+
path
.
Join
(
"apis"
,
c
ustomResourceDefinition
.
Spec
.
Group
,
customResourceDefinition
.
Spec
.
Version
,
"namespaces"
)
+
"/"
selfLinkPrefix
=
"/"
+
path
.
Join
(
"apis"
,
c
rd
.
Spec
.
Group
,
crd
.
Spec
.
Version
,
"namespaces"
)
+
"/"
}
requestScope
:=
handlers
.
RequestScope
{
...
...
@@ -273,7 +276,7 @@ func (r *customResourceDefinitionHandler) getServingInfoFor(customResourceDefini
return
ret
},
SelfLinker
:
meta
.
NewAccessor
(),
ClusterScoped
:
c
ustomResourceDefinition
.
Spec
.
Scope
==
apiextensions
.
ClusterScoped
,
ClusterScoped
:
c
rd
.
Spec
.
Scope
==
apiextensions
.
ClusterScoped
,
SelfLinkPathPrefix
:
selfLinkPrefix
,
},
ContextFunc
:
func
(
req
*
http
.
Request
)
apirequest
.
Context
{
...
...
@@ -291,18 +294,18 @@ func (r *customResourceDefinitionHandler) getServingInfoFor(customResourceDefini
Typer
:
discovery
.
NewUnstructuredObjectTyper
(
nil
),
UnsafeConvertor
:
unstructured
.
UnstructuredObjectConverter
{},
Resource
:
schema
.
GroupVersionResource
{
Group
:
c
ustomResourceDefinition
.
Spec
.
Group
,
Version
:
customResourceDefinition
.
Spec
.
Version
,
Resource
:
customResourceDefinition
.
Spec
.
Names
.
Plural
},
Kind
:
schema
.
GroupVersionKind
{
Group
:
c
ustomResourceDefinition
.
Spec
.
Group
,
Version
:
customResourceDefinition
.
Spec
.
Version
,
Kind
:
customResourceDefinition
.
Spec
.
Names
.
Kind
},
Resource
:
schema
.
GroupVersionResource
{
Group
:
c
rd
.
Spec
.
Group
,
Version
:
crd
.
Spec
.
Version
,
Resource
:
crd
.
Spec
.
Names
.
Plural
},
Kind
:
schema
.
GroupVersionKind
{
Group
:
c
rd
.
Spec
.
Group
,
Version
:
crd
.
Spec
.
Version
,
Kind
:
crd
.
Spec
.
Names
.
Kind
},
Subresource
:
""
,
MetaGroupVersion
:
metav1
.
SchemeGroupVersion
,
}
ret
=
&
c
ustomResourceDefinition
Info
{
ret
=
&
c
rd
Info
{
storage
:
storage
,
requestScope
:
requestScope
,
}
storageMap
[
c
ustomResourceDefinition
.
UID
]
=
ret
storageMap
[
c
rd
.
UID
]
=
ret
r
.
customStorage
.
Store
(
storageMap
)
return
ret
}
...
...
staging/src/k8s.io/kube-apiextensions-server/pkg/controller/status/BUILD
0 → 100644
View file @
cb604f75
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 = ["naming_controller_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
"//vendor/k8s.io/kube-apiextensions-server/pkg/apis/apiextensions:go_default_library",
"//vendor/k8s.io/kube-apiextensions-server/pkg/client/listers/apiextensions/internalversion:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["naming_controller.go"],
tags = ["automanaged"],
deps = [
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait: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-apiextensions-server/pkg/apis/apiextensions:go_default_library",
"//vendor/k8s.io/kube-apiextensions-server/pkg/client/clientset/internalclientset/typed/apiextensions/internalversion:go_default_library",
"//vendor/k8s.io/kube-apiextensions-server/pkg/client/informers/internalversion/apiextensions/internalversion:go_default_library",
"//vendor/k8s.io/kube-apiextensions-server/pkg/client/listers/apiextensions/internalversion:go_default_library",
],
)
staging/src/k8s.io/kube-apiextensions-server/pkg/controller/status/naming_controller.go
0 → 100644
View file @
cb604f75
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package
status
import
(
"fmt"
"reflect"
"time"
"github.com/golang/glog"
apierrors
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/labels"
utilerrors
"k8s.io/apimachinery/pkg/util/errors"
utilruntime
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
"k8s.io/kube-apiextensions-server/pkg/apis/apiextensions"
client
"k8s.io/kube-apiextensions-server/pkg/client/clientset/internalclientset/typed/apiextensions/internalversion"
informers
"k8s.io/kube-apiextensions-server/pkg/client/informers/internalversion/apiextensions/internalversion"
listers
"k8s.io/kube-apiextensions-server/pkg/client/listers/apiextensions/internalversion"
)
var
cloner
=
conversion
.
NewCloner
()
// This controller is reserving names. To avoid conflicts, be sure to run only one instance of the worker at a time.
// This could eventually be lifted, but starting simple.
type
NamingConditionController
struct
{
crdClient
client
.
CustomResourceDefinitionsGetter
crdLister
listers
.
CustomResourceDefinitionLister
crdSynced
cache
.
InformerSynced
// crdMutationCache backs our lister and keeps track of committed updates to avoid racy
// write/lookup cycles. It's got 100 slots by default, so it unlikely to overrun
// TODO to revisit this if naming conflicts are found to occur in the wild
crdMutationCache
cache
.
MutationCache
// To allow injection for testing.
syncFn
func
(
key
string
)
error
queue
workqueue
.
RateLimitingInterface
}
func
NewNamingConditionController
(
crdInformer
informers
.
CustomResourceDefinitionInformer
,
crdClient
client
.
CustomResourceDefinitionsGetter
,
)
*
NamingConditionController
{
c
:=
&
NamingConditionController
{
crdClient
:
crdClient
,
crdLister
:
crdInformer
.
Lister
(),
crdSynced
:
crdInformer
.
Informer
()
.
HasSynced
,
queue
:
workqueue
.
NewNamedRateLimitingQueue
(
workqueue
.
DefaultControllerRateLimiter
(),
"CustomResourceDefinition-NamingConditionController"
),
}
informerIndexer
:=
crdInformer
.
Informer
()
.
GetIndexer
()
c
.
crdMutationCache
=
cache
.
NewIntegerResourceVersionMutationCache
(
informerIndexer
,
informerIndexer
)
crdInformer
.
Informer
()
.
AddEventHandler
(
cache
.
ResourceEventHandlerFuncs
{
AddFunc
:
c
.
addCustomResourceDefinition
,
UpdateFunc
:
c
.
updateCustomResourceDefinition
,
DeleteFunc
:
c
.
deleteCustomResourceDefinition
,
})
c
.
syncFn
=
c
.
sync
return
c
}
func
(
c
*
NamingConditionController
)
getAcceptedNamesForGroup
(
group
string
)
(
allResources
sets
.
String
,
allKinds
sets
.
String
)
{
allResources
=
sets
.
String
{}
allKinds
=
sets
.
String
{}
list
,
err
:=
c
.
crdLister
.
List
(
labels
.
Everything
())
if
err
!=
nil
{
panic
(
err
)
}
for
_
,
curr
:=
range
list
{
if
curr
.
Spec
.
Group
!=
group
{
continue
}
// for each item here, see if we have a mutation cache entry that is more recent
// this makes sure that if we tight loop on update and run, our mutation cache will show
// us the version of the objects we just updated to.
item
:=
curr
obj
,
exists
,
err
:=
c
.
crdMutationCache
.
GetByKey
(
curr
.
Name
)
if
exists
&&
err
==
nil
{
item
=
obj
.
(
*
apiextensions
.
CustomResourceDefinition
)
}
allResources
.
Insert
(
item
.
Status
.
AcceptedNames
.
Plural
)
allResources
.
Insert
(
item
.
Status
.
AcceptedNames
.
Singular
)
allResources
.
Insert
(
item
.
Status
.
AcceptedNames
.
ShortNames
...
)
allKinds
.
Insert
(
item
.
Status
.
AcceptedNames
.
Kind
)
allKinds
.
Insert
(
item
.
Status
.
AcceptedNames
.
ListKind
)
}
return
allResources
,
allKinds
}
func
(
c
*
NamingConditionController
)
calculateNames
(
in
*
apiextensions
.
CustomResourceDefinition
)
(
apiextensions
.
CustomResourceDefinitionNames
,
apiextensions
.
CustomResourceDefinitionCondition
)
{
// Get the names that have already been claimed
allResources
,
allKinds
:=
c
.
getAcceptedNamesForGroup
(
in
.
Spec
.
Group
)
condition
:=
apiextensions
.
CustomResourceDefinitionCondition
{
Type
:
apiextensions
.
NameConflict
,
Status
:
apiextensions
.
ConditionUnknown
,
}
requestedNames
:=
in
.
Spec
.
Names
acceptedNames
:=
in
.
Status
.
AcceptedNames
newNames
:=
in
.
Status
.
AcceptedNames
// Check each name for mismatches. If there's a mismatch between spec and status, then try to deconflict.
// Continue on errors so that the status is the best match possible
if
err
:=
equalToAcceptedOrFresh
(
requestedNames
.
Plural
,
acceptedNames
.
Plural
,
allResources
);
err
!=
nil
{
condition
.
Status
=
apiextensions
.
ConditionTrue
condition
.
Reason
=
"Plural"
condition
.
Message
=
err
.
Error
()
}
else
{
newNames
.
Plural
=
requestedNames
.
Plural
}
if
err
:=
equalToAcceptedOrFresh
(
requestedNames
.
Singular
,
acceptedNames
.
Singular
,
allResources
);
err
!=
nil
{
condition
.
Status
=
apiextensions
.
ConditionTrue
condition
.
Reason
=
"Singular"
condition
.
Message
=
err
.
Error
()
}
else
{
newNames
.
Singular
=
requestedNames
.
Singular
}
if
!
reflect
.
DeepEqual
(
requestedNames
.
ShortNames
,
acceptedNames
.
ShortNames
)
{
errs
:=
[]
error
{}
existingShortNames
:=
sets
.
NewString
(
acceptedNames
.
ShortNames
...
)
for
_
,
shortName
:=
range
requestedNames
.
ShortNames
{
// if the shortname is already ours, then we're fine
if
existingShortNames
.
Has
(
shortName
)
{
continue
}
if
err
:=
equalToAcceptedOrFresh
(
shortName
,
""
,
allResources
);
err
!=
nil
{
errs
=
append
(
errs
,
err
)
}
}
if
err
:=
utilerrors
.
NewAggregate
(
errs
);
err
!=
nil
{
condition
.
Status
=
apiextensions
.
ConditionTrue
condition
.
Reason
=
"ShortNames"
condition
.
Message
=
err
.
Error
()
}
else
{
newNames
.
ShortNames
=
requestedNames
.
ShortNames
}
}
if
err
:=
equalToAcceptedOrFresh
(
requestedNames
.
Kind
,
acceptedNames
.
Kind
,
allKinds
);
err
!=
nil
{
condition
.
Status
=
apiextensions
.
ConditionTrue
condition
.
Reason
=
"Kind"
condition
.
Message
=
err
.
Error
()
}
else
{
newNames
.
Kind
=
requestedNames
.
Kind
}
if
err
:=
equalToAcceptedOrFresh
(
requestedNames
.
ListKind
,
acceptedNames
.
ListKind
,
allKinds
);
err
!=
nil
{
condition
.
Status
=
apiextensions
.
ConditionTrue
condition
.
Reason
=
"ListKind"
condition
.
Message
=
err
.
Error
()
}
else
{
newNames
.
ListKind
=
requestedNames
.
ListKind
}
// if we haven't changed the condition, then our names must be good.
if
condition
.
Status
==
apiextensions
.
ConditionUnknown
{
condition
.
Status
=
apiextensions
.
ConditionFalse
condition
.
Reason
=
"NoConflicts"
condition
.
Message
=
"no conflicts found"
}
return
newNames
,
condition
}
func
equalToAcceptedOrFresh
(
requestedName
,
acceptedName
string
,
usedNames
sets
.
String
)
error
{
if
requestedName
==
acceptedName
{
return
nil
}
if
!
usedNames
.
Has
(
requestedName
)
{
return
nil
}
return
fmt
.
Errorf
(
"%q is already in use"
,
requestedName
)
}
func
(
c
*
NamingConditionController
)
sync
(
key
string
)
error
{
inCustomResourceDefinition
,
err
:=
c
.
crdLister
.
Get
(
key
)
if
apierrors
.
IsNotFound
(
err
)
{
return
nil
}
if
err
!=
nil
{
return
err
}
acceptedNames
,
namingCondition
:=
c
.
calculateNames
(
inCustomResourceDefinition
)
// nothing to do if accepted names and NameConflict condition didn't change
if
reflect
.
DeepEqual
(
inCustomResourceDefinition
.
Status
.
AcceptedNames
,
acceptedNames
)
&&
apiextensions
.
IsCRDConditionEquivalent
(
&
namingCondition
,
apiextensions
.
FindCRDCondition
(
inCustomResourceDefinition
,
apiextensions
.
NameConflict
))
{
return
nil
}
crd
:=
&
apiextensions
.
CustomResourceDefinition
{}
if
err
:=
apiextensions
.
DeepCopy_apiextensions_CustomResourceDefinition
(
inCustomResourceDefinition
,
crd
,
cloner
);
err
!=
nil
{
return
err
}
crd
.
Status
.
AcceptedNames
=
acceptedNames
apiextensions
.
SetCRDCondition
(
crd
,
namingCondition
)
updatedObj
,
err
:=
c
.
crdClient
.
CustomResourceDefinitions
()
.
UpdateStatus
(
crd
)
if
err
!=
nil
{
return
err
}
// if the update was successful, go ahead and add the entry to the mutation cache
c
.
crdMutationCache
.
Mutation
(
updatedObj
)
// we updated our status, so we may be releasing a name. When this happens, we need to rekick everything in our group
// if we fail to rekick, just return as normal. We'll get everything on a resync
list
,
err
:=
c
.
crdLister
.
List
(
labels
.
Everything
())
if
err
!=
nil
{
return
nil
}
for
_
,
curr
:=
range
list
{
if
curr
.
Spec
.
Group
==
crd
.
Spec
.
Group
{
c
.
queue
.
Add
(
curr
.
Name
)
}
}
return
nil
}
func
(
c
*
NamingConditionController
)
Run
(
stopCh
<-
chan
struct
{})
{
defer
utilruntime
.
HandleCrash
()
defer
c
.
queue
.
ShutDown
()
glog
.
Infof
(
"Starting NamingConditionController"
)
defer
glog
.
Infof
(
"Shutting down NamingConditionController"
)
if
!
cache
.
WaitForCacheSync
(
stopCh
,
c
.
crdSynced
)
{
return
}
// only start one worker thread since its a slow moving API and the naming conflict resolution bits aren't thread-safe
go
wait
.
Until
(
c
.
runWorker
,
time
.
Second
,
stopCh
)
<-
stopCh
}
func
(
c
*
NamingConditionController
)
runWorker
()
{
for
c
.
processNextWorkItem
()
{
}
}
// processNextWorkItem deals with one key off the queue. It returns false when it's time to quit.
func
(
c
*
NamingConditionController
)
processNextWorkItem
()
bool
{
key
,
quit
:=
c
.
queue
.
Get
()
if
quit
{
return
false
}
defer
c
.
queue
.
Done
(
key
)
err
:=
c
.
syncFn
(
key
.
(
string
))
if
err
==
nil
{
c
.
queue
.
Forget
(
key
)
return
true
}
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"%v failed with: %v"
,
key
,
err
))
c
.
queue
.
AddRateLimited
(
key
)
return
true
}
func
(
c
*
NamingConditionController
)
enqueue
(
obj
*
apiextensions
.
CustomResourceDefinition
)
{
key
,
err
:=
cache
.
DeletionHandlingMetaNamespaceKeyFunc
(
obj
)
if
err
!=
nil
{
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"Couldn't get key for object %#v: %v"
,
obj
,
err
))
return
}
c
.
queue
.
Add
(
key
)
}
func
(
c
*
NamingConditionController
)
addCustomResourceDefinition
(
obj
interface
{})
{
castObj
:=
obj
.
(
*
apiextensions
.
CustomResourceDefinition
)
glog
.
V
(
4
)
.
Infof
(
"Adding %s"
,
castObj
.
Name
)
c
.
enqueue
(
castObj
)
}
func
(
c
*
NamingConditionController
)
updateCustomResourceDefinition
(
obj
,
_
interface
{})
{
castObj
:=
obj
.
(
*
apiextensions
.
CustomResourceDefinition
)
glog
.
V
(
4
)
.
Infof
(
"Updating %s"
,
castObj
.
Name
)
c
.
enqueue
(
castObj
)
}
func
(
c
*
NamingConditionController
)
deleteCustomResourceDefinition
(
obj
interface
{})
{
castObj
,
ok
:=
obj
.
(
*
apiextensions
.
CustomResourceDefinition
)
if
!
ok
{
tombstone
,
ok
:=
obj
.
(
cache
.
DeletedFinalStateUnknown
)
if
!
ok
{
glog
.
Errorf
(
"Couldn't get object from tombstone %#v"
,
obj
)
return
}
castObj
,
ok
=
tombstone
.
Obj
.
(
*
apiextensions
.
CustomResourceDefinition
)
if
!
ok
{
glog
.
Errorf
(
"Tombstone contained object that is not expected %#v"
,
obj
)
return
}
}
glog
.
V
(
4
)
.
Infof
(
"Deleting %q"
,
castObj
.
Name
)
c
.
enqueue
(
castObj
)
}
staging/src/k8s.io/kube-apiextensions-server/pkg/controller/status/naming_controller_test.go
0 → 100644
View file @
cb604f75
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package
status
import
(
"reflect"
"strings"
"testing"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/kube-apiextensions-server/pkg/apis/apiextensions"
listers
"k8s.io/kube-apiextensions-server/pkg/client/listers/apiextensions/internalversion"
)
type
crdBuilder
struct
{
curr
apiextensions
.
CustomResourceDefinition
}
func
newCRD
(
name
string
)
*
crdBuilder
{
tokens
:=
strings
.
SplitN
(
name
,
"."
,
2
)
return
&
crdBuilder
{
curr
:
apiextensions
.
CustomResourceDefinition
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
name
},
Spec
:
apiextensions
.
CustomResourceDefinitionSpec
{
Group
:
tokens
[
1
],
Names
:
apiextensions
.
CustomResourceDefinitionNames
{
Plural
:
tokens
[
0
],
},
},
},
}
}
func
(
b
*
crdBuilder
)
SpecNames
(
plural
,
singular
,
kind
,
listKind
string
,
shortNames
...
string
)
*
crdBuilder
{
b
.
curr
.
Spec
.
Names
.
Plural
=
plural
b
.
curr
.
Spec
.
Names
.
Singular
=
singular
b
.
curr
.
Spec
.
Names
.
Kind
=
kind
b
.
curr
.
Spec
.
Names
.
ListKind
=
listKind
b
.
curr
.
Spec
.
Names
.
ShortNames
=
shortNames
return
b
}
func
(
b
*
crdBuilder
)
StatusNames
(
plural
,
singular
,
kind
,
listKind
string
,
shortNames
...
string
)
*
crdBuilder
{
b
.
curr
.
Status
.
AcceptedNames
.
Plural
=
plural
b
.
curr
.
Status
.
AcceptedNames
.
Singular
=
singular
b
.
curr
.
Status
.
AcceptedNames
.
Kind
=
kind
b
.
curr
.
Status
.
AcceptedNames
.
ListKind
=
listKind
b
.
curr
.
Status
.
AcceptedNames
.
ShortNames
=
shortNames
return
b
}
func
names
(
plural
,
singular
,
kind
,
listKind
string
,
shortNames
...
string
)
apiextensions
.
CustomResourceDefinitionNames
{
ret
:=
apiextensions
.
CustomResourceDefinitionNames
{
Plural
:
plural
,
Singular
:
singular
,
Kind
:
kind
,
ListKind
:
listKind
,
ShortNames
:
shortNames
,
}
return
ret
}
func
(
b
*
crdBuilder
)
NewOrDie
()
*
apiextensions
.
CustomResourceDefinition
{
return
&
b
.
curr
}
var
goodCondition
=
apiextensions
.
CustomResourceDefinitionCondition
{
Type
:
apiextensions
.
NameConflict
,
Status
:
apiextensions
.
ConditionFalse
,
Reason
:
"NoConflicts"
,
Message
:
"no conflicts found"
,
}
func
badCondition
(
reason
,
message
string
)
apiextensions
.
CustomResourceDefinitionCondition
{
return
apiextensions
.
CustomResourceDefinitionCondition
{
Type
:
apiextensions
.
NameConflict
,
Status
:
apiextensions
.
ConditionTrue
,
Reason
:
reason
,
Message
:
message
,
}
}
func
TestSync
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
in
*
apiextensions
.
CustomResourceDefinition
existing
[]
*
apiextensions
.
CustomResourceDefinition
expectedNames
apiextensions
.
CustomResourceDefinitionNames
expectedCondition
apiextensions
.
CustomResourceDefinitionCondition
}{
{
name
:
"first resource"
,
in
:
newCRD
(
"alfa.bravo.com"
)
.
NewOrDie
(),
existing
:
[]
*
apiextensions
.
CustomResourceDefinition
{},
expectedNames
:
apiextensions
.
CustomResourceDefinitionNames
{
Plural
:
"alfa"
,
},
expectedCondition
:
goodCondition
,
},
{
name
:
"different groups"
,
in
:
newCRD
(
"alfa.bravo.com"
)
.
SpecNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
NewOrDie
(),
existing
:
[]
*
apiextensions
.
CustomResourceDefinition
{
newCRD
(
"alfa.charlie.com"
)
.
StatusNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
NewOrDie
(),
},
expectedNames
:
names
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
),
expectedCondition
:
goodCondition
,
},
{
name
:
"conflict plural to singular"
,
in
:
newCRD
(
"alfa.bravo.com"
)
.
SpecNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
NewOrDie
(),
existing
:
[]
*
apiextensions
.
CustomResourceDefinition
{
newCRD
(
"india.bravo.com"
)
.
StatusNames
(
"india"
,
"alfa"
,
""
,
""
)
.
NewOrDie
(),
},
expectedNames
:
names
(
""
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
),
expectedCondition
:
badCondition
(
"Plural"
,
`"alfa" is already in use`
),
},
{
name
:
"conflict singular to shortName"
,
in
:
newCRD
(
"alfa.bravo.com"
)
.
SpecNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
NewOrDie
(),
existing
:
[]
*
apiextensions
.
CustomResourceDefinition
{
newCRD
(
"india.bravo.com"
)
.
StatusNames
(
"india"
,
"indias"
,
""
,
""
,
"delta-singular"
)
.
NewOrDie
(),
},
expectedNames
:
names
(
"alfa"
,
""
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
),
expectedCondition
:
badCondition
(
"Singular"
,
`"delta-singular" is already in use`
),
},
{
name
:
"conflict on shortName to shortName"
,
in
:
newCRD
(
"alfa.bravo.com"
)
.
SpecNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
NewOrDie
(),
existing
:
[]
*
apiextensions
.
CustomResourceDefinition
{
newCRD
(
"india.bravo.com"
)
.
StatusNames
(
"india"
,
"indias"
,
""
,
""
,
"hotel-shortname-2"
)
.
NewOrDie
(),
},
expectedNames
:
names
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
),
expectedCondition
:
badCondition
(
"ShortNames"
,
`"hotel-shortname-2" is already in use`
),
},
{
name
:
"conflict on kind to listkind"
,
in
:
newCRD
(
"alfa.bravo.com"
)
.
SpecNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
NewOrDie
(),
existing
:
[]
*
apiextensions
.
CustomResourceDefinition
{
newCRD
(
"india.bravo.com"
)
.
StatusNames
(
"india"
,
"indias"
,
""
,
"echo-kind"
)
.
NewOrDie
(),
},
expectedNames
:
names
(
"alfa"
,
"delta-singular"
,
""
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
),
expectedCondition
:
badCondition
(
"Kind"
,
`"echo-kind" is already in use`
),
},
{
name
:
"conflict on listkind to kind"
,
in
:
newCRD
(
"alfa.bravo.com"
)
.
SpecNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
NewOrDie
(),
existing
:
[]
*
apiextensions
.
CustomResourceDefinition
{
newCRD
(
"india.bravo.com"
)
.
StatusNames
(
"india"
,
"indias"
,
"foxtrot-listkind"
,
""
)
.
NewOrDie
(),
},
expectedNames
:
names
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
""
,
"golf-shortname-1"
,
"hotel-shortname-2"
),
expectedCondition
:
badCondition
(
"ListKind"
,
`"foxtrot-listkind" is already in use`
),
},
{
name
:
"no conflict on resource and kind"
,
in
:
newCRD
(
"alfa.bravo.com"
)
.
SpecNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
NewOrDie
(),
existing
:
[]
*
apiextensions
.
CustomResourceDefinition
{
newCRD
(
"india.bravo.com"
)
.
StatusNames
(
"india"
,
"echo-kind"
,
""
,
""
)
.
NewOrDie
(),
},
expectedNames
:
names
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
),
expectedCondition
:
goodCondition
,
},
{
name
:
"merge on conflicts"
,
in
:
newCRD
(
"alfa.bravo.com"
)
.
SpecNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
StatusNames
(
"zulu"
,
"yankee-singular"
,
"xray-kind"
,
"whiskey-listkind"
,
"victor-shortname-1"
,
"uniform-shortname-2"
)
.
NewOrDie
(),
existing
:
[]
*
apiextensions
.
CustomResourceDefinition
{
newCRD
(
"india.bravo.com"
)
.
StatusNames
(
"india"
,
"indias"
,
"foxtrot-listkind"
,
""
,
"delta-singular"
)
.
NewOrDie
(),
},
expectedNames
:
names
(
"alfa"
,
"yankee-singular"
,
"echo-kind"
,
"whiskey-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
),
expectedCondition
:
badCondition
(
"ListKind"
,
`"foxtrot-listkind" is already in use`
),
},
{
name
:
"merge on conflicts shortNames as one"
,
in
:
newCRD
(
"alfa.bravo.com"
)
.
SpecNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
StatusNames
(
"zulu"
,
"yankee-singular"
,
"xray-kind"
,
"whiskey-listkind"
,
"victor-shortname-1"
,
"uniform-shortname-2"
)
.
NewOrDie
(),
existing
:
[]
*
apiextensions
.
CustomResourceDefinition
{
newCRD
(
"india.bravo.com"
)
.
StatusNames
(
"india"
,
"indias"
,
"foxtrot-listkind"
,
""
,
"delta-singular"
,
"golf-shortname-1"
)
.
NewOrDie
(),
},
expectedNames
:
names
(
"alfa"
,
"yankee-singular"
,
"echo-kind"
,
"whiskey-listkind"
,
"victor-shortname-1"
,
"uniform-shortname-2"
),
expectedCondition
:
badCondition
(
"ListKind"
,
`"foxtrot-listkind" is already in use`
),
},
{
name
:
"no conflicts on self"
,
in
:
newCRD
(
"alfa.bravo.com"
)
.
SpecNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
StatusNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
NewOrDie
(),
existing
:
[]
*
apiextensions
.
CustomResourceDefinition
{
newCRD
(
"alfa.bravo.com"
)
.
SpecNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
StatusNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
NewOrDie
(),
},
expectedNames
:
names
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
),
expectedCondition
:
goodCondition
,
},
{
name
:
"no conflicts on self, remove shortname"
,
in
:
newCRD
(
"alfa.bravo.com"
)
.
SpecNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
)
.
StatusNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
NewOrDie
(),
existing
:
[]
*
apiextensions
.
CustomResourceDefinition
{
newCRD
(
"alfa.bravo.com"
)
.
SpecNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
StatusNames
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
,
"hotel-shortname-2"
)
.
NewOrDie
(),
},
expectedNames
:
names
(
"alfa"
,
"delta-singular"
,
"echo-kind"
,
"foxtrot-listkind"
,
"golf-shortname-1"
),
expectedCondition
:
goodCondition
,
},
}
for
_
,
tc
:=
range
tests
{
crdIndexer
:=
cache
.
NewIndexer
(
cache
.
MetaNamespaceKeyFunc
,
cache
.
Indexers
{
cache
.
NamespaceIndex
:
cache
.
MetaNamespaceIndexFunc
})
for
_
,
obj
:=
range
tc
.
existing
{
crdIndexer
.
Add
(
obj
)
}
c
:=
NamingConditionController
{
crdLister
:
listers
.
NewCustomResourceDefinitionLister
(
crdIndexer
),
crdMutationCache
:
cache
.
NewIntegerResourceVersionMutationCache
(
crdIndexer
,
crdIndexer
),
}
actualNames
,
actualCondition
:=
c
.
calculateNames
(
tc
.
in
)
if
e
,
a
:=
tc
.
expectedNames
,
actualNames
;
!
reflect
.
DeepEqual
(
e
,
a
)
{
t
.
Errorf
(
"%v expected %v, got %#v"
,
tc
.
name
,
e
,
a
)
}
if
e
,
a
:=
tc
.
expectedCondition
,
actualCondition
;
!
apiextensions
.
IsCRDConditionEquivalent
(
&
e
,
&
a
)
{
t
.
Errorf
(
"%v expected %v, got %v"
,
tc
.
name
,
e
,
a
)
}
}
}
staging/src/k8s.io/kube-apiextensions-server/pkg/registry/customresourcedefinition/BUILD
View file @
cb604f75
...
...
@@ -22,6 +22,7 @@ go_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",
"//vendor/k8s.io/apiserver/pkg/storage:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
"//vendor/k8s.io/kube-apiextensions-server/pkg/apis/apiextensions:go_default_library",
...
...
staging/src/k8s.io/kube-apiextensions-server/pkg/registry/customresourcedefinition/etcd.go
View file @
cb604f75
...
...
@@ -18,8 +18,10 @@ package customresourcedefinition
import
(
"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/kube-apiextensions-server/pkg/apis/apiextensions"
)
...
...
@@ -52,3 +54,28 @@ func NewREST(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) *REST
}
return
&
REST
{
store
}
}
// NewStatusREST makes a RESTStorage for status that has more limited options.
// It is based on the original REST so that we can share the same underlying store
func
NewStatusREST
(
scheme
*
runtime
.
Scheme
,
rest
*
REST
)
*
StatusREST
{
statusStore
:=
*
rest
.
Store
statusStore
.
CreateStrategy
=
nil
statusStore
.
DeleteStrategy
=
nil
statusStore
.
UpdateStrategy
=
NewStatusStrategy
(
scheme
)
return
&
StatusREST
{
store
:
&
statusStore
}
}
type
StatusREST
struct
{
store
*
genericregistry
.
Store
}
var
_
=
rest
.
Updater
(
&
StatusREST
{})
func
(
r
*
StatusREST
)
New
()
runtime
.
Object
{
return
&
apiextensions
.
CustomResourceDefinition
{}
}
// Update alters the status subset of an object.
func
(
r
*
StatusREST
)
Update
(
ctx
genericapirequest
.
Context
,
name
string
,
objInfo
rest
.
UpdatedObjectInfo
)
(
runtime
.
Object
,
bool
,
error
)
{
return
r
.
store
.
Update
(
ctx
,
name
,
objInfo
)
}
staging/src/k8s.io/kube-apiextensions-server/pkg/registry/customresourcedefinition/strategy.go
View file @
cb604f75
...
...
@@ -32,44 +32,82 @@ import (
"k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/validation"
)
type
apiServerS
trategy
struct
{
type
s
trategy
struct
{
runtime
.
ObjectTyper
names
.
NameGenerator
}
func
NewStrategy
(
typer
runtime
.
ObjectTyper
)
apiServerS
trategy
{
return
apiServerS
trategy
{
typer
,
names
.
SimpleNameGenerator
}
func
NewStrategy
(
typer
runtime
.
ObjectTyper
)
s
trategy
{
return
s
trategy
{
typer
,
names
.
SimpleNameGenerator
}
}
func
(
apiServerS
trategy
)
NamespaceScoped
()
bool
{
func
(
s
trategy
)
NamespaceScoped
()
bool
{
return
false
}
func
(
apiServerS
trategy
)
PrepareForCreate
(
ctx
genericapirequest
.
Context
,
obj
runtime
.
Object
)
{
func
(
s
trategy
)
PrepareForCreate
(
ctx
genericapirequest
.
Context
,
obj
runtime
.
Object
)
{
}
func
(
apiServerS
trategy
)
PrepareForUpdate
(
ctx
genericapirequest
.
Context
,
obj
,
old
runtime
.
Object
)
{
func
(
s
trategy
)
PrepareForUpdate
(
ctx
genericapirequest
.
Context
,
obj
,
old
runtime
.
Object
)
{
}
func
(
apiServerS
trategy
)
Validate
(
ctx
genericapirequest
.
Context
,
obj
runtime
.
Object
)
field
.
ErrorList
{
func
(
s
trategy
)
Validate
(
ctx
genericapirequest
.
Context
,
obj
runtime
.
Object
)
field
.
ErrorList
{
return
validation
.
ValidateCustomResourceDefinition
(
obj
.
(
*
apiextensions
.
CustomResourceDefinition
))
}
func
(
apiServerS
trategy
)
AllowCreateOnUpdate
()
bool
{
func
(
s
trategy
)
AllowCreateOnUpdate
()
bool
{
return
false
}
func
(
apiServerS
trategy
)
AllowUnconditionalUpdate
()
bool
{
func
(
s
trategy
)
AllowUnconditionalUpdate
()
bool
{
return
false
}
func
(
apiServerS
trategy
)
Canonicalize
(
obj
runtime
.
Object
)
{
func
(
s
trategy
)
Canonicalize
(
obj
runtime
.
Object
)
{
}
func
(
apiServerS
trategy
)
ValidateUpdate
(
ctx
genericapirequest
.
Context
,
obj
,
old
runtime
.
Object
)
field
.
ErrorList
{
func
(
s
trategy
)
ValidateUpdate
(
ctx
genericapirequest
.
Context
,
obj
,
old
runtime
.
Object
)
field
.
ErrorList
{
return
validation
.
ValidateCustomResourceDefinitionUpdate
(
obj
.
(
*
apiextensions
.
CustomResourceDefinition
),
old
.
(
*
apiextensions
.
CustomResourceDefinition
))
}
type
statusStrategy
struct
{
runtime
.
ObjectTyper
names
.
NameGenerator
}
func
NewStatusStrategy
(
typer
runtime
.
ObjectTyper
)
statusStrategy
{
return
statusStrategy
{
typer
,
names
.
SimpleNameGenerator
}
}
func
(
statusStrategy
)
NamespaceScoped
()
bool
{
return
false
}
func
(
statusStrategy
)
PrepareForUpdate
(
ctx
genericapirequest
.
Context
,
obj
,
old
runtime
.
Object
)
{
newObj
:=
obj
.
(
*
apiextensions
.
CustomResourceDefinition
)
oldObj
:=
old
.
(
*
apiextensions
.
CustomResourceDefinition
)
newObj
.
Spec
=
oldObj
.
Spec
newObj
.
Labels
=
oldObj
.
Labels
newObj
.
Annotations
=
oldObj
.
Annotations
newObj
.
Finalizers
=
oldObj
.
Finalizers
newObj
.
OwnerReferences
=
oldObj
.
OwnerReferences
}
func
(
statusStrategy
)
AllowCreateOnUpdate
()
bool
{
return
false
}
func
(
statusStrategy
)
AllowUnconditionalUpdate
()
bool
{
return
false
}
func
(
statusStrategy
)
Canonicalize
(
obj
runtime
.
Object
)
{
}
func
(
statusStrategy
)
ValidateUpdate
(
ctx
genericapirequest
.
Context
,
obj
,
old
runtime
.
Object
)
field
.
ErrorList
{
return
validation
.
ValidateUpdateCustomResourceDefinitionStatus
(
obj
.
(
*
apiextensions
.
CustomResourceDefinition
),
old
.
(
*
apiextensions
.
CustomResourceDefinition
))
}
func
GetAttrs
(
obj
runtime
.
Object
)
(
labels
.
Set
,
fields
.
Set
,
error
)
{
apiserver
,
ok
:=
obj
.
(
*
apiextensions
.
CustomResourceDefinition
)
if
!
ok
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment