create.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. /*
  2. Copyright 2018 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 utils
  14. import (
  15. "bytes"
  16. "context"
  17. "encoding/json"
  18. "fmt"
  19. imageutils "k8s.io/kubernetes/test/utils/image"
  20. "github.com/pkg/errors"
  21. appsv1 "k8s.io/api/apps/v1"
  22. v1 "k8s.io/api/core/v1"
  23. rbacv1 "k8s.io/api/rbac/v1"
  24. storagev1 "k8s.io/api/storage/v1"
  25. storagev1beta1 "k8s.io/api/storage/v1beta1"
  26. apierrors "k8s.io/apimachinery/pkg/api/errors"
  27. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  28. "k8s.io/apimachinery/pkg/runtime"
  29. "k8s.io/apimachinery/pkg/runtime/schema"
  30. "k8s.io/client-go/kubernetes/scheme"
  31. "k8s.io/client-go/tools/cache"
  32. "k8s.io/kubernetes/test/e2e/framework"
  33. "k8s.io/kubernetes/test/e2e/framework/testfiles"
  34. )
  35. // LoadFromManifests loads .yaml or .json manifest files and returns
  36. // all items that it finds in them. It supports all items for which
  37. // there is a factory registered in factories and .yaml files with
  38. // multiple items separated by "---". Files are accessed via the
  39. // "testfiles" package, which means they can come from a file system
  40. // or be built into the binary.
  41. //
  42. // LoadFromManifests has some limitations:
  43. // - aliases are not supported (i.e. use serviceAccountName instead of the deprecated serviceAccount,
  44. // https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#podspec-v1-core)
  45. // and silently ignored
  46. // - the latest stable API version for each item is used, regardless of what
  47. // is specified in the manifest files
  48. func LoadFromManifests(files ...string) ([]interface{}, error) {
  49. var items []interface{}
  50. err := visitManifests(func(data []byte) error {
  51. // Ignore any additional fields for now, just determine what we have.
  52. var what What
  53. if err := runtime.DecodeInto(scheme.Codecs.UniversalDecoder(), data, &what); err != nil {
  54. return errors.Wrap(err, "decode TypeMeta")
  55. }
  56. factory := factories[what]
  57. if factory == nil {
  58. return errors.Errorf("item of type %+v not supported", what)
  59. }
  60. object := factory.New()
  61. if err := runtime.DecodeInto(scheme.Codecs.UniversalDecoder(), data, object); err != nil {
  62. return errors.Wrapf(err, "decode %+v", what)
  63. }
  64. items = append(items, object)
  65. return nil
  66. }, files...)
  67. return items, err
  68. }
  69. func visitManifests(cb func([]byte) error, files ...string) error {
  70. for _, fileName := range files {
  71. data, err := testfiles.Read(fileName)
  72. if err != nil {
  73. framework.Failf("reading manifest file: %v", err)
  74. }
  75. // Split at the "---" separator before working on
  76. // individual item. Only works for .yaml.
  77. //
  78. // We need to split ourselves because we need access
  79. // to each original chunk of data for
  80. // runtime.DecodeInto. kubectl has its own
  81. // infrastructure for this, but that is a lot of code
  82. // with many dependencies.
  83. items := bytes.Split(data, []byte("\n---"))
  84. for _, item := range items {
  85. if err := cb(item); err != nil {
  86. return errors.Wrap(err, fileName)
  87. }
  88. }
  89. }
  90. return nil
  91. }
  92. // PatchItems modifies the given items in place such that each test
  93. // gets its own instances, to avoid conflicts between different tests
  94. // and between tests and normal deployments.
  95. //
  96. // This is done by:
  97. // - creating namespaced items inside the test's namespace
  98. // - changing the name of non-namespaced items like ClusterRole
  99. //
  100. // PatchItems has some limitations:
  101. // - only some common items are supported, unknown ones trigger an error
  102. // - only the latest stable API version for each item is supported
  103. func PatchItems(f *framework.Framework, items ...interface{}) error {
  104. for _, item := range items {
  105. // Uncomment when debugging the loading and patching of items.
  106. // Logf("patching original content of %T:\n%s", item, PrettyPrint(item))
  107. if err := patchItemRecursively(f, item); err != nil {
  108. return err
  109. }
  110. }
  111. return nil
  112. }
  113. // CreateItems creates the items. Each of them must be an API object
  114. // of a type that is registered in Factory.
  115. //
  116. // It returns either a cleanup function or an error, but never both.
  117. //
  118. // Cleaning up after a test can be triggered in two ways:
  119. // - the test invokes the returned cleanup function,
  120. // usually in an AfterEach
  121. // - the test suite terminates, potentially after
  122. // skipping the test's AfterEach (https://github.com/onsi/ginkgo/issues/222)
  123. //
  124. // PatchItems has the some limitations as LoadFromManifests:
  125. // - only some common items are supported, unknown ones trigger an error
  126. // - only the latest stable API version for each item is supported
  127. func CreateItems(f *framework.Framework, items ...interface{}) (func(), error) {
  128. var destructors []func() error
  129. var cleanupHandle framework.CleanupActionHandle
  130. cleanup := func() {
  131. if cleanupHandle == nil {
  132. // Already done.
  133. return
  134. }
  135. framework.RemoveCleanupAction(cleanupHandle)
  136. // TODO (?): use same logic as framework.go for determining
  137. // whether we are expected to clean up? This would change the
  138. // meaning of the -delete-namespace and -delete-namespace-on-failure
  139. // command line flags, because they would also start to apply
  140. // to non-namespaced items.
  141. for _, destructor := range destructors {
  142. if err := destructor(); err != nil && !apierrors.IsNotFound(err) {
  143. framework.Logf("deleting failed: %s", err)
  144. }
  145. }
  146. }
  147. cleanupHandle = framework.AddCleanupAction(cleanup)
  148. var result error
  149. for _, item := range items {
  150. // Each factory knows which item(s) it supports, so try each one.
  151. done := false
  152. description := describeItem(item)
  153. // Uncomment this line to get a full dump of the entire item.
  154. // description = fmt.Sprintf("%s:\n%s", description, PrettyPrint(item))
  155. framework.Logf("creating %s", description)
  156. for _, factory := range factories {
  157. destructor, err := factory.Create(f, item)
  158. if destructor != nil {
  159. destructors = append(destructors, func() error {
  160. framework.Logf("deleting %s", description)
  161. return destructor()
  162. })
  163. }
  164. if err == nil {
  165. done = true
  166. break
  167. } else if errors.Cause(err) != errorItemNotSupported {
  168. result = err
  169. break
  170. }
  171. }
  172. if result == nil && !done {
  173. result = errors.Errorf("item of type %T not supported", item)
  174. break
  175. }
  176. }
  177. if result != nil {
  178. cleanup()
  179. return nil, result
  180. }
  181. return cleanup, nil
  182. }
  183. // CreateFromManifests is a combination of LoadFromManifests,
  184. // PatchItems, patching with an optional custom function,
  185. // and CreateItems.
  186. func CreateFromManifests(f *framework.Framework, patch func(item interface{}) error, files ...string) (func(), error) {
  187. items, err := LoadFromManifests(files...)
  188. if err != nil {
  189. return nil, errors.Wrap(err, "CreateFromManifests")
  190. }
  191. if err := PatchItems(f, items...); err != nil {
  192. return nil, err
  193. }
  194. if patch != nil {
  195. for _, item := range items {
  196. if err := patch(item); err != nil {
  197. return nil, err
  198. }
  199. }
  200. }
  201. return CreateItems(f, items...)
  202. }
  203. // What is a subset of metav1.TypeMeta which (in contrast to
  204. // metav1.TypeMeta itself) satisfies the runtime.Object interface.
  205. type What struct {
  206. Kind string `json:"kind"`
  207. }
  208. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new What.
  209. func (in *What) DeepCopy() *What {
  210. return &What{Kind: in.Kind}
  211. }
  212. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out.
  213. func (in *What) DeepCopyInto(out *What) {
  214. out.Kind = in.Kind
  215. }
  216. // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
  217. func (in *What) DeepCopyObject() runtime.Object {
  218. return &What{Kind: in.Kind}
  219. }
  220. // GetObjectKind returns the ObjectKind schema
  221. func (in *What) GetObjectKind() schema.ObjectKind {
  222. return nil
  223. }
  224. // ItemFactory provides support for creating one particular item.
  225. // The type gets exported because other packages might want to
  226. // extend the set of pre-defined factories.
  227. type ItemFactory interface {
  228. // New returns a new empty item.
  229. New() runtime.Object
  230. // Create is responsible for creating the item. It returns an
  231. // error or a cleanup function for the created item.
  232. // If the item is of an unsupported type, it must return
  233. // an error that has errorItemNotSupported as cause.
  234. Create(f *framework.Framework, item interface{}) (func() error, error)
  235. }
  236. // describeItem always returns a string that describes the item,
  237. // usually by calling out to cache.MetaNamespaceKeyFunc which
  238. // concatenates namespace (if set) and name. If that fails, the entire
  239. // item gets converted to a string.
  240. func describeItem(item interface{}) string {
  241. key, err := cache.MetaNamespaceKeyFunc(item)
  242. if err == nil && key != "" {
  243. return fmt.Sprintf("%T: %s", item, key)
  244. }
  245. return fmt.Sprintf("%T: %s", item, item)
  246. }
  247. // errorItemNotSupported is the error that Create methods
  248. // must return or wrap when they don't support the given item.
  249. var errorItemNotSupported = errors.New("not supported")
  250. var factories = map[What]ItemFactory{
  251. {"ClusterRole"}: &clusterRoleFactory{},
  252. {"ClusterRoleBinding"}: &clusterRoleBindingFactory{},
  253. {"CSIDriver"}: &csiDriverFactory{},
  254. {"DaemonSet"}: &daemonSetFactory{},
  255. {"Role"}: &roleFactory{},
  256. {"RoleBinding"}: &roleBindingFactory{},
  257. {"Secret"}: &secretFactory{},
  258. {"Service"}: &serviceFactory{},
  259. {"ServiceAccount"}: &serviceAccountFactory{},
  260. {"StatefulSet"}: &statefulSetFactory{},
  261. {"StorageClass"}: &storageClassFactory{},
  262. }
  263. // PatchName makes the name of some item unique by appending the
  264. // generated unique name.
  265. func PatchName(f *framework.Framework, item *string) {
  266. if *item != "" {
  267. *item = *item + "-" + f.UniqueName
  268. }
  269. }
  270. // PatchNamespace moves the item into the test's namespace. Not
  271. // all items can be namespaced. For those, the name also needs to be
  272. // patched.
  273. func PatchNamespace(f *framework.Framework, item *string) {
  274. if f.Namespace != nil {
  275. *item = f.Namespace.GetName()
  276. }
  277. }
  278. func patchItemRecursively(f *framework.Framework, item interface{}) error {
  279. switch item := item.(type) {
  280. case *rbacv1.Subject:
  281. PatchNamespace(f, &item.Namespace)
  282. case *rbacv1.RoleRef:
  283. // TODO: avoid hard-coding this special name. Perhaps add a Framework.PredefinedRoles
  284. // which contains all role names that are defined cluster-wide before the test starts?
  285. // All those names are excempt from renaming. That list could be populated by querying
  286. // and get extended by tests.
  287. if item.Name != "e2e-test-privileged-psp" {
  288. PatchName(f, &item.Name)
  289. }
  290. case *rbacv1.ClusterRole:
  291. PatchName(f, &item.Name)
  292. case *rbacv1.Role:
  293. PatchNamespace(f, &item.Namespace)
  294. // Roles are namespaced, but because for RoleRef above we don't
  295. // know whether the referenced role is a ClusterRole or Role
  296. // and therefore always renames, we have to do the same here.
  297. PatchName(f, &item.Name)
  298. case *storagev1.StorageClass:
  299. PatchName(f, &item.Name)
  300. case *storagev1beta1.CSIDriver:
  301. PatchName(f, &item.Name)
  302. case *v1.ServiceAccount:
  303. PatchNamespace(f, &item.ObjectMeta.Namespace)
  304. case *v1.Secret:
  305. PatchNamespace(f, &item.ObjectMeta.Namespace)
  306. case *rbacv1.ClusterRoleBinding:
  307. PatchName(f, &item.Name)
  308. for i := range item.Subjects {
  309. if err := patchItemRecursively(f, &item.Subjects[i]); err != nil {
  310. return errors.Wrapf(err, "%T", f)
  311. }
  312. }
  313. if err := patchItemRecursively(f, &item.RoleRef); err != nil {
  314. return errors.Wrapf(err, "%T", f)
  315. }
  316. case *rbacv1.RoleBinding:
  317. PatchNamespace(f, &item.Namespace)
  318. for i := range item.Subjects {
  319. if err := patchItemRecursively(f, &item.Subjects[i]); err != nil {
  320. return errors.Wrapf(err, "%T", f)
  321. }
  322. }
  323. if err := patchItemRecursively(f, &item.RoleRef); err != nil {
  324. return errors.Wrapf(err, "%T", f)
  325. }
  326. case *v1.Service:
  327. PatchNamespace(f, &item.ObjectMeta.Namespace)
  328. case *appsv1.StatefulSet:
  329. PatchNamespace(f, &item.ObjectMeta.Namespace)
  330. if err := patchContainerImages(item.Spec.Template.Spec.Containers); err != nil {
  331. return err
  332. }
  333. if err := patchContainerImages(item.Spec.Template.Spec.InitContainers); err != nil {
  334. return err
  335. }
  336. case *appsv1.DaemonSet:
  337. PatchNamespace(f, &item.ObjectMeta.Namespace)
  338. if err := patchContainerImages(item.Spec.Template.Spec.Containers); err != nil {
  339. return err
  340. }
  341. if err := patchContainerImages(item.Spec.Template.Spec.InitContainers); err != nil {
  342. return err
  343. }
  344. default:
  345. return errors.Errorf("missing support for patching item of type %T", item)
  346. }
  347. return nil
  348. }
  349. // The individual factories all follow the same template, but with
  350. // enough differences in types and functions that copy-and-paste
  351. // looked like the least dirty approach. Perhaps one day Go will have
  352. // generics.
  353. type serviceAccountFactory struct{}
  354. func (f *serviceAccountFactory) New() runtime.Object {
  355. return &v1.ServiceAccount{}
  356. }
  357. func (*serviceAccountFactory) Create(f *framework.Framework, i interface{}) (func() error, error) {
  358. item, ok := i.(*v1.ServiceAccount)
  359. if !ok {
  360. return nil, errorItemNotSupported
  361. }
  362. client := f.ClientSet.CoreV1().ServiceAccounts(f.Namespace.GetName())
  363. if _, err := client.Create(context.TODO(), item, metav1.CreateOptions{}); err != nil {
  364. return nil, errors.Wrap(err, "create ServiceAccount")
  365. }
  366. return func() error {
  367. return client.Delete(context.TODO(), item.GetName(), &metav1.DeleteOptions{})
  368. }, nil
  369. }
  370. type clusterRoleFactory struct{}
  371. func (f *clusterRoleFactory) New() runtime.Object {
  372. return &rbacv1.ClusterRole{}
  373. }
  374. func (*clusterRoleFactory) Create(f *framework.Framework, i interface{}) (func() error, error) {
  375. item, ok := i.(*rbacv1.ClusterRole)
  376. if !ok {
  377. return nil, errorItemNotSupported
  378. }
  379. framework.Logf("Define cluster role %v", item.GetName())
  380. client := f.ClientSet.RbacV1().ClusterRoles()
  381. if _, err := client.Create(context.TODO(), item, metav1.CreateOptions{}); err != nil {
  382. return nil, errors.Wrap(err, "create ClusterRole")
  383. }
  384. return func() error {
  385. return client.Delete(context.TODO(), item.GetName(), &metav1.DeleteOptions{})
  386. }, nil
  387. }
  388. type clusterRoleBindingFactory struct{}
  389. func (f *clusterRoleBindingFactory) New() runtime.Object {
  390. return &rbacv1.ClusterRoleBinding{}
  391. }
  392. func (*clusterRoleBindingFactory) Create(f *framework.Framework, i interface{}) (func() error, error) {
  393. item, ok := i.(*rbacv1.ClusterRoleBinding)
  394. if !ok {
  395. return nil, errorItemNotSupported
  396. }
  397. client := f.ClientSet.RbacV1().ClusterRoleBindings()
  398. if _, err := client.Create(context.TODO(), item, metav1.CreateOptions{}); err != nil {
  399. return nil, errors.Wrap(err, "create ClusterRoleBinding")
  400. }
  401. return func() error {
  402. return client.Delete(context.TODO(), item.GetName(), &metav1.DeleteOptions{})
  403. }, nil
  404. }
  405. type roleFactory struct{}
  406. func (f *roleFactory) New() runtime.Object {
  407. return &rbacv1.Role{}
  408. }
  409. func (*roleFactory) Create(f *framework.Framework, i interface{}) (func() error, error) {
  410. item, ok := i.(*rbacv1.Role)
  411. if !ok {
  412. return nil, errorItemNotSupported
  413. }
  414. client := f.ClientSet.RbacV1().Roles(f.Namespace.GetName())
  415. if _, err := client.Create(context.TODO(), item, metav1.CreateOptions{}); err != nil {
  416. return nil, errors.Wrap(err, "create Role")
  417. }
  418. return func() error {
  419. return client.Delete(context.TODO(), item.GetName(), &metav1.DeleteOptions{})
  420. }, nil
  421. }
  422. type roleBindingFactory struct{}
  423. func (f *roleBindingFactory) New() runtime.Object {
  424. return &rbacv1.RoleBinding{}
  425. }
  426. func (*roleBindingFactory) Create(f *framework.Framework, i interface{}) (func() error, error) {
  427. item, ok := i.(*rbacv1.RoleBinding)
  428. if !ok {
  429. return nil, errorItemNotSupported
  430. }
  431. client := f.ClientSet.RbacV1().RoleBindings(f.Namespace.GetName())
  432. if _, err := client.Create(context.TODO(), item, metav1.CreateOptions{}); err != nil {
  433. return nil, errors.Wrap(err, "create RoleBinding")
  434. }
  435. return func() error {
  436. return client.Delete(context.TODO(), item.GetName(), &metav1.DeleteOptions{})
  437. }, nil
  438. }
  439. type serviceFactory struct{}
  440. func (f *serviceFactory) New() runtime.Object {
  441. return &v1.Service{}
  442. }
  443. func (*serviceFactory) Create(f *framework.Framework, i interface{}) (func() error, error) {
  444. item, ok := i.(*v1.Service)
  445. if !ok {
  446. return nil, errorItemNotSupported
  447. }
  448. client := f.ClientSet.CoreV1().Services(f.Namespace.GetName())
  449. if _, err := client.Create(context.TODO(), item, metav1.CreateOptions{}); err != nil {
  450. return nil, errors.Wrap(err, "create Service")
  451. }
  452. return func() error {
  453. return client.Delete(context.TODO(), item.GetName(), &metav1.DeleteOptions{})
  454. }, nil
  455. }
  456. type statefulSetFactory struct{}
  457. func (f *statefulSetFactory) New() runtime.Object {
  458. return &appsv1.StatefulSet{}
  459. }
  460. func (*statefulSetFactory) Create(f *framework.Framework, i interface{}) (func() error, error) {
  461. item, ok := i.(*appsv1.StatefulSet)
  462. if !ok {
  463. return nil, errorItemNotSupported
  464. }
  465. client := f.ClientSet.AppsV1().StatefulSets(f.Namespace.GetName())
  466. if _, err := client.Create(context.TODO(), item, metav1.CreateOptions{}); err != nil {
  467. return nil, errors.Wrap(err, "create StatefulSet")
  468. }
  469. return func() error {
  470. return client.Delete(context.TODO(), item.GetName(), &metav1.DeleteOptions{})
  471. }, nil
  472. }
  473. type daemonSetFactory struct{}
  474. func (f *daemonSetFactory) New() runtime.Object {
  475. return &appsv1.DaemonSet{}
  476. }
  477. func (*daemonSetFactory) Create(f *framework.Framework, i interface{}) (func() error, error) {
  478. item, ok := i.(*appsv1.DaemonSet)
  479. if !ok {
  480. return nil, errorItemNotSupported
  481. }
  482. client := f.ClientSet.AppsV1().DaemonSets(f.Namespace.GetName())
  483. if _, err := client.Create(context.TODO(), item, metav1.CreateOptions{}); err != nil {
  484. return nil, errors.Wrap(err, "create DaemonSet")
  485. }
  486. return func() error {
  487. return client.Delete(context.TODO(), item.GetName(), &metav1.DeleteOptions{})
  488. }, nil
  489. }
  490. type storageClassFactory struct{}
  491. func (f *storageClassFactory) New() runtime.Object {
  492. return &storagev1.StorageClass{}
  493. }
  494. func (*storageClassFactory) Create(f *framework.Framework, i interface{}) (func() error, error) {
  495. item, ok := i.(*storagev1.StorageClass)
  496. if !ok {
  497. return nil, errorItemNotSupported
  498. }
  499. client := f.ClientSet.StorageV1().StorageClasses()
  500. if _, err := client.Create(context.TODO(), item, metav1.CreateOptions{}); err != nil {
  501. return nil, errors.Wrap(err, "create StorageClass")
  502. }
  503. return func() error {
  504. return client.Delete(context.TODO(), item.GetName(), &metav1.DeleteOptions{})
  505. }, nil
  506. }
  507. type csiDriverFactory struct{}
  508. func (f *csiDriverFactory) New() runtime.Object {
  509. return &storagev1beta1.CSIDriver{}
  510. }
  511. func (*csiDriverFactory) Create(f *framework.Framework, i interface{}) (func() error, error) {
  512. item, ok := i.(*storagev1beta1.CSIDriver)
  513. if !ok {
  514. return nil, errorItemNotSupported
  515. }
  516. client := f.ClientSet.StorageV1beta1().CSIDrivers()
  517. if _, err := client.Create(context.TODO(), item, metav1.CreateOptions{}); err != nil {
  518. return nil, errors.Wrap(err, "create CSIDriver")
  519. }
  520. return func() error {
  521. return client.Delete(context.TODO(), item.GetName(), &metav1.DeleteOptions{})
  522. }, nil
  523. }
  524. type secretFactory struct{}
  525. func (f *secretFactory) New() runtime.Object {
  526. return &v1.Secret{}
  527. }
  528. func (*secretFactory) Create(f *framework.Framework, i interface{}) (func() error, error) {
  529. item, ok := i.(*v1.Secret)
  530. if !ok {
  531. return nil, errorItemNotSupported
  532. }
  533. client := f.ClientSet.CoreV1().Secrets(f.Namespace.GetName())
  534. if _, err := client.Create(context.TODO(), item, metav1.CreateOptions{}); err != nil {
  535. return nil, errors.Wrap(err, "create Secret")
  536. }
  537. return func() error {
  538. return client.Delete(context.TODO(), item.GetName(), &metav1.DeleteOptions{})
  539. }, nil
  540. }
  541. // PrettyPrint returns a human-readable representation of an item.
  542. func PrettyPrint(item interface{}) string {
  543. data, err := json.MarshalIndent(item, "", " ")
  544. if err == nil {
  545. return string(data)
  546. }
  547. return fmt.Sprintf("%+v", item)
  548. }
  549. // patchContainerImages replaces the specified Container Registry with a custom
  550. // one provided via the KUBE_TEST_REPO_LIST env variable
  551. func patchContainerImages(containers []v1.Container) error {
  552. var err error
  553. for _, c := range containers {
  554. c.Image, err = imageutils.ReplaceRegistryInImageURL(c.Image)
  555. if err != nil {
  556. return err
  557. }
  558. }
  559. return nil
  560. }