123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- #!/usr/bin/env bash
- # Copyright 2018 The Kubernetes Authors.
- #
- # 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.
- set -o errexit
- set -o nounset
- set -o pipefail
- run_crd_tests() {
- set -o nounset
- set -o errexit
- create_and_use_new_namespace
- kube::log::status "Testing kubectl crd"
- kubectl "${kube_flags_with_token[@]:?}" create -f - << __EOF__
- {
- "kind": "CustomResourceDefinition",
- "apiVersion": "apiextensions.k8s.io/v1beta1",
- "metadata": {
- "name": "foos.company.com"
- },
- "spec": {
- "group": "company.com",
- "version": "v1",
- "scope": "Namespaced",
- "names": {
- "plural": "foos",
- "kind": "Foo"
- }
- }
- }
- __EOF__
- # Post-Condition: assertion object exist
- kube::test::get_object_assert customresourcedefinitions "{{range.items}}{{if eq ${id_field:?} \\\"foos.company.com\\\"}}{{$id_field}}:{{end}}{{end}}" 'foos.company.com:'
- kubectl "${kube_flags_with_token[@]}" create -f - << __EOF__
- {
- "kind": "CustomResourceDefinition",
- "apiVersion": "apiextensions.k8s.io/v1beta1",
- "metadata": {
- "name": "bars.company.com"
- },
- "spec": {
- "group": "company.com",
- "version": "v1",
- "scope": "Namespaced",
- "names": {
- "plural": "bars",
- "kind": "Bar"
- }
- }
- }
- __EOF__
- # Post-Condition: assertion object exist
- 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:'
- # This test ensures that the name printer is able to output a resource
- # in the proper "kind.group/resource_name" format, and that the
- # resource builder is able to resolve a GVK when a kind.group pair is given.
- kubectl "${kube_flags_with_token[@]}" create -f - << __EOF__
- {
- "kind": "CustomResourceDefinition",
- "apiVersion": "apiextensions.k8s.io/v1beta1",
- "metadata": {
- "name": "resources.mygroup.example.com"
- },
- "spec": {
- "group": "mygroup.example.com",
- "version": "v1alpha1",
- "scope": "Namespaced",
- "names": {
- "plural": "resources",
- "singular": "resource",
- "kind": "Kind",
- "listKind": "KindList"
- }
- }
- }
- __EOF__
- # Post-Condition: assertion crd with non-matching kind and resource exists
- 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:'
- # This test ensures that we can create complex validation without client-side validation complaining
- kubectl "${kube_flags_with_token[@]}" create -f - << __EOF__
- {
- "kind": "CustomResourceDefinition",
- "apiVersion": "apiextensions.k8s.io/v1beta1",
- "metadata": {
- "name": "validfoos.company.com"
- },
- "spec": {
- "group": "company.com",
- "version": "v1",
- "scope": "Namespaced",
- "names": {
- "plural": "validfoos",
- "kind": "ValidFoo"
- },
- "validation": {
- "openAPIV3Schema": {
- "properties": {
- "spec": {
- "type": "array",
- "items": {
- "type": "number"
- }
- }
- }
- }
- }
- }
- }
- __EOF__
- # Post-Condition: assertion crd with non-matching kind and resource exists
- 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:'
- run_non_native_resource_tests
- # teardown
- kubectl delete customresourcedefinitions/foos.company.com "${kube_flags_with_token[@]}"
- kubectl delete customresourcedefinitions/bars.company.com "${kube_flags_with_token[@]}"
- kubectl delete customresourcedefinitions/resources.mygroup.example.com "${kube_flags_with_token[@]}"
- kubectl delete customresourcedefinitions/validfoos.company.com "${kube_flags_with_token[@]}"
- set +o nounset
- set +o errexit
- }
- kube::util::non_native_resources() {
- local times
- local wait
- local failed
- times=30
- wait=10
- for _ in $(seq 1 $times); do
- failed=""
- kubectl "${kube_flags[@]:?}" get --raw '/apis/company.com/v1' || failed=true
- kubectl "${kube_flags[@]}" get --raw '/apis/company.com/v1/foos' || failed=true
- kubectl "${kube_flags[@]}" get --raw '/apis/company.com/v1/bars' || failed=true
- if [ -z "${failed}" ]; then
- return 0
- fi
- sleep ${wait}
- done
- kube::log::error "Timed out waiting for non-native-resources; tried ${times} waiting ${wait}s between each"
- return 1
- }
- run_non_native_resource_tests() {
- set -o nounset
- set -o errexit
- create_and_use_new_namespace
- kube::log::status "Testing kubectl non-native resources"
- kube::util::non_native_resources
- # Test that we can list this new CustomResource (foos)
- kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" ''
- # Test that we can list this new CustomResource (bars)
- kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" ''
- # Test that we can list this new CustomResource (resources)
- kube::test::get_object_assert resources "{{range.items}}{{$id_field}}:{{end}}" ''
- # Test that we can create a new resource of type Kind
- kubectl "${kube_flags[@]}" create -f hack/testdata/CRD/resource.yaml "${kube_flags[@]}"
- # Test that -o name returns kind.group/resourcename
- output_message=$(kubectl "${kube_flags[@]}" get resource/myobj -o name)
- kube::test::if_has_string "${output_message}" 'kind.mygroup.example.com/myobj'
- output_message=$(kubectl "${kube_flags[@]}" get resources/myobj -o name)
- kube::test::if_has_string "${output_message}" 'kind.mygroup.example.com/myobj'
- output_message=$(kubectl "${kube_flags[@]}" get kind.mygroup.example.com/myobj -o name)
- kube::test::if_has_string "${output_message}" 'kind.mygroup.example.com/myobj'
- # Delete the resource with cascade.
- kubectl "${kube_flags[@]}" delete resources myobj --cascade=true
- # Make sure it's gone
- kube::test::wait_object_assert resources "{{range.items}}{{$id_field}}:{{end}}" ''
- # Test that we can create a new resource of type Foo
- kubectl "${kube_flags[@]}" create -f hack/testdata/CRD/foo.yaml "${kube_flags[@]}"
- # Test that we can list this new custom resource
- kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" 'test:'
- # Test alternate forms
- kube::test::get_object_assert foo "{{range.items}}{{$id_field}}:{{end}}" 'test:'
- kube::test::get_object_assert foos.company.com "{{range.items}}{{$id_field}}:{{end}}" 'test:'
- kube::test::get_object_assert foos.v1.company.com "{{range.items}}{{$id_field}}:{{end}}" 'test:'
- # Test all printers, with lists and individual items
- kube::log::status "Testing CustomResource printing"
- kubectl "${kube_flags[@]}" get foos
- kubectl "${kube_flags[@]}" get foos/test
- kubectl "${kube_flags[@]}" get foos -o name
- kubectl "${kube_flags[@]}" get foos/test -o name
- kubectl "${kube_flags[@]}" get foos -o wide
- kubectl "${kube_flags[@]}" get foos/test -o wide
- kubectl "${kube_flags[@]}" get foos -o json
- kubectl "${kube_flags[@]}" get foos/test -o json
- kubectl "${kube_flags[@]}" get foos -o yaml
- kubectl "${kube_flags[@]}" get foos/test -o yaml
- kubectl "${kube_flags[@]}" get foos -o "jsonpath={.items[*].someField}" --allow-missing-template-keys=false
- kubectl "${kube_flags[@]}" get foos/test -o "jsonpath={.someField}" --allow-missing-template-keys=false
- kubectl "${kube_flags[@]}" get foos -o "go-template={{range .items}}{{.someField}}{{end}}" --allow-missing-template-keys=false
- kubectl "${kube_flags[@]}" get foos/test -o "go-template={{.someField}}" --allow-missing-template-keys=false
- output_message=$(kubectl "${kube_flags[@]}" get foos/test -o name)
- kube::test::if_has_string "${output_message}" 'foo.company.com/test'
- # Test patching
- kube::log::status "Testing CustomResource patching"
- kubectl "${kube_flags[@]}" patch foos/test -p '{"patched":"value1"}' --type=merge
- kube::test::get_object_assert foos/test "{{.patched}}" 'value1'
- kubectl "${kube_flags[@]}" patch foos/test -p '{"patched":"value2"}' --type=merge --record
- kube::test::get_object_assert foos/test "{{.patched}}" 'value2'
- kubectl "${kube_flags[@]}" patch foos/test -p '{"patched":null}' --type=merge --record
- kube::test::get_object_assert foos/test "{{.patched}}" '<no value>'
- # Get local version
- CRD_RESOURCE_FILE="${KUBE_TEMP}/crd-foos-test.json"
- kubectl "${kube_flags[@]}" get foos/test -o json > "${CRD_RESOURCE_FILE}"
- # cannot apply strategic patch locally
- CRD_PATCH_ERROR_FILE="${KUBE_TEMP}/crd-foos-test-error"
- ! kubectl "${kube_flags[@]}" patch --local -f "${CRD_RESOURCE_FILE}" -p '{"patched":"value3"}' 2> "${CRD_PATCH_ERROR_FILE}" || exit 1
- if grep -q "try --type merge" "${CRD_PATCH_ERROR_FILE}"; then
- kube::log::status "\"kubectl patch --local\" returns error as expected for CustomResource: $(cat "${CRD_PATCH_ERROR_FILE}")"
- else
- kube::log::status "\"kubectl patch --local\" returns unexpected error or non-error: $(cat "${CRD_PATCH_ERROR_FILE}")"
- exit 1
- fi
- # can apply merge patch locally
- kubectl "${kube_flags[@]}" patch --local -f "${CRD_RESOURCE_FILE}" -p '{"patched":"value3"}' --type=merge -o json
- # can apply merge patch remotely
- kubectl "${kube_flags[@]}" patch --record -f "${CRD_RESOURCE_FILE}" -p '{"patched":"value3"}' --type=merge -o json
- kube::test::get_object_assert foos/test "{{.patched}}" 'value3'
- rm "${CRD_RESOURCE_FILE}"
- rm "${CRD_PATCH_ERROR_FILE}"
- # Test labeling
- kube::log::status "Testing CustomResource labeling"
- kubectl "${kube_flags[@]}" label foos --all listlabel=true
- kubectl "${kube_flags[@]}" label foo/test itemlabel=true
- # Test annotating
- kube::log::status "Testing CustomResource annotating"
- kubectl "${kube_flags[@]}" annotate foos --all listannotation=true
- kubectl "${kube_flags[@]}" annotate foo/test itemannotation=true
- # Test describing
- kube::log::status "Testing CustomResource describing"
- kubectl "${kube_flags[@]}" describe foos
- kubectl "${kube_flags[@]}" describe foos/test
- kubectl "${kube_flags[@]}" describe foos | grep listlabel=true
- kubectl "${kube_flags[@]}" describe foos | grep itemlabel=true
- # Delete the resource with cascade.
- kubectl "${kube_flags[@]}" delete foos test --cascade=true
- # Make sure it's gone
- kube::test::wait_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" ''
- # Test that we can create a new resource of type Bar
- kubectl "${kube_flags[@]}" create -f hack/testdata/CRD/bar.yaml "${kube_flags[@]}"
- # Test that we can list this new custom resource
- kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" 'test:'
- # Test that we can watch the resource.
- # Start watcher in background with process substitution,
- # so we can read from stdout asynchronously.
- kube::log::status "Testing CustomResource watching"
- exec 3< <(kubectl "${kube_flags[@]}" get bars --request-timeout=1m --watch-only -o name & echo $! ; wait)
- local watch_pid
- read -r <&3 watch_pid
- # We can't be sure when the watch gets established,
- # so keep triggering events (in the background) until something comes through.
- local tries=0
- while [ ${tries} -lt 10 ]; do
- tries=$((tries+1))
- kubectl "${kube_flags[@]}" patch bars/test -p "{\"patched\":\"${tries}\"}" --type=merge
- sleep 1
- done &
- local patch_pid=$!
- # Wait up to 30s for a complete line of output.
- local watch_output
- read -r <&3 -t 30 watch_output
- # Stop the watcher and the patch loop.
- kill -9 "${watch_pid}"
- kill -9 "${patch_pid}"
- kube::test::if_has_string "${watch_output}" 'bar.company.com/test'
- # Delete the resource without cascade.
- kubectl "${kube_flags[@]}" delete bars test --cascade=false
- # Make sure it's gone
- kube::test::wait_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" ''
- # Test that we can create single item via apply
- kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/foo.yaml
- # Test that we have create a foo named test
- kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" 'test:'
- # Test that the field has the expected value
- kube::test::get_object_assert foos/test '{{.someField}}' 'field1'
- # Test that apply an empty patch doesn't change fields
- kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/foo.yaml
- # Test that the field has the same value after re-apply
- kube::test::get_object_assert foos/test '{{.someField}}' 'field1'
- # Test that apply has updated the subfield
- kube::test::get_object_assert foos/test '{{.nestedField.someSubfield}}' 'subfield1'
- # Update a subfield and then apply the change
- kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/foo-updated-subfield.yaml
- # Test that apply has updated the subfield
- kube::test::get_object_assert foos/test '{{.nestedField.someSubfield}}' 'modifiedSubfield'
- # Test that the field has the expected value
- kube::test::get_object_assert foos/test '{{.nestedField.otherSubfield}}' 'subfield2'
- # Delete a subfield and then apply the change
- kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/foo-deleted-subfield.yaml
- # Test that apply has deleted the field
- kube::test::get_object_assert foos/test '{{.nestedField.otherSubfield}}' '<no value>'
- # Test that the field does not exist
- kube::test::get_object_assert foos/test '{{.nestedField.newSubfield}}' '<no value>'
- # Add a field and then apply the change
- kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/foo-added-subfield.yaml
- # Test that apply has added the field
- kube::test::get_object_assert foos/test '{{.nestedField.newSubfield}}' 'subfield3'
- # Delete the resource
- kubectl "${kube_flags[@]}" delete -f hack/testdata/CRD/foo.yaml
- # Make sure it's gone
- kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" ''
- # Test that we can create list via apply
- kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/multi-crd-list.yaml
- # Test that we have create a foo and a bar from a list
- kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" 'test-list:'
- kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" 'test-list:'
- # Test that the field has the expected value
- kube::test::get_object_assert foos/test-list '{{.someField}}' 'field1'
- kube::test::get_object_assert bars/test-list '{{.someField}}' 'field1'
- # Test that re-apply an list doesn't change anything
- kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/multi-crd-list.yaml
- # Test that the field has the same value after re-apply
- kube::test::get_object_assert foos/test-list '{{.someField}}' 'field1'
- kube::test::get_object_assert bars/test-list '{{.someField}}' 'field1'
- # Test that the fields have the expected value
- kube::test::get_object_assert foos/test-list '{{.someField}}' 'field1'
- kube::test::get_object_assert bars/test-list '{{.someField}}' 'field1'
- # Update fields and then apply the change
- kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/multi-crd-list-updated-field.yaml
- # Test that apply has updated the fields
- kube::test::get_object_assert foos/test-list '{{.someField}}' 'modifiedField'
- kube::test::get_object_assert bars/test-list '{{.someField}}' 'modifiedField'
- # Test that the field has the expected value
- kube::test::get_object_assert foos/test-list '{{.otherField}}' 'field2'
- kube::test::get_object_assert bars/test-list '{{.otherField}}' 'field2'
- # Delete fields and then apply the change
- kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/multi-crd-list-deleted-field.yaml
- # Test that apply has deleted the fields
- kube::test::get_object_assert foos/test-list '{{.otherField}}' '<no value>'
- kube::test::get_object_assert bars/test-list '{{.otherField}}' '<no value>'
- # Test that the fields does not exist
- kube::test::get_object_assert foos/test-list '{{.newField}}' '<no value>'
- kube::test::get_object_assert bars/test-list '{{.newField}}' '<no value>'
- # Add a field and then apply the change
- kubectl "${kube_flags[@]}" apply -f hack/testdata/CRD/multi-crd-list-added-field.yaml
- # Test that apply has added the field
- kube::test::get_object_assert foos/test-list '{{.newField}}' 'field3'
- kube::test::get_object_assert bars/test-list '{{.newField}}' 'field3'
- # Delete the resource
- kubectl "${kube_flags[@]}" delete -f hack/testdata/CRD/multi-crd-list.yaml
- # Make sure it's gone
- kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" ''
- kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" ''
- ## kubectl apply --prune
- # Test that no foo or bar exist
- kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" ''
- kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" ''
- # apply --prune on foo.yaml that has foo/test
- 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
- # check right crds exist
- kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" 'test:'
- kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" ''
- # apply --prune on bar.yaml that has bar/test
- 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
- # check right crds exist
- kube::test::wait_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" ''
- kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" 'test:'
- # Delete the resource
- kubectl "${kube_flags[@]}" delete -f hack/testdata/CRD/bar.yaml
- # Make sure it's gone
- kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" ''
- kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" ''
- # Test 'kubectl create' with namespace, and namespace cleanup.
- kubectl "${kube_flags[@]}" create namespace non-native-resources
- kubectl "${kube_flags[@]}" create -f hack/testdata/CRD/bar.yaml --namespace=non-native-resources
- kube::test::get_object_assert bars '{{len .items}}' '1' --namespace=non-native-resources
- kubectl "${kube_flags[@]}" delete namespace non-native-resources
- # Make sure objects go away.
- kube::test::wait_object_assert bars '{{len .items}}' '0' --namespace=non-native-resources
- # Make sure namespace goes away.
- local tries=0
- while kubectl "${kube_flags[@]}" get namespace non-native-resources && [ ${tries} -lt 10 ]; do
- tries=$((tries+1))
- sleep ${tries}
- done
- set +o nounset
- set +o errexit
- }
|