Skip to content

Commit 0dee03d

Browse files
committed
Even with build error, kubectl apply should apply all valid resources
1 parent ec8c186 commit 0dee03d

File tree

6 files changed

+120
-22
lines changed

6 files changed

+120
-22
lines changed

hack/testdata/multi-resource-2.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Simple test that errors should not block apply of valid
2+
# resources. The ConfigMap should successfully apply, while
3+
# the custom resource fails because the CRD is missing.
4+
apiVersion: v1
5+
kind: ConfigMap
6+
metadata:
7+
name: foo
8+
---
9+
apiVersion: example.com/v1
10+
kind: Bogus
11+
metadata:
12+
name: foo

hack/testdata/multi-resource-3.yaml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Test failures do not block the apply for valid resources.
2+
# pod-a and pod-c should apply, while POD-B is invalid
3+
# because of the capitialization.
4+
apiVersion: v1
5+
kind: Pod
6+
metadata:
7+
name: pod-a
8+
spec:
9+
containers:
10+
- name: kubernetes-pause
11+
image: k8s.gcr.io/pause:2.0
12+
---
13+
apiVersion: v1
14+
kind: Pod
15+
metadata:
16+
name: POD-B
17+
spec:
18+
containers:
19+
- name: kubernetes-pause
20+
image: k8s.gcr.io/pause:2.0
21+
---
22+
apiVersion: v1
23+
kind: Pod
24+
metadata:
25+
name: pod-c
26+
spec:
27+
containers:
28+
- name: kubernetes-pause
29+
image: k8s.gcr.io/pause:2.0
30+

hack/testdata/multi-resource-4.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Tests that initial failures to not block subsequent applies.
2+
# Initial apply for Widget fails, since CRD is not applied yet,
3+
# but the CRD apply should succeed. Subsequent custom resource
4+
# apply of Widget should succeed.
5+
apiVersion: example.com/v1
6+
kind: Widget
7+
metadata:
8+
name: foo
9+
---
10+
apiVersion: apiextensions.k8s.io/v1beta1
11+
kind: CustomResourceDefinition
12+
metadata:
13+
name: widgets.example.com
14+
spec:
15+
group: example.com
16+
version: v1
17+
scope: Namespaced
18+
names:
19+
plural: widgets
20+
kind: Widget

staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -316,13 +316,14 @@ func isIncompatibleServerError(err error) bool {
316316
return err.(*errors.StatusError).Status().Code == http.StatusUnsupportedMediaType
317317
}
318318

