k8s.io/kubernetes@v1.29.3/test/cmd/crd.sh (about)

     1  #!/usr/bin/env bash
     2  
     3  # Copyright 2018 The Kubernetes Authors.
     4  #
     5  # Licensed under the Apache License, Version 2.0 (the "License");
     6  # you may not use this file except in compliance with the License.
     7  # You may obtain a copy of the License at
     8  #
     9  #     http://www.apache.org/licenses/LICENSE-2.0
    10  #
    11  # Unless required by applicable law or agreed to in writing, software
    12  # distributed under the License is distributed on an "AS IS" BASIS,
    13  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  # See the License for the specific language governing permissions and
    15  # limitations under the License.
    16  
    17  set -o errexit
    18  set -o nounset
    19  set -o pipefail
    20  
    21  run_crd_tests() {
    22    set -o nounset
    23    set -o errexit
    24  
    25    create_and_use_new_namespace
    26    kube::log::status "Testing kubectl crd"
    27    kubectl "${kube_flags_with_token[@]:?}" create -f - << __EOF__
    28  {
    29    "kind": "CustomResourceDefinition",
    30    "apiVersion": "apiextensions.k8s.io/v1",
    31    "metadata": {
    32      "name": "foos.company.com"
    33    },
    34    "spec": {
    35      "group": "company.com",
    36      "scope": "Namespaced",
    37      "names": {
    38        "plural": "foos",
    39        "kind": "Foo"
    40      },
    41      "versions": [
    42        {
    43          "name": "v1",
    44          "served": true,
    45          "storage": true,
    46          "schema": {
    47            "openAPIV3Schema": {
    48              "type": "object",
    49              "properties": {
    50                "metadata": {"type": "object"},
    51                "nestedField": {
    52                  "type": "object",
    53                  "properties": {
    54                    "someSubfield": {"type": "string"},
    55                    "otherSubfield": {"type": "string"},
    56                    "newSubfield": {"type": "string"}
    57                  }
    58                },
    59                "otherField": {"type": "string"},
    60                "someField": {"type": "string"},
    61                "newField": {"type": "string"},
    62                "patched": {"type": "string"}
    63              }
    64            }
    65          }
    66        }
    67      ]
    68    }
    69  }
    70  __EOF__
    71  
    72    # Post-Condition: assertion object exist
    73    kube::test::get_object_assert customresourcedefinitions "{{range.items}}{{if eq ${id_field:?} \"foos.company.com\"}}{{$id_field}}:{{end}}{{end}}" 'foos.company.com:'
    74  
    75    kubectl "${kube_flags_with_token[@]}" create -f - << __EOF__
    76  {
    77    "kind": "CustomResourceDefinition",
    78    "apiVersion": "apiextensions.k8s.io/v1",
    79    "metadata": {
    80      "name": "bars.company.com"
    81    },
    82    "spec": {
    83      "group": "company.com",
    84      "scope": "Namespaced",
    85      "names": {
    86        "plural": "bars",
    87        "kind": "Bar"
    88      },
    89      "versions": [
    90        {
    91          "name": "v1",
    92          "served": true,
    93          "storage": true,
    94          "schema": {
    95            "openAPIV3Schema": {
    96              "x-kubernetes-preserve-unknown-fields": true,
    97              "type": "object"
    98            }
    99          }
   100        }
   101      ]
   102    }
   103  }
   104  __EOF__
   105  
   106    # Post-Condition: assertion object exist
   107    kube::test::get_object_assert customresourcedefinitions "{{range.items}}{{if eq $id_field \"foos.company.com\" \"bars.company.com\"}}{{$id_field}}:{{end}}{{end}}" 'bars.company.com:foos.company.com:'
   108  
   109    # This test ensures that the name printer is able to output a resource
   110    # in the proper "kind.group/resource_name" format, and that the
   111    # resource builder is able to resolve a GVK when a kind.group pair is given.
   112    kubectl "${kube_flags_with_token[@]}" create -f - << __EOF__
   113  {
   114    "kind": "CustomResourceDefinition",
   115    "apiVersion": "apiextensions.k8s.io/v1",
   116    "metadata": {
   117      "name": "resources.mygroup.example.com"
   118    },
   119    "spec": {
   120      "group": "mygroup.example.com",
   121      "scope": "Namespaced",
   122      "names": {
   123        "plural": "resources",
   124        "singular": "resource",
   125        "kind": "Kind",
   126        "listKind": "KindList"
   127      },
   128      "versions": [
   129        {
   130          "name": "v1alpha1",
   131          "served": true,
   132          "storage": true,
   133          "schema": {
   134            "openAPIV3Schema": {
   135              "x-kubernetes-preserve-unknown-fields": true,
   136              "type": "object"
   137            }
   138          }
   139        }
   140      ]
   141    }
   142  }
   143  __EOF__
   144  
   145    # Post-Condition: assertion crd with non-matching kind and resource exists
   146    kube::test::get_object_assert customresourcedefinitions "{{range.items}}{{if eq $id_field \"foos.company.com\" \"bars.company.com\" \"resources.mygroup.example.com\"}}{{$id_field}}:{{end}}{{end}}" 'bars.company.com:foos.company.com:resources.mygroup.example.com:'
   147  
   148    # This test ensures that we can create complex validation without client-side validation complaining
   149    kubectl "${kube_flags_with_token[@]}" create -f - << __EOF__
   150  {
   151    "kind": "CustomResourceDefinition",
   152    "apiVersion": "apiextensions.k8s.io/v1",
   153    "metadata": {
   154      "name": "validfoos.company.com"
   155    },
   156    "spec": {
   157      "group": "company.com",
   158      "scope": "Namespaced",
   159      "names": {
   160        "plural": "validfoos",
   161        "kind": "ValidFoo"
   162      },
   163      "versions": [
   164        {
   165          "name": "v1",
   166          "served": true,
   167          "storage": true,
   168          "schema": {
   169            "openAPIV3Schema": {
   170              "type": "object",
   171              "properties": {
   172                "spec": {
   173                  "type": "array",
   174                  "items": {
   175                    "type": "number"
   176                  }
   177                }
   178              }
   179            }
   180          }
   181        }
   182      ]
   183    }
   184  }
   185  __EOF__
   186  
   187    # Post-Condition: assertion crd with non-matching kind and resource exists
   188    kube::test::get_object_assert customresourcedefinitions "{{range.items}}{{if eq $id_field \"foos.company.com\" \"bars.company.com\" \"resources.mygroup.example.com\" \"validfoos.company.com\"}}{{$id_field}}:{{end}}{{end}}" 'bars.company.com:foos.company.com:resources.mygroup.example.com:validfoos.company.com:'
   189  
   190    run_non_native_resource_tests
   191  
   192    # teardown
   193    kubectl delete customresourcedefinitions/foos.company.com "${kube_flags_with_token[@]}"
   194    kubectl delete customresourcedefinitions/bars.company.com "${kube_flags_with_token[@]}"
   195    kubectl delete customresourcedefinitions/resources.mygroup.example.com "${kube_flags_with_token[@]}"
   196    kubectl delete customresourcedefinitions/validfoos.company.com "${kube_flags_with_token[@]}"
   197  
   198    set +o nounset
   199    set +o errexit
   200  }
   201  
   202  kube::util::non_native_resources() {
   203    local times
   204    local wait
   205    local failed
   206    times=30
   207    wait=10
   208    for _ in $(seq 1 $times); do
   209      failed=""
   210      kubectl "${kube_flags[@]:?}" get --raw '/apis/company.com/v1' || failed=true
   211      kubectl "${kube_flags[@]}" get --raw '/apis/company.com/v1/foos' || failed=true
   212      kubectl "${kube_flags[@]}" get --raw '/apis/company.com/v1/bars' || failed=true
   213  
   214      if [ -z "${failed}" ]; then
   215        return 0
   216      fi
   217      sleep ${wait}
   218    done
   219  
   220    kube::log::error "Timed out waiting for non-native-resources; tried ${times} waiting ${wait}s between each"
   221    return 1
   222  }
   223  
   224  run_non_native_resource_tests() {
   225    set -o nounset
   226    set -o errexit
   227  
   228    create_and_use_new_namespace
   229    kube::log::status "Testing kubectl non-native resources"
   230    kube::util::non_native_resources
   231  
   232    # Test that we can list this new CustomResource (foos)
   233    kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" ''
   234  
   235    # Test that we can list this new CustomResource (bars)
   236    kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" ''
   237  
   238    # Test that we can list this new CustomResource (resources)
   239    kube::test::get_object_assert resources "{{range.items}}{{$id_field}}:{{end}}" ''
   240  
   241    # Test that we can create a new resource of type Kind
   242    kubectl "${kube_flags[@]}" create -f hack/testdata/CRD/resource.yaml "${kube_flags[@]}"
   243  
   244    # Test that -o name returns kind.group/resourcename
   245    output_message=$(kubectl "${kube_flags[@]}" get resource/myobj -o name)
   246    kube::test::if_has_string "${output_message}" 'kind.mygroup.example.com/myobj'
   247  
   248    output_message=$(kubectl "${kube_flags[@]}" get resources/myobj -o name)
   249    kube::test::if_has_string "${output_message}" 'kind.mygroup.example.com/myobj'
   250  
   251    output_message=$(kubectl "${kube_flags[@]}" get kind.mygroup.example.com/myobj -o name)
   252    kube::test::if_has_string "${output_message}" 'kind.mygroup.example.com/myobj'
   253  
   254    # Delete the resource with cascading strategy background.
   255    kubectl "${kube_flags[@]}" delete resources myobj --cascade=background
   256  
   257    # Make sure it's gone
   258    kube::test::wait_object_assert resources "{{range.items}}{{$id_field}}:{{end}}" ''
   259  
   260    # Test that we can create a new resource of type Foo
   261    kubectl "${kube_flags[@]}" create -f hack/testdata/CRD/foo.yaml "${kube_flags[@]}"
   262    kubectl "${kube_flags[@]}" create -f hack/testdata/CRD/foo-2.yaml "${kube_flags[@]}"
   263  
   264    # Test that we can list this new custom resource
   265    kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" 'test:'
   266  
   267    # Test alternate forms
   268    kube::test::get_object_assert foo                 "{{range.items}}{{$id_field}}:{{end}}" 'test:'
   269    kube::test::get_object_assert foos.company.com    "{{range.items}}{{$id_field}}:{{end}}" 'test:'
   270    kube::test::get_object_assert foos.v1.company.com "{{range.items}}{{$id_field}}:{{end}}" 'test:'
   271  
   272    # Test all printers, with lists and individual items
   273    kube::log::status "Testing CustomResource printing"
   274    kubectl "${kube_flags[@]}" get foos
   275    kubectl "${kube_flags[@]}" get foos/test
   276    kubectl "${kube_flags[@]}" get foos      -o name
   277    kubectl "${kube_flags[@]}" get foos/test -o name
   278    kubectl "${kube_flags[@]}" get foos      -o wide
   279    kubectl "${kube_flags[@]}" get foos/test -o wide
   280    kubectl "${kube_flags[@]}" get foos      -o json
   281    kubectl "${kube_flags[@]}" get foos/test -o json
   282    kubectl "${kube_flags[@]}" get foos      -o yaml
   283    kubectl "${kube_flags[@]}" get foos/test -o yaml
   284    kubectl "${kube_flags[@]}" get foos      -o "jsonpath={.items[*].someField}" --allow-missing-template-keys=false
   285    kubectl "${kube_flags[@]}" get foos/test -o "jsonpath={.someField}"          --allow-missing-template-keys=false
   286    kubectl "${kube_flags[@]}" get foos      -o "go-template={{range .items}}{{.someField}}{{end}}" --allow-missing-template-keys=false
   287    kubectl "${kube_flags[@]}" get foos/test -o "go-template={{.someField}}"                        --allow-missing-template-keys=false
   288    output_message=$(kubectl "${kube_flags[@]}" get foos/test -o name)
   289    kube::test::if_has_string "${output_message}" 'foo.company.com/test'
   290  
   291    # Test patching
   292    kube::log::status "Testing CustomResource patching"
   293    kubectl "${kube_flags[@]}" patch foos/test -p '{"patched":"value1"}' --type=merge
   294    kube::test::get_object_assert foos/test "{{.patched}}" 'value1'
   295    kubectl "${kube_flags[@]}" patch foos/test -p '{"patched":"value2"}' --type=merge --record
   296    kube::test::get_object_assert foos/test "{{.patched}}" 'value2'
   297    kubectl "${kube_flags[@]}" patch foos/test -p '{"patched":null}' --type=merge --record
   298    kube::test::get_object_assert foos/test "{{.patched}}" '<no value>'
   299    # Get local version
   300    CRD_RESOURCE_FILE="${KUBE_TEMP}/crd-foos-test.json"
   301    kubectl "${kube_flags[@]}" get foos/test -o json > "${CRD_RESOURCE_FILE}"
   302    # cannot apply strategic patch locally
   303    CRD_PATCH_ERROR_FILE="${KUBE_TEMP}/crd-foos-test-error"
   304    ! kubectl "${kube_flags[@]}" patch --local -f "${CRD_RESOURCE_FILE}" -p '{"patched":"value3"}' 2> "${CRD_PATCH_ERROR_FILE}" || exit 1
   305    if grep -q "try --type merge" "${CRD_PATCH_ERROR_FILE}"; then
   306      kube::log::status "\"kubectl patch --local\" returns error as expected for CustomResource: $(cat "${CRD_PATCH_ERROR_FILE}")"
   307    else
   308      kube::log::status "\"kubectl patch --local\" returns unexpected error or non-error: $(cat "${CRD_PATCH_ERROR_FILE}")"
   309      exit 1
   310    fi
   311    # can apply merge patch locally
   312    kubectl "${kube_flags[@]}" patch --local -f "${CRD_RESOURCE_FILE}" -p '{"patched":"value3"}' --type=merge -o json
   313    # can apply merge patch remotely
   314    kubectl "${kube_flags[@]}" patch --record -f "${CRD_RESOURCE_FILE}" -p '{"patched":"value3"}' --type=merge -o json
   315    kube::test::get_object_assert foos/test "{{.patched}}" 'value3'
   316    rm "${CRD_RESOURCE_FILE}"
   317    rm "${CRD_PATCH_ERROR_FILE}"
   318  
   319    # Test labeling
   320    kube::log::status "Testing CustomResource labeling"
   321    kubectl "${kube_flags[@]}" label foos --all listlabel=true
   322    kubectl "${kube_flags[@]}" label foo/test itemlabel=true
   323    kubectl "${kube_flags[@]}" label --all --all-namespaces foo allnsLabel=true
   324    # make sure all instances in different namespaces got the annotation
   325    kubectl "${kube_flags[@]}" get foo/test -oyaml | grep allnsLabel
   326    kubectl "${kube_flags[@]}" get -n default foo -oyaml | grep allnsLabel
   327  
   328    # Test annotating
   329    kube::log::status "Testing CustomResource annotating"
   330    kubectl "${kube_flags[@]}" annotate foos --all listannotation=true
   331    kubectl "${kube_flags[@]}" annotate foo/test itemannotation=true
   332    kubectl "${kube_flags[@]}" annotate --all --all-namespaces foo allnsannotation=true
   333    # make sure all instances in different namespaces got the annotation
   334    kubectl "${kube_flags[@]}" get foo/test -oyaml | grep allnsannotation
   335    kubectl "${kube_flags[@]}" get -n default foo -oyaml | grep allnsannotation
   336  
   337    # Test describing
   338    kube::log::status "Testing CustomResource describing"
   339    kubectl "${kube_flags[@]}" describe foos
   340    kubectl "${kube_flags[@]}" describe foos/test
   341    kubectl "${kube_flags[@]}" describe foos | grep listlabel=true
   342    kubectl "${kube_flags[@]}" describe foos | grep itemlabel=true
   343    # Describe command should respect the chunk size parameter
   344    kube::test::describe_resource_chunk_size_assert customresourcedefinitions events
   345    kube::test::describe_resource_chunk_size_assert foos events
   346  
   347    # Delete the resource with cascading strategy background.
   348    kubectl "${kube_flags[@]}" delete foos test --cascade=background
   349  
   350    # Make sure it's gone
   351    kube::test::wait_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" ''
   352  
   353    # Test that we can create a new resource of type Bar
   354    kubectl "${kube_flags[@]}" create -f hack/testdata/CRD/bar.yaml "${kube_flags[@]}"
   355  
   356    # Test that we can list this new custom resource
   357    kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" 'test:'
   358  
   359    # Test that we can watch the resource.
   360    # Start watcher in background with process substitution,
   361    # so we can read from stdout asynchronously.
   362    kube::log::status "Testing CustomResource watching"
   363    exec 3< <(kubectl "${kube_flags[@]}" get bars --request-timeout=1m --watch-only -o name & echo $! ; wait)
   364    local watch_pid
   365    read -r <&3 watch_pid
   366  
   367    # We can't be sure when the watch gets established,
   368    # so keep triggering events (in the background) until something comes through.
   369    local tries=0
   370    while [ ${tries} -lt 10 ]; do
   371      tries=$((tries+1))
   372      kubectl "${kube_flags[@]}" patch bars/test -p "{\"patched\":\"${tries}\"}" --type=merge
   373      sleep 1
   374    done &
   375    local patch_pid=$!
   376  
   377    # Wait up to 30s for a complete line of output.
   378    local watch_output
   379    read -r <&3 -t 30 watch_output
   380    # Stop the watcher and the patch loop.
   381    kill -9 "${watch_pid}"
   382    kill -9 "${patch_pid}"
   383    kube::test::if_has_string "${watch_output}" 'bar.company.com/test'
   384  
   385    # Delete the resource with cascading strategy orphan.
   386    kubectl "${kube_flags[@]}" delete bars test --cascade=orphan
   387  
   388    # Make sure it's gone
   389    kube::test::wait_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" ''
   390  
   391    # Test that we can create single item via apply
   392    kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/foo.yaml
   393  
   394    # Test that we have create a foo named test
   395    kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" 'test:'
   396  
   397    # Test that the field has the expected value
   398    kube::test::get_object_assert foos/test '{{.someField}}' 'field1'
   399  
   400    # Test that apply an empty patch doesn't change fields
   401    kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/foo.yaml
   402  
   403    # Test that the field has the same value after re-apply
   404    kube::test::get_object_assert foos/test '{{.someField}}' 'field1'
   405  
   406    # Test that apply has updated the subfield
   407    kube::test::get_object_assert foos/test '{{.nestedField.someSubfield}}' 'subfield1'
   408  
   409    # Update a subfield and then apply the change
   410    kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/foo-updated-subfield.yaml
   411  
   412    # Test that apply has updated the subfield
   413    kube::test::get_object_assert foos/test '{{.nestedField.someSubfield}}' 'modifiedSubfield'
   414  
   415    # Test that the field has the expected value
   416    kube::test::get_object_assert foos/test '{{.nestedField.otherSubfield}}' 'subfield2'
   417  
   418    # Delete a subfield and then apply the change
   419    kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/foo-deleted-subfield.yaml
   420  
   421    # Test that apply has deleted the field
   422    kube::test::get_object_assert foos/test '{{.nestedField.otherSubfield}}' '<no value>'
   423  
   424    # Test that the field does not exist
   425    kube::test::get_object_assert foos/test '{{.nestedField.newSubfield}}' '<no value>'
   426  
   427    # Add a field and then apply the change
   428    kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/foo-added-subfield.yaml
   429  
   430    # Test that apply has added the field
   431    kube::test::get_object_assert foos/test '{{.nestedField.newSubfield}}' 'subfield3'
   432  
   433    # Delete the resource
   434    kubectl "${kube_flags[@]}" delete -f hack/testdata/CRD/foo.yaml
   435  
   436    # Make sure it's gone
   437    kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" ''
   438  
   439    # Test that we can create list via apply
   440    kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/multi-crd-list.yaml
   441  
   442    # Test that we have create a foo and a bar from a list
   443    kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" 'test-list:'
   444    kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" 'test-list:'
   445  
   446    # Test that the field has the expected value
   447    kube::test::get_object_assert foos/test-list '{{.someField}}' 'field1'
   448    kube::test::get_object_assert bars/test-list '{{.someField}}' 'field1'
   449  
   450    # Test that re-apply an list doesn't change anything
   451    kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/multi-crd-list.yaml
   452  
   453    # Test that the field has the same value after re-apply
   454    kube::test::get_object_assert foos/test-list '{{.someField}}' 'field1'
   455    kube::test::get_object_assert bars/test-list '{{.someField}}' 'field1'
   456  
   457    # Test that the fields have the expected value
   458    kube::test::get_object_assert foos/test-list '{{.someField}}' 'field1'
   459    kube::test::get_object_assert bars/test-list '{{.someField}}' 'field1'
   460  
   461    # Update fields and then apply the change
   462    kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/multi-crd-list-updated-field.yaml
   463  
   464    # Test that apply has updated the fields
   465    kube::test::get_object_assert foos/test-list '{{.someField}}' 'modifiedField'
   466    kube::test::get_object_assert bars/test-list '{{.someField}}' 'modifiedField'
   467  
   468    # Test that the field has the expected value
   469    kube::test::get_object_assert foos/test-list '{{.otherField}}' 'field2'
   470    kube::test::get_object_assert bars/test-list '{{.otherField}}' 'field2'
   471  
   472    # Delete fields and then apply the change
   473    kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/multi-crd-list-deleted-field.yaml
   474  
   475    # Test that apply has deleted the fields
   476    kube::test::get_object_assert foos/test-list '{{.otherField}}' '<no value>'
   477    kube::test::get_object_assert bars/test-list '{{.otherField}}' '<no value>'
   478  
   479    # Test that the fields does not exist
   480    kube::test::get_object_assert foos/test-list '{{.newField}}' '<no value>'
   481    kube::test::get_object_assert bars/test-list '{{.newField}}' '<no value>'
   482  
   483    # Add a field and then apply the change
   484    kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/multi-crd-list-added-field.yaml
   485  
   486    # Test that apply has added the field
   487    kube::test::get_object_assert foos/test-list '{{.newField}}' 'field3'
   488    kube::test::get_object_assert bars/test-list '{{.newField}}' 'field3'
   489  
   490    # Delete the resource
   491    kubectl "${kube_flags[@]}" delete -f hack/testdata/CRD/multi-crd-list.yaml
   492  
   493    # Make sure it's gone
   494    kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" ''
   495    kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" ''
   496  
   497    ## kubectl apply --prune
   498    # Test that no foo or bar exist
   499    kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" ''
   500    kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" ''
   501  
   502    # apply --prune on foo.yaml that has foo/test
   503    kubectl apply --prune -l pruneGroup=true -f hack/testdata/CRD/foo.yaml "${kube_flags[@]}" --prune-whitelist=company.com/v1/Foo --prune-whitelist=company.com/v1/Bar
   504    # check right crds exist
   505    kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" 'test:'
   506    kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" ''
   507  
   508    # apply --prune on bar.yaml that has bar/test
   509    kubectl apply --prune -l pruneGroup=true -f hack/testdata/CRD/bar.yaml "${kube_flags[@]}" --prune-whitelist=company.com/v1/Foo --prune-whitelist=company.com/v1/Bar
   510    # check right crds exist
   511    kube::test::wait_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" ''
   512    kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" 'test:'
   513  
   514    # Delete the resource
   515    kubectl "${kube_flags[@]}" delete -f hack/testdata/CRD/bar.yaml
   516  
   517    # Make sure it's gone
   518    kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" ''
   519    kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" ''
   520  
   521    # Test 'kubectl create' with namespace, and namespace cleanup.
   522    kubectl "${kube_flags[@]}" create namespace non-native-resources
   523    kubectl "${kube_flags[@]}" create -f hack/testdata/CRD/bar.yaml --namespace=non-native-resources
   524    kube::test::get_object_assert bars '{{len .items}}' '1' --namespace=non-native-resources
   525    kubectl "${kube_flags[@]}" delete namespace non-native-resources
   526    # Make sure objects go away.
   527    kube::test::wait_object_assert bars '{{len .items}}' '0' --namespace=non-native-resources
   528    # Make sure namespace goes away.
   529    local tries=0
   530    while kubectl "${kube_flags[@]}" get namespace non-native-resources && [ ${tries} -lt 10 ]; do
   531      tries=$((tries+1))
   532      sleep ${tries}
   533    done
   534  
   535    set +o nounset
   536    set +o errexit
   537  }