Merge pull request #53158 from liggitt/update-pod-spec-versioned

Automatic merge from submit-queue (batch tested with PRs 53101, 53158, 52165). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Calculate patches for  commands using input version

Fixes #53040

the encoder used for encoding these objects while calculating patches does not have sufficient information to select a correct version when the object does not exist in all versions of a target group (like replicasets not existing in apps/v1beta1)

this PR wraps the encoder to first convert to the same version used to read the object (based on the mapping's GroupVersion)

long-term, we should switch UpdatePodSpecForObject to work on versioned objects and v1.PodSpec and avoid conversion altogether

```release-note
Fixes an issue with `kubectl set` commands encountering conversion errors for ReplicaSet and DaemonSet objects
```
This commit is contained in:
Kubernetes Submit Queue 2017-09-29 14:36:19 -07:00 committed by GitHub
commit 4425841ce2
6 changed files with 35 additions and 5 deletions

View File

@ -3004,6 +3004,16 @@ run_rs_tests() {
# Cleanup services # Cleanup services
kubectl delete service frontend{,-2} "${kube_flags[@]}" kubectl delete service frontend{,-2} "${kube_flags[@]}"
# Test set commands
# Pre-condition: frontend replica set exists at generation 1
kube::test::get_object_assert 'rs frontend' "{{${generation_field}}}" '1'
kubectl set image rs/frontend "${kube_flags[@]}" *=gcr.io/google-containers/pause:test-cmd
kube::test::get_object_assert 'rs frontend' "{{${generation_field}}}" '2'
kubectl set env rs/frontend "${kube_flags[@]}" foo=bar
kube::test::get_object_assert 'rs frontend' "{{${generation_field}}}" '3'
kubectl set resources rs/frontend "${kube_flags[@]}" --limits=cpu=200m,memory=512Mi
kube::test::get_object_assert 'rs frontend' "{{${generation_field}}}" '4'
### Delete replica set with id ### Delete replica set with id
# Pre-condition: frontend replica set exists # Pre-condition: frontend replica set exists
kube::test::get_object_assert rs "{{range.items}}{{$id_field}}:{{end}}" 'frontend:' kube::test::get_object_assert rs "{{range.items}}{{$id_field}}:{{end}}" 'frontend:'
@ -3083,6 +3093,14 @@ run_daemonset_tests() {
kubectl apply -f hack/testdata/rollingupdate-daemonset.yaml "${kube_flags[@]}" kubectl apply -f hack/testdata/rollingupdate-daemonset.yaml "${kube_flags[@]}"
# Template Generation should stay 1 # Template Generation should stay 1
kube::test::get_object_assert 'daemonsets bind' "{{${template_generation_field}}}" '1' kube::test::get_object_assert 'daemonsets bind' "{{${template_generation_field}}}" '1'
# Test set commands
kubectl set image daemonsets/bind "${kube_flags[@]}" *=gcr.io/google-containers/pause:test-cmd
kube::test::get_object_assert 'daemonsets bind' "{{${template_generation_field}}}" '2'
kubectl set env daemonsets/bind "${kube_flags[@]}" foo=bar
kube::test::get_object_assert 'daemonsets bind' "{{${template_generation_field}}}" '3'
kubectl set resources daemonsets/bind "${kube_flags[@]}" --limits=cpu=200m,memory=512Mi
kube::test::get_object_assert 'daemonsets bind' "{{${template_generation_field}}}" '4'
# Clean up # Clean up
kubectl delete -f hack/testdata/rollingupdate-daemonset.yaml "${kube_flags[@]}" kubectl delete -f hack/testdata/rollingupdate-daemonset.yaml "${kube_flags[@]}"
@ -4374,6 +4392,7 @@ runTests() {
change_cause_annotation='.*kubernetes.io/change-cause.*' change_cause_annotation='.*kubernetes.io/change-cause.*'
pdb_min_available=".spec.minAvailable" pdb_min_available=".spec.minAvailable"
pdb_max_unavailable=".spec.maxUnavailable" pdb_max_unavailable=".spec.maxUnavailable"
generation_field=".metadata.generation"
template_generation_field=".spec.templateGeneration" template_generation_field=".spec.templateGeneration"
container_len="(len .spec.template.spec.containers)" container_len="(len .spec.template.spec.containers)"
image_field0="(index .spec.template.spec.containers 0).image" image_field0="(index .spec.template.spec.containers 0).image"

View File

@ -126,7 +126,9 @@ type patchFn func(*resource.Info) ([]byte, error)
// the changes in the object. Encoder must be able to encode the info into the appropriate destination type. // the changes in the object. Encoder must be able to encode the info into the appropriate destination type.
// This function returns whether the mutation function made any change in the original object. // This function returns whether the mutation function made any change in the original object.
func CalculatePatch(patch *Patch, encoder runtime.Encoder, mutateFn patchFn) bool { func CalculatePatch(patch *Patch, encoder runtime.Encoder, mutateFn patchFn) bool {
patch.Before, patch.Err = runtime.Encode(encoder, patch.Info.Object) versionedEncoder := api.Codecs.EncoderForVersion(encoder, patch.Info.Mapping.GroupVersionKind.GroupVersion())
patch.Before, patch.Err = runtime.Encode(versionedEncoder, patch.Info.Object)
patch.After, patch.Err = mutateFn(patch.Info) patch.After, patch.Err = mutateFn(patch.Info)
if patch.Err != nil { if patch.Err != nil {

View File

@ -381,7 +381,9 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
}) })
if err == nil { if err == nil {
return runtime.Encode(o.Encoder, info.Object) // TODO: switch UpdatePodSpecForObject to work on v1.PodSpec, use info.VersionedObject, and avoid conversion completely
versionedEncoder := api.Codecs.EncoderForVersion(o.Encoder, info.Mapping.GroupVersionKind.GroupVersion())
return runtime.Encode(versionedEncoder, info.Object)
} }
return nil, err return nil, err
}) })

