etcd.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /*
  2. Copyright 2015 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 registrytest
  14. import (
  15. "fmt"
  16. "testing"
  17. "k8s.io/kubernetes/pkg/api"
  18. "k8s.io/kubernetes/pkg/api/errors"
  19. "k8s.io/kubernetes/pkg/api/meta"
  20. "k8s.io/kubernetes/pkg/api/rest/resttest"
  21. "k8s.io/kubernetes/pkg/api/testapi"
  22. "k8s.io/kubernetes/pkg/fields"
  23. "k8s.io/kubernetes/pkg/labels"
  24. "k8s.io/kubernetes/pkg/registry/generic/registry"
  25. "k8s.io/kubernetes/pkg/runtime"
  26. etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
  27. "k8s.io/kubernetes/pkg/storage/etcd/etcdtest"
  28. etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
  29. "k8s.io/kubernetes/pkg/storage/storagebackend"
  30. storagetesting "k8s.io/kubernetes/pkg/storage/testing"
  31. )
  32. func NewEtcdStorage(t *testing.T, group string) (*storagebackend.Config, *etcdtesting.EtcdTestServer) {
  33. server := etcdtesting.NewUnsecuredEtcdTestClientServer(t)
  34. config := &storagebackend.Config{
  35. Type: "etcd2",
  36. Prefix: etcdtest.PathPrefix(),
  37. ServerList: server.Client.Endpoints(),
  38. DeserializationCacheSize: etcdtest.DeserializationCacheSize,
  39. Codec: testapi.Groups[group].StorageCodec(),
  40. }
  41. return config, server
  42. }
  43. type Tester struct {
  44. tester *resttest.Tester
  45. storage *registry.Store
  46. }
  47. type UpdateFunc func(runtime.Object) runtime.Object
  48. func New(t *testing.T, storage *registry.Store) *Tester {
  49. return &Tester{
  50. tester: resttest.New(t, storage),
  51. storage: storage,
  52. }
  53. }
  54. func (t *Tester) TestNamespace() string {
  55. return t.tester.TestNamespace()
  56. }
  57. func (t *Tester) ClusterScope() *Tester {
  58. t.tester = t.tester.ClusterScope()
  59. return t
  60. }
  61. func (t *Tester) Namer(namer func(int) string) *Tester {
  62. t.tester = t.tester.Namer(namer)
  63. return t
  64. }
  65. func (t *Tester) AllowCreateOnUpdate() *Tester {
  66. t.tester = t.tester.AllowCreateOnUpdate()
  67. return t
  68. }
  69. func (t *Tester) GeneratesName() *Tester {
  70. t.tester = t.tester.GeneratesName()
  71. return t
  72. }
  73. func (t *Tester) ReturnDeletedObject() *Tester {
  74. t.tester = t.tester.ReturnDeletedObject()
  75. return t
  76. }
  77. func (t *Tester) TestCreate(valid runtime.Object, invalid ...runtime.Object) {
  78. t.tester.TestCreate(
  79. valid,
  80. t.createObject,
  81. t.getObject,
  82. invalid...,
  83. )
  84. }
  85. func (t *Tester) TestUpdate(valid runtime.Object, validUpdateFunc UpdateFunc, invalidUpdateFunc ...UpdateFunc) {
  86. var invalidFuncs []resttest.UpdateFunc
  87. for _, f := range invalidUpdateFunc {
  88. invalidFuncs = append(invalidFuncs, resttest.UpdateFunc(f))
  89. }
  90. t.tester.TestUpdate(
  91. valid,
  92. t.createObject,
  93. t.getObject,
  94. resttest.UpdateFunc(validUpdateFunc),
  95. invalidFuncs...,
  96. )
  97. }
  98. func (t *Tester) TestDelete(valid runtime.Object) {
  99. t.tester.TestDelete(
  100. valid,
  101. t.createObject,
  102. t.getObject,
  103. errors.IsNotFound,
  104. )
  105. }
  106. func (t *Tester) TestDeleteGraceful(valid runtime.Object, expectedGrace int64) {
  107. t.tester.TestDeleteGraceful(
  108. valid,
  109. t.createObject,
  110. t.getObject,
  111. expectedGrace,
  112. )
  113. }
  114. func (t *Tester) TestGet(valid runtime.Object) {
  115. t.tester.TestGet(valid)
  116. }
  117. func (t *Tester) TestList(valid runtime.Object) {
  118. t.tester.TestList(
  119. valid,
  120. t.setObjectsForList,
  121. )
  122. }
  123. func (t *Tester) TestWatch(valid runtime.Object, labelsPass, labelsFail []labels.Set, fieldsPass, fieldsFail []fields.Set) {
  124. t.tester.TestWatch(
  125. valid,
  126. t.emitObject,
  127. labelsPass,
  128. labelsFail,
  129. fieldsPass,
  130. fieldsFail,
  131. // TODO: This should be filtered, the registry should not be aware of this level of detail
  132. []string{etcdstorage.EtcdCreate, etcdstorage.EtcdDelete},
  133. )
  134. }
  135. // =============================================================================
  136. // get codec based on runtime.Object
  137. func getCodec(obj runtime.Object) (runtime.Codec, error) {
  138. fqKinds, _, err := api.Scheme.ObjectKinds(obj)
  139. if err != nil {
  140. return nil, fmt.Errorf("unexpected encoding error: %v", err)
  141. }
  142. fqKind := fqKinds[0]
  143. // TODO: caesarxuchao: we should detect which group an object belongs to
  144. // by using the version returned by Schem.ObjectVersionAndKind() once we
  145. // split the schemes for internal objects.
  146. // TODO: caesarxuchao: we should add a map from kind to group in Scheme.
  147. var codec runtime.Codec
  148. if api.Scheme.Recognizes(testapi.Default.GroupVersion().WithKind(fqKind.Kind)) {
  149. codec = testapi.Default.Codec()
  150. } else if api.Scheme.Recognizes(testapi.Extensions.GroupVersion().WithKind(fqKind.Kind)) {
  151. codec = testapi.Extensions.Codec()
  152. } else {
  153. return nil, fmt.Errorf("unexpected kind: %v", fqKind)
  154. }
  155. return codec, nil
  156. }
  157. // Helper functions
  158. func (t *Tester) getObject(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
  159. accessor, err := meta.Accessor(obj)
  160. if err != nil {
  161. return nil, err
  162. }
  163. result, err := t.storage.Get(ctx, accessor.GetName())
  164. if err != nil {
  165. return nil, err
  166. }
  167. return result, nil
  168. }
  169. func (t *Tester) createObject(ctx api.Context, obj runtime.Object) error {
  170. accessor, err := meta.Accessor(obj)
  171. if err != nil {
  172. return err
  173. }
  174. key, err := t.storage.KeyFunc(ctx, accessor.GetName())
  175. if err != nil {
  176. return err
  177. }
  178. return t.storage.Storage.Create(ctx, key, obj, nil, 0)
  179. }
  180. func (t *Tester) setObjectsForList(objects []runtime.Object) []runtime.Object {
  181. key := t.storage.KeyRootFunc(t.tester.TestContext())
  182. if err := storagetesting.CreateObjList(key, t.storage.Storage, objects); err != nil {
  183. t.tester.Errorf("unexpected error: %v", err)
  184. return nil
  185. }
  186. return objects
  187. }
  188. func (t *Tester) emitObject(obj runtime.Object, action string) error {
  189. ctx := t.tester.TestContext()
  190. var err error
  191. switch action {
  192. case etcdstorage.EtcdCreate:
  193. err = t.createObject(ctx, obj)
  194. case etcdstorage.EtcdDelete:
  195. accessor, err := meta.Accessor(obj)
  196. if err != nil {
  197. return err
  198. }
  199. _, err = t.storage.Delete(ctx, accessor.GetName(), nil)
  200. default:
  201. err = fmt.Errorf("unexpected action: %v", action)
  202. }
  203. return err
  204. }