thirdparty_controller.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package master
  14. import (
  15. "fmt"
  16. "strings"
  17. "k8s.io/kubernetes/pkg/api"
  18. "k8s.io/kubernetes/pkg/api/unversioned"
  19. expapi "k8s.io/kubernetes/pkg/apis/extensions"
  20. "k8s.io/kubernetes/pkg/apiserver"
  21. thirdpartyresourceetcd "k8s.io/kubernetes/pkg/registry/thirdpartyresource/etcd"
  22. "k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata"
  23. "k8s.io/kubernetes/pkg/runtime"
  24. "k8s.io/kubernetes/pkg/util/sets"
  25. )
  26. const thirdpartyprefix = "/apis"
  27. // dynamicLister is used to list resources for dynamic third party
  28. // apis. It implements the apiserver.APIResourceLister interface
  29. type dynamicLister struct {
  30. m *Master
  31. path string
  32. }
  33. func (d dynamicLister) ListAPIResources() []unversioned.APIResource {
  34. return d.m.getExistingThirdPartyResources(d.path)
  35. }
  36. var _ apiserver.APIResourceLister = &dynamicLister{}
  37. func makeThirdPartyPath(group string) string {
  38. if len(group) == 0 {
  39. return thirdpartyprefix
  40. }
  41. return thirdpartyprefix + "/" + group
  42. }
  43. func getThirdPartyGroupName(path string) string {
  44. return strings.TrimPrefix(strings.TrimPrefix(path, thirdpartyprefix), "/")
  45. }
  46. // resourceInterface is the interface for the parts of the master that know how to add/remove
  47. // third party resources. Extracted into an interface for injection for testing.
  48. type resourceInterface interface {
  49. // Remove a third party resource based on the RESTful path for that resource, the path is <api-group-path>/<resource-plural-name>
  50. RemoveThirdPartyResource(path string) error
  51. // Install a third party resource described by 'rsrc'
  52. InstallThirdPartyResource(rsrc *expapi.ThirdPartyResource) error
  53. // Is a particular third party resource currently installed?
  54. HasThirdPartyResource(rsrc *expapi.ThirdPartyResource) (bool, error)
  55. // List all currently installed third party resources, the returned
  56. // names are of the form <api-group-path>/<resource-plural-name>
  57. ListThirdPartyResources() []string
  58. }
  59. // ThirdPartyController is a control loop that knows how to synchronize ThirdPartyResource objects with
  60. // RESTful resources which are present in the API server.
  61. type ThirdPartyController struct {
  62. master resourceInterface
  63. thirdPartyResourceRegistry *thirdpartyresourceetcd.REST
  64. }
  65. // Synchronize a single resource with RESTful resources on the master
  66. func (t *ThirdPartyController) SyncOneResource(rsrc *expapi.ThirdPartyResource) error {
  67. // TODO: we also need to test if the existing installed resource matches the resource we are sync-ing.
  68. // Currently, if there is an older, incompatible resource installed, we won't remove it. We should detect
  69. // older, incompatible resources and remove them before testing if the resource exists.
  70. hasResource, err := t.master.HasThirdPartyResource(rsrc)
  71. if err != nil {
  72. return err
  73. }
  74. if !hasResource {
  75. return t.master.InstallThirdPartyResource(rsrc)
  76. }
  77. return nil
  78. }
  79. // Synchronize all resources with RESTful resources on the master
  80. func (t *ThirdPartyController) SyncResources() error {
  81. list, err := t.thirdPartyResourceRegistry.List(api.NewDefaultContext(), nil)
  82. if err != nil {
  83. return err
  84. }
  85. return t.syncResourceList(list)
  86. }
  87. func (t *ThirdPartyController) syncResourceList(list runtime.Object) error {
  88. existing := sets.String{}
  89. switch list := list.(type) {
  90. case *expapi.ThirdPartyResourceList:
  91. // Loop across all schema objects for third party resources
  92. for ix := range list.Items {
  93. item := &list.Items[ix]
  94. // extract the api group and resource kind from the schema
  95. _, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(item)
  96. if err != nil {
  97. return err
  98. }
  99. // place it in the set of resources that we expect, so that we don't delete it in the delete pass
  100. existing.Insert(makeThirdPartyPath(group))
  101. // ensure a RESTful resource for this schema exists on the master
  102. if err := t.SyncOneResource(item); err != nil {
  103. return err
  104. }
  105. }
  106. default:
  107. return fmt.Errorf("expected a *ThirdPartyResourceList, got %#v", list)
  108. }
  109. // deletion phase, get all installed RESTful resources
  110. installed := t.master.ListThirdPartyResources()
  111. for _, installedAPI := range installed {
  112. found := false
  113. // search across the expected restful resources to see if this resource belongs to one of the expected ones
  114. for _, apiPath := range existing.List() {
  115. if installedAPI == apiPath || strings.HasPrefix(installedAPI, apiPath+"/") {
  116. found = true
  117. break
  118. }
  119. }
  120. // not expected, delete the resource
  121. if !found {
  122. if err := t.master.RemoveThirdPartyResource(installedAPI); err != nil {
  123. return err
  124. }
  125. }
  126. }
  127. return nil
  128. }