View File

@ -225,7 +225,9 @@ func (o *ImageOptions) Run() error {
return nil return nil
}) })
if transformed && err == nil { if transformed && err == nil {
return runtime.Encode(o.Encoder, info.Object) // TODO: switch UpdatePodSpecForObject to work on v1.PodSpec, use info.VersionedObject, and avoid conversion completely
versionedEncoder := api.Codecs.EncoderForVersion(o.Encoder, info.Mapping.GroupVersionKind.GroupVersion())
return runtime.Encode(versionedEncoder, info.Object)
} }
return nil, err return nil, err
}) })

View File

@ -216,7 +216,9 @@ func (o *ResourcesOptions) Run() error {
return nil return nil
}) })
if transformed && err == nil { if transformed && err == nil {
return runtime.Encode(o.Encoder, info.Object) // TODO: switch UpdatePodSpecForObject to work on v1.PodSpec, use info.VersionedObject, and avoid conversion completely
versionedEncoder := api.Codecs.EncoderForVersion(o.Encoder, info.Mapping.GroupVersionKind.GroupVersion())
return runtime.Encode(versionedEncoder, info.Object)
} }
return nil, err return nil, err
}) })

View File

@ -28,6 +28,7 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
@ -218,7 +219,9 @@ func (o *SubjectOptions) Run(f cmdutil.Factory, fn updateSubjects) error {
transformed, err := updateSubjectForObject(info.Object, subjects, fn) transformed, err := updateSubjectForObject(info.Object, subjects, fn)
if transformed && err == nil { if transformed && err == nil {
return runtime.Encode(o.Encoder, info.Object) // TODO: switch UpdatePodSpecForObject to work on v1.PodSpec, use info.VersionedObject, and avoid conversion completely
versionedEncoder := api.Codecs.EncoderForVersion(o.Encoder, info.Mapping.GroupVersionKind.GroupVersion())
return runtime.Encode(versionedEncoder, info.Object)
} }
return nil, err return nil, err
}) })