319-
// GetObjects returns a (possibly cached) version of all the objects to apply
320-
// as a slice of pointer to resource.Info. The resource.Info contains the object
321-
// and some other denormalized data. This function should not be called until
322-
// AFTER the "complete" and "validate" methods have been called to ensure that
323-
// the ApplyOptions is filled in and valid. Returns an error if the resource
324-
// builder returns an error retrieving the objects.
319+
// GetObjects returns a (possibly cached) version of all the valid objects to apply
320+
// as a slice of pointer to resource.Info and an error if one or more occurred.
321+
// IMPORTANT: This function can return both valid objects AND an error, since
322+
// "ContinueOnError" is set on the builder. This function should not be called
323+
// until AFTER the "complete" and "validate" methods have been called to ensure that
324+
// the ApplyOptions is filled in and valid.
325325
func (o *ApplyOptions) GetObjects() ([]*resource.Info, error) {
326+
var err error = nil
326327
if !o.objectsCached {
327328
// include the uninitialized objects by default if --prune is true
328329
// unless explicitly set --include-uninitialized=false
@@ -335,17 +336,10 @@ func (o *ApplyOptions) GetObjects() ([]*resource.Info, error) {
335336
LabelSelectorParam(o.Selector).
336337
Flatten().
337338
Do()
338-
if err := r.Err(); err != nil {
339-
return nil, err
340-
}
341-
infos, err := r.Infos()
342-
if err != nil {
343-
return nil, err
344-
}
345-
o.objects = infos
339+
o.objects, err = r.Infos()
346340
o.objectsCached = true
347341
}
348-
return o.objects, nil
342+
return o.objects, err
349343
}
350344

351345
// SetObjects stores the set of objects (as resource.Info) to be

test/cmd/apply.sh

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -274,18 +274,60 @@ __EOF__
274274
# cleanup
275275
kubectl delete --kustomize hack/testdata/kustomize
276276

277-
## kubectl apply multiple resources with initial failure.
278-
# Pre-Condition: no POD exists
277+
## kubectl apply multiple resources with one failure during apply phase.
278+
# Pre-Condition: namepace does not exist and no POD exists
279+
output_message=$(! kubectl get namespace multi-resource-ns 2>&1 "${kube_flags[@]:?}")
280+
kube::test::if_has_string "${output_message}" 'namespaces "multi-resource-ns" not found'
279281
kube::test::get_object_assert pods "{{range.items}}{{${id_field:?}}}:{{end}}" ''
280282
# First pass, namespace is created, but pod is not (since namespace does not exist yet).
281-
kubectl apply -f hack/testdata/multi-resource.yaml "${kube_flags[@]:?}"
282-
output_message=$(! kubectl get pods test-pod 2>&1 "${kube_flags[@]:?}")
283+
output_message=$(! kubectl apply -f hack/testdata/multi-resource-1.yaml 2>&1 "${kube_flags[@]:?}")
284+
kube::test::if_has_string "${output_message}" 'namespaces "multi-resource-ns" not found'
285+
output_message=$(! kubectl get pods test-pod -n multi-resource-ns 2>&1 "${kube_flags[@]:?}")
283286
kube::test::if_has_string "${output_message}" 'pods "test-pod" not found'
284287
# Second pass, pod is created (now that namespace exists).
285-
kubectl apply -f hack/testdata/multi-resource.yaml "${kube_flags[@]:?}"
286-
kube::test::get_object_assert 'pod test-pod' "{{${id_field}}}" 'test-pod'
288+
kubectl apply -f hack/testdata/multi-resource-1.yaml "${kube_flags[@]:?}"
289+
kube::test::get_object_assert 'pods test-pod -n multi-resource-ns' "{{${id_field}}}" 'test-pod'
290+
# cleanup
291+
kubectl delete -f hack/testdata/multi-resource-1.yaml "${kube_flags[@]:?}"
292+
293+
## kubectl apply multiple resources with one failure during builder phase.
294+
# Pre-Condition: No configmaps
295+
kube::test::get_object_assert configmaps "{{range.items}}{{${id_field:?}}}:{{end}}" ''
296+
# Apply a configmap and a bogus custom resource.
297+
output_message=$(! kubectl apply -f hack/testdata/multi-resource-2.yaml 2>&1 "${kube_flags[@]:?}")
298+
# Should be error message from bogus custom resource.
299+
kube::test::if_has_string "${output_message}" 'no matches for kind "Bogus" in version "example.com/v1"'
300+
# ConfigMap should have been created even with custom resource error.
301+
kube::test::get_object_assert 'configmaps foo' "{{${id_field}}}" 'foo'
302+
# cleanup
303+
kubectl delete configmaps foo "${kube_flags[@]:?}"
304+
305+
## kubectl apply multiple resources with one failure during builder phase.
306+
# Pre-Condition: No pods exist.
307+
kube::test::get_object_assert pods "{{range.items}}{{${id_field:?}}}:{{end}}" ''
308+
# Applies three pods, one of which is invalid (POD-B), two succeed (pod-a, pod-c).
309+
output_message=$(! kubectl apply -f hack/testdata/multi-resource-3.yaml 2>&1 "${kube_flags[@]:?}")
310+
kube::test::if_has_string "${output_message}" 'The Pod "POD-B" is invalid'
311+
kube::test::get_object_assert 'pods pod-a' "{{${id_field}}}" 'pod-a'
312+
kube::test::get_object_assert 'pods pod-c' "{{${id_field}}}" 'pod-c'
313+
# cleanup
314+
kubectl delete pod pod-a pod-c "${kube_flags[@]:?}"
315+
kube::test::get_object_assert pods "{{range.items}}{{${id_field:?}}}:{{end}}" ''
316+
317+
## kubectl apply multiple resources with one failure during apply phase.
318+
# Pre-Condition: crd does not exist, and custom resource does not exist.
319+
kube::test::get_object_assert crds "{{range.items}}{{${id_field:?}}}:{{end}}" ''
320+
# First pass, custom resource fails, but crd apply succeeds.
321+
output_message=$(! kubectl apply -f hack/testdata/multi-resource-4.yaml 2>&1 "${kube_flags[@]:?}")
322+
kube::test::if_has_string "${output_message}" 'no matches for kind "Widget" in version "example.com/v1"'
323+
output_message=$(! kubectl get widgets foo 2>&1 "${kube_flags[@]:?}")
324+
kube::test::if_has_string "${output_message}" 'widgets.example.com "foo" not found'
325+
kube::test::get_object_assert 'crds widgets.example.com' "{{${id_field}}}" 'widgets.example.com'
326+
# Second pass, custom resource is created (now that crd exists).
327+
kubectl apply -f hack/testdata/multi-resource-4.yaml "${kube_flags[@]:?}"
328+
kube::test::get_object_assert 'widget foo' "{{${id_field}}}" 'foo'
287329
# cleanup
288-
kubectl delete -f hack/testdata/multi-resource.yaml
330+
kubectl delete -f hack/testdata/multi-resource-4.yaml "${kube_flags[@]:?}"
289331

290332
set +o nounset
291333
set +o errexit

0 commit comments

Comments
 (0)