mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-01-05 23:47:50 +00:00
Merge pull request #29724 from brendandburns/thirdparty3
Automatic merge from submit-queue Fix third party APIResource reporting @polvi @caesarxuchao @deads2k This "fixes" some additional bugs in third party `APIResourceList` reporting. This code needs a bunch of cleanup, and more tests, but sending it out for a quick smell check review in case I'm doing something stupid. Fixes the bug referenced here: https://github.com/kubernetes/kubernetes/pull/28414#issuecomment-235346108 and in https://github.com/kubernetes/kubernetes/issues/23831 Fixes https://github.com/kubernetes/kubernetes/issues/25570
This commit is contained in:
@@ -144,7 +144,7 @@ type Master struct {
|
||||
// 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
|
||||
thirdPartyResources map[string]*thirdPartyEntry
|
||||
// protects the map
|
||||
thirdPartyResourcesLock sync.RWMutex
|
||||
// Useful for reliable testing. Shouldn't be used otherwise.
|
||||
@@ -157,7 +157,8 @@ type Master struct {
|
||||
// thirdPartyEntry combines objects storage and API group into one struct
|
||||
// for easy lookup.
|
||||
type thirdPartyEntry struct {
|
||||
storage *thirdpartyresourcedataetcd.REST
|
||||
// Map from plural resource name to entry
|
||||
storage map[string]*thirdpartyresourcedataetcd.REST
|
||||
group unversioned.APIGroup
|
||||
}
|
||||
|
||||
@@ -280,7 +281,7 @@ func (m *Master) InstallAPIs(c *Config) {
|
||||
if err != nil {
|
||||
glog.Fatalf("Error getting third party storage: %v", err)
|
||||
}
|
||||
m.thirdPartyResources = map[string]thirdPartyEntry{}
|
||||
m.thirdPartyResources = map[string]*thirdPartyEntry{}
|
||||
}
|
||||
|
||||
restOptionsGetter := func(resource unversioned.GroupResource) generic.RESTOptions {
|
||||
@@ -511,37 +512,60 @@ func (m *Master) getServersToValidate(c *Config) map[string]apiserver.Server {
|
||||
|
||||
// HasThirdPartyResource returns true if a particular third party resource currently installed.
|
||||
func (m *Master) HasThirdPartyResource(rsrc *extensions.ThirdPartyResource) (bool, error) {
|
||||
_, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(rsrc)
|
||||
kind, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(rsrc)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
path := makeThirdPartyPath(group)
|
||||
services := m.HandlerContainer.RegisteredWebServices()
|
||||
for ix := range services {
|
||||
if services[ix].RootPath() == path {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (m *Master) removeThirdPartyStorage(path string) error {
|
||||
m.thirdPartyResourcesLock.Lock()
|
||||
defer m.thirdPartyResourcesLock.Unlock()
|
||||
storage, found := m.thirdPartyResources[path]
|
||||
if found {
|
||||
if err := m.removeAllThirdPartyResources(storage.storage); err != nil {
|
||||
return err
|
||||
}
|
||||
entry := m.thirdPartyResources[path]
|
||||
if entry == nil {
|
||||
return false, nil
|
||||
}
|
||||
plural, _ := meta.KindToResource(unversioned.GroupVersionKind{
|
||||
Group: group,
|
||||
Version: rsrc.Versions[0].Name,
|
||||
Kind: kind,
|
||||
})
|
||||
_, found := entry.storage[plural.Resource]
|
||||
return found, nil
|
||||
}
|
||||
|
||||
func (m *Master) 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.removeAllThirdPartyResources(storage); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(entry.storage, resource)
|
||||
if len(entry.storage) == 0 {
|
||||
delete(m.thirdPartyResources, path)
|
||||
m.RemoveAPIGroupForDiscovery(getThirdPartyGroupName(path))
|
||||
} else {
|
||||
m.thirdPartyResources[path] = entry
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveThirdPartyResource removes all resources matching `path`. Also deletes any stored data
|
||||
func (m *Master) RemoveThirdPartyResource(path string) error {
|
||||
if err := m.removeThirdPartyStorage(path); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -575,28 +599,58 @@ func (m *Master) removeAllThirdPartyResources(registry *thirdpartyresourcedataet
|
||||
}
|
||||
|
||||
// ListThirdPartyResources lists all currently installed third party resources
|
||||
// The format is <path>/<resource-plural-name>
|
||||
func (m *Master) ListThirdPartyResources() []string {
|
||||
m.thirdPartyResourcesLock.RLock()
|
||||
defer m.thirdPartyResourcesLock.RUnlock()
|
||||
result := []string{}
|
||||
for key := range m.thirdPartyResources {
|
||||
result = append(result, key)
|
||||
for rsrc := range m.thirdPartyResources[key].storage {
|
||||
result = append(result, key+"/"+rsrc)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *Master) hasThirdPartyResourceStorage(path string) bool {
|
||||
func (m *Master) getExistingThirdPartyResources(path string) []unversioned.APIResource {
|
||||
result := []unversioned.APIResource{}
|
||||
m.thirdPartyResourcesLock.Lock()
|
||||
defer m.thirdPartyResourcesLock.Unlock()
|
||||
entry := m.thirdPartyResources[path]
|
||||
if entry != nil {
|
||||
for key, obj := range entry.storage {
|
||||
result = append(result, unversioned.APIResource{
|
||||
Name: key,
|
||||
Namespaced: true,
|
||||
Kind: obj.Kind(),
|
||||
})
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *Master) hasThirdPartyGroupStorage(path string) bool {
|
||||
m.thirdPartyResourcesLock.Lock()
|
||||
defer m.thirdPartyResourcesLock.Unlock()
|
||||
_, found := m.thirdPartyResources[path]
|
||||
return found
|
||||
}
|
||||
|
||||
func (m *Master) addThirdPartyResourceStorage(path string, storage *thirdpartyresourcedataetcd.REST, apiGroup unversioned.APIGroup) {
|
||||
func (m *Master) addThirdPartyResourceStorage(path, resource string, storage *thirdpartyresourcedataetcd.REST, apiGroup unversioned.APIGroup) {
|
||||
m.thirdPartyResourcesLock.Lock()
|
||||
defer m.thirdPartyResourcesLock.Unlock()
|
||||
m.thirdPartyResources[path] = thirdPartyEntry{storage, apiGroup}
|
||||
m.AddAPIGroupForDiscovery(apiGroup)
|
||||
entry, found := m.thirdPartyResources[path]
|
||||
if entry == nil {
|
||||
entry = &thirdPartyEntry{
|
||||
group: apiGroup,
|
||||
storage: map[string]*thirdpartyresourcedataetcd.REST{},
|
||||
}
|
||||
m.thirdPartyResources[path] = entry
|
||||
}
|
||||
entry.storage[resource] = storage
|
||||
if !found {
|
||||
m.AddAPIGroupForDiscovery(apiGroup)
|
||||
}
|
||||
}
|
||||
|
||||
// InstallThirdPartyResource installs a third party resource specified by 'rsrc'. When a resource is
|
||||
@@ -618,17 +672,6 @@ func (m *Master) InstallThirdPartyResource(rsrc *extensions.ThirdPartyResource)
|
||||
})
|
||||
path := makeThirdPartyPath(group)
|
||||
|
||||
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.hasThirdPartyResourceStorage(path) {
|
||||
return thirdparty.UpdateREST(m.HandlerContainer)
|
||||
}
|
||||
|
||||
if err := thirdparty.InstallREST(m.HandlerContainer); err != nil {
|
||||
glog.Errorf("Unable to setup thirdparty api: %v", err)
|
||||
}
|
||||
groupVersion := unversioned.GroupVersionForDiscovery{
|
||||
GroupVersion: group + "/" + rsrc.Versions[0].Name,
|
||||
Version: rsrc.Versions[0].Name,
|
||||
@@ -638,9 +681,22 @@ func (m *Master) InstallThirdPartyResource(rsrc *extensions.ThirdPartyResource)
|
||||
Versions: []unversioned.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].(*thirdpartyresourcedataetcd.REST), apiGroup)
|
||||
return thirdparty.UpdateREST(m.HandlerContainer)
|
||||
}
|
||||
|
||||
if err := thirdparty.InstallREST(m.HandlerContainer); err != nil {
|
||||
glog.Errorf("Unable to setup thirdparty api: %v", err)
|
||||
}
|
||||
apiserver.AddGroupWebService(api.Codecs, m.HandlerContainer, path, apiGroup)
|
||||
|
||||
m.addThirdPartyResourceStorage(path, thirdparty.Storage[plural.Resource].(*thirdpartyresourcedataetcd.REST), apiGroup)
|
||||
m.addThirdPartyResourceStorage(path, plural.Resource, thirdparty.Storage[plural.Resource].(*thirdpartyresourcedataetcd.REST), apiGroup)
|
||||
apiserver.InstallServiceErrorHandler(api.Codecs, m.HandlerContainer, m.NewRequestInfoResolver(), []string{thirdparty.GroupVersion.String()})
|
||||
return nil
|
||||
}
|
||||
@@ -656,8 +712,6 @@ func (m *Master) thirdpartyapi(group, kind, version, pluralResource string) *api
|
||||
kind,
|
||||
)
|
||||
|
||||
apiRoot := makeThirdPartyPath("")
|
||||
|
||||
storage := map[string]rest.Storage{
|
||||
pluralResource: resourceStorage,
|
||||
}
|
||||
@@ -666,6 +720,7 @@ func (m *Master) thirdpartyapi(group, kind, version, pluralResource string) *api
|
||||
internalVersion := unversioned.GroupVersion{Group: group, Version: runtime.APIVersionInternal}
|
||||
externalVersion := unversioned.GroupVersion{Group: group, Version: version}
|
||||
|
||||
apiRoot := makeThirdPartyPath("")
|
||||
return &apiserver.APIGroupVersion{
|
||||
Root: apiRoot,
|
||||
GroupVersion: externalVersion,
|
||||
@@ -687,6 +742,8 @@ func (m *Master) thirdpartyapi(group, kind, version, pluralResource string) *api
|
||||
Context: m.RequestContextMapper,
|
||||
|
||||
MinRequestTimeout: m.MinRequestTimeout,
|
||||
|
||||
ResourceLister: dynamicLister{m, makeThirdPartyPath(group)},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user