2015-09-09 21:36:02 +00:00
|
|
|
/*
|
|
|
|
Copyright 2014 The Kubernetes Authors All rights reserved.
|
|
|
|
|
|
|
|
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 master
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"k8s.io/kubernetes/pkg/api"
|
2015-10-09 22:04:41 +00:00
|
|
|
expapi "k8s.io/kubernetes/pkg/apis/extensions"
|
2015-09-09 21:36:02 +00:00
|
|
|
thirdpartyresourceetcd "k8s.io/kubernetes/pkg/registry/thirdpartyresource/etcd"
|
|
|
|
"k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata"
|
|
|
|
"k8s.io/kubernetes/pkg/runtime"
|
|
|
|
"k8s.io/kubernetes/pkg/util/sets"
|
|
|
|
)
|
|
|
|
|
2015-09-29 21:36:47 +00:00
|
|
|
const thirdpartyprefix = "/apis"
|
2015-09-09 21:36:02 +00:00
|
|
|
|
|
|
|
func makeThirdPartyPath(group string) string {
|
2015-09-29 21:36:47 +00:00
|
|
|
if len(group) == 0 {
|
|
|
|
return thirdpartyprefix
|
|
|
|
}
|
|
|
|
return thirdpartyprefix + "/" + group
|
2015-09-09 21:36:02 +00:00
|
|
|
}
|
|
|
|
|
2016-02-03 01:29:17 +00:00
|
|
|
func getThirdPartyGroupName(path string) string {
|
|
|
|
return strings.TrimPrefix(strings.TrimPrefix(path, thirdpartyprefix), "/")
|
|
|
|
}
|
|
|
|
|
2015-09-14 20:37:40 +00:00
|
|
|
// resourceInterface is the interface for the parts of the master that know how to add/remove
|
2015-09-09 21:36:02 +00:00
|
|
|
// 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
|
|
|
|
RemoveThirdPartyResource(path string) error
|
|
|
|
// Install a third party resource described by 'rsrc'
|
|
|
|
InstallThirdPartyResource(rsrc *expapi.ThirdPartyResource) error
|
|
|
|
// Is a particular third party resource currently installed?
|
|
|
|
HasThirdPartyResource(rsrc *expapi.ThirdPartyResource) (bool, error)
|
|
|
|
// List all currently installed third party resources
|
|
|
|
ListThirdPartyResources() []string
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
thirdPartyResourceRegistry *thirdpartyresourceetcd.REST
|
|
|
|
}
|
|
|
|
|
|
|
|
// Synchronize a single resource with RESTful resources on the master
|
|
|
|
func (t *ThirdPartyController) SyncOneResource(rsrc *expapi.ThirdPartyResource) error {
|
2015-09-14 20:37:40 +00:00
|
|
|
// 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.
|
2015-09-09 21:36:02 +00:00
|
|
|
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 {
|
2015-10-27 13:47:58 +00:00
|
|
|
list, err := t.thirdPartyResourceRegistry.List(api.NewDefaultContext(), nil)
|
2015-09-09 21:36:02 +00:00
|
|
|
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 *expapi.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() {
|
2015-09-14 20:37:40 +00:00
|
|
|
if installedAPI == apiPath || strings.HasPrefix(installedAPI, apiPath+"/") {
|
2015-09-09 21:36:02 +00:00
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// not expected, delete the resource
|
|
|
|
if !found {
|
|
|
|
if err := t.master.RemoveThirdPartyResource(installedAPI); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|