mirror of https://github.com/k3s-io/k3s
Merge pull request #47075 from janetkuo/ds-history-patch
Automatic merge from submit-queue Change what is stored in DaemonSet history `.data` **What this PR does / why we need it**: In DaemonSet history `.data`, store a strategic merge patch that can be applied to restore a DaemonSet. Only PodSpecTemplate is saved. This will become consistent with the data stored in StatefulSet history. Before this fix, a serialized pod template is stored in `.data`; however, seriazlized pod template isn't a `runtime.RawExtension`, and caused problems when controllers try to patch the history's controller ref. **Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #47008 **Special notes for your reviewer**: @kubernetes/sig-apps-bugs @erictune @kow3ns @kargakis @lukaszo @mengqiy **Release note**: ```release-note NONE ```pull/6/head
commit
aa35738a21
|
@ -43,6 +43,7 @@ IMAGE_DEPLOYMENT_R2="$IMAGE_NGINX" # deployment-revision2.yaml
|
|||
IMAGE_PERL="gcr.io/google-containers/perl"
|
||||
IMAGE_DAEMONSET_R1="gcr.io/google-containers/pause:2.0"
|
||||
IMAGE_DAEMONSET_R2="gcr.io/google-containers/pause:latest"
|
||||
IMAGE_DAEMONSET_R2_2="gcr.io/google-containers/nginx:test-cmd" # rollingupdate-daemonset-rv2.yaml
|
||||
|
||||
# Expose kubectl directly for readability
|
||||
PATH="${KUBE_OUTPUT_HOSTBIN}":$PATH
|
||||
|
@ -2900,23 +2901,32 @@ run_daemonset_history_tests() {
|
|||
kubectl apply -f hack/testdata/rollingupdate-daemonset.yaml "${kube_flags[@]}"
|
||||
# Rollback to revision 1 - should be no-op
|
||||
kubectl rollout undo daemonset --to-revision=1 "${kube_flags[@]}"
|
||||
kube::test::get_object_assert daemonset "{{range.items}}{{$daemonset_image_field}}:{{end}}" "${IMAGE_DAEMONSET_R1}:"
|
||||
kube::test::get_object_assert daemonset "{{range.items}}{{$daemonset_image_field0}}:{{end}}" "${IMAGE_DAEMONSET_R1}:"
|
||||
kube::test::get_object_assert daemonset "{{range.items}}{{$container_len}}{{end}}" "1"
|
||||
# Update the DaemonSet (revision 2)
|
||||
kubectl apply -f hack/testdata/rollingupdate-daemonset-rv2.yaml "${kube_flags[@]}"
|
||||
kube::test::wait_object_assert daemonset "{{range.items}}{{$daemonset_image_field}}:{{end}}" "${IMAGE_DAEMONSET_R2}:"
|
||||
kube::test::wait_object_assert daemonset "{{range.items}}{{$daemonset_image_field0}}:{{end}}" "${IMAGE_DAEMONSET_R2}:"
|
||||
kube::test::wait_object_assert daemonset "{{range.items}}{{$daemonset_image_field1}}:{{end}}" "${IMAGE_DAEMONSET_R2_2}:"
|
||||
kube::test::get_object_assert daemonset "{{range.items}}{{$container_len}}{{end}}" "2"
|
||||
# Rollback to revision 1 with dry-run - should be no-op
|
||||
kubectl rollout undo daemonset --dry-run=true "${kube_flags[@]}"
|
||||
kube::test::get_object_assert daemonset "{{range.items}}{{$daemonset_image_field}}:{{end}}" "${IMAGE_DAEMONSET_R2}:"
|
||||
kube::test::get_object_assert daemonset "{{range.items}}{{$daemonset_image_field0}}:{{end}}" "${IMAGE_DAEMONSET_R2}:"
|
||||
kube::test::get_object_assert daemonset "{{range.items}}{{$daemonset_image_field1}}:{{end}}" "${IMAGE_DAEMONSET_R2_2}:"
|
||||
kube::test::get_object_assert daemonset "{{range.items}}{{$container_len}}{{end}}" "2"
|
||||
# Rollback to revision 1
|
||||
kubectl rollout undo daemonset --to-revision=1 "${kube_flags[@]}"
|
||||
kube::test::wait_object_assert daemonset "{{range.items}}{{$daemonset_image_field}}:{{end}}" "${IMAGE_DAEMONSET_R1}:"
|
||||
kube::test::wait_object_assert daemonset "{{range.items}}{{$daemonset_image_field0}}:{{end}}" "${IMAGE_DAEMONSET_R1}:"
|
||||
kube::test::get_object_assert daemonset "{{range.items}}{{$container_len}}{{end}}" "1"
|
||||
# Rollback to revision 1000000 - should fail
|
||||
output_message=$(! kubectl rollout undo daemonset --to-revision=1000000 "${kube_flags[@]}" 2>&1)
|
||||
kube::test::if_has_string "${output_message}" "unable to find specified revision"
|
||||
kube::test::get_object_assert daemonset "{{range.items}}{{$daemonset_image_field}}:{{end}}" "${IMAGE_DAEMONSET_R1}:"
|
||||
kube::test::get_object_assert daemonset "{{range.items}}{{$daemonset_image_field0}}:{{end}}" "${IMAGE_DAEMONSET_R1}:"
|
||||
kube::test::get_object_assert daemonset "{{range.items}}{{$container_len}}{{end}}" "1"
|
||||
# Rollback to last revision
|
||||
kubectl rollout undo daemonset "${kube_flags[@]}"
|
||||
kube::test::wait_object_assert daemonset "{{range.items}}{{$daemonset_image_field}}:{{end}}" "${IMAGE_DAEMONSET_R2}:"
|
||||
kube::test::wait_object_assert daemonset "{{range.items}}{{$daemonset_image_field0}}:{{end}}" "${IMAGE_DAEMONSET_R2}:"
|
||||
kube::test::wait_object_assert daemonset "{{range.items}}{{$daemonset_image_field1}}:{{end}}" "${IMAGE_DAEMONSET_R2_2}:"
|
||||
kube::test::get_object_assert daemonset "{{range.items}}{{$container_len}}{{end}}" "2"
|
||||
# Clean up
|
||||
kubectl delete -f hack/testdata/rollingupdate-daemonset.yaml "${kube_flags[@]}"
|
||||
}
|
||||
|
@ -3153,7 +3163,9 @@ runTests() {
|
|||
pdb_min_available=".spec.minAvailable"
|
||||
pdb_max_unavailable=".spec.maxUnavailable"
|
||||
template_generation_field=".spec.templateGeneration"
|
||||
daemonset_image_field="(index .spec.template.spec.containers 0).image"
|
||||
container_len="(len .spec.template.spec.containers)"
|
||||
daemonset_image_field0="(index .spec.template.spec.containers 0).image"
|
||||
daemonset_image_field1="(index .spec.template.spec.containers 1).image"
|
||||
|
||||
# Make sure "default" namespace exists.
|
||||
if kube::test::if_supports_resource "${namespaces}" ; then
|
||||
|
|
|
@ -25,3 +25,5 @@ spec:
|
|||
containers:
|
||||
- name: kubernetes-pause
|
||||
image: gcr.io/google-containers/pause:latest
|
||||
- name: app
|
||||
image: gcr.io/google-containers/nginx:test-cmd
|
||||
|
|
|
@ -41,7 +41,6 @@ go_library(
|
|||
"//plugin/pkg/scheduler/algorithm/predicates:go_default_library",
|
||||
"//plugin/pkg/scheduler/schedulercache:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
|
|
|
@ -17,11 +17,12 @@ limitations under the License.
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/golang/glog"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
@ -103,7 +104,7 @@ func (dsc *DaemonSetsController) constructHistory(ds *extensions.DaemonSet) (cur
|
|||
}
|
||||
// Compare histories with ds to separate cur and old history
|
||||
found := false
|
||||
found, err = Match(&ds.Spec.Template, history)
|
||||
found, err = Match(ds, history)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -294,24 +295,44 @@ func (dsc *DaemonSetsController) controlledHistories(ds *extensions.DaemonSet) (
|
|||
return result, nil
|
||||
}
|
||||
|
||||
// Match check if ds template is semantically equal to the template stored in history
|
||||
func Match(template *v1.PodTemplateSpec, history *apps.ControllerRevision) (bool, error) {
|
||||
t, err := DecodeHistory(history)
|
||||
return apiequality.Semantic.DeepEqual(template, t), err
|
||||
// Match check if the given DaemonSet's template matches the template stored in the given history.
|
||||
func Match(ds *extensions.DaemonSet, history *apps.ControllerRevision) (bool, error) {
|
||||
patch, err := getPatch(ds)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return bytes.Equal(patch, history.Data.Raw), nil
|
||||
}
|
||||
|
||||
func DecodeHistory(history *apps.ControllerRevision) (*v1.PodTemplateSpec, error) {
|
||||
template := v1.PodTemplateSpec{}
|
||||
err := json.Unmarshal(history.Data.Raw, &template)
|
||||
return &template, err
|
||||
// getPatch returns a strategic merge patch that can be applied to restore a Daemonset to a
|
||||
// previous version. If the returned error is nil the patch is valid. The current state that we save is just the
|
||||
// PodSpecTemplate. We can modify this later to encompass more state (or less) and remain compatible with previously
|
||||
// recorded patches.
|
||||
func getPatch(ds *extensions.DaemonSet) ([]byte, error) {
|
||||
dsBytes, err := json.Marshal(ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var raw map[string]interface{}
|
||||
err = json.Unmarshal(dsBytes, &raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objCopy := make(map[string]interface{})
|
||||
specCopy := make(map[string]interface{})
|
||||
|
||||
func encodeTemplate(template *v1.PodTemplateSpec) ([]byte, error) {
|
||||
return json.Marshal(template)
|
||||
// Create a patch of the DaemonSet that replaces spec.template
|
||||
spec := raw["spec"].(map[string]interface{})
|
||||
template := spec["template"].(map[string]interface{})
|
||||
specCopy["template"] = template
|
||||
template["$patch"] = "replace"
|
||||
objCopy["spec"] = specCopy
|
||||
patch, err := json.Marshal(objCopy)
|
||||
return patch, err
|
||||
}
|
||||
|
||||
func (dsc *DaemonSetsController) snapshot(ds *extensions.DaemonSet, revision int64) (*apps.ControllerRevision, error) {
|
||||
encodedTemplate, err := encodeTemplate(&ds.Spec.Template)
|
||||
patch, err := getPatch(ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -325,7 +346,7 @@ func (dsc *DaemonSetsController) snapshot(ds *extensions.DaemonSet, revision int
|
|||
Annotations: ds.Annotations,
|
||||
OwnerReferences: []metav1.OwnerReference{*newControllerRef(ds)},
|
||||
},
|
||||
Data: runtime.RawExtension{Raw: encodedTemplate},
|
||||
Data: runtime.RawExtension{Raw: patch},
|
||||
Revision: revision,
|
||||
}
|
||||
|
||||
|
@ -337,7 +358,7 @@ func (dsc *DaemonSetsController) snapshot(ds *extensions.DaemonSet, revision int
|
|||
return nil, getErr
|
||||
}
|
||||
// Check if we already created it
|
||||
done, err := Match(&ds.Spec.Template, existedHistory)
|
||||
done, err := Match(ds, existedHistory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -154,7 +154,6 @@ go_library(
|
|||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
|
@ -167,7 +166,9 @@ go_library(
|
|||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
|
|
|
@ -26,14 +26,17 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
appsv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||
externalclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
"k8s.io/kubernetes/pkg/controller/daemon"
|
||||
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
sliceutil "k8s.io/kubernetes/pkg/util/slice"
|
||||
|
@ -149,13 +152,10 @@ type DaemonSetHistoryViewer struct {
|
|||
// ViewHistory returns a revision-to-history map as the revision history of a deployment
|
||||
// TODO: this should be a describer
|
||||
func (h *DaemonSetHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) {
|
||||
ds, err := h.c.Extensions().DaemonSets(namespace).Get(name, metav1.GetOptions{})
|
||||
versionedClient := versionedClientsetForDaemonSet(h.c)
|
||||
ds, allHistory, err := controlledHistories(versionedClient, namespace, name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to retrieve DaemonSet %s: %v", name, err)
|
||||
}
|
||||
allHistory, err := controlledHistories(h.c, ds)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to find history controlled by DaemonSet %s: %v", ds.Name, err)
|
||||
return "", fmt.Errorf("unable to find history controlled by DaemonSet %s: %v", name, err)
|
||||
}
|
||||
historyInfo := make(map[int64]*appsv1beta1.ControllerRevision)
|
||||
for _, history := range allHistory {
|
||||
|
@ -173,11 +173,11 @@ func (h *DaemonSetHistoryViewer) ViewHistory(namespace, name string, revision in
|
|||
if !ok {
|
||||
return "", fmt.Errorf("unable to find the specified revision")
|
||||
}
|
||||
template, err := daemon.DecodeHistory(history)
|
||||
dsOfHistory, err := applyHistory(ds, history)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to decode history %s", history.Name)
|
||||
return "", fmt.Errorf("unable to parse history %s", history.Name)
|
||||
}
|
||||
return printTemplate(template)
|
||||
return printTemplate(&dsOfHistory.Spec.Template)
|
||||
}
|
||||
|
||||
// Print an overview of all Revisions
|
||||
|
@ -255,17 +255,19 @@ func (h *StatefulSetHistoryViewer) ViewHistory(namespace, name string, revision
|
|||
}
|
||||
|
||||
// controlledHistories returns all ControllerRevisions controlled by the given DaemonSet
|
||||
// TODO: Use external version DaemonSet instead when #3955 is fixed
|
||||
func controlledHistories(c clientset.Interface, ds *extensions.DaemonSet) ([]*appsv1beta1.ControllerRevision, error) {
|
||||
func controlledHistories(c externalclientset.Interface, namespace, name string) (*extensionsv1beta1.DaemonSet, []*appsv1beta1.ControllerRevision, error) {
|
||||
ds, err := c.ExtensionsV1beta1().DaemonSets(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to retrieve DaemonSet %s: %v", name, err)
|
||||
}
|
||||
var result []*appsv1beta1.ControllerRevision
|
||||
selector, err := metav1.LabelSelectorAsSelector(ds.Spec.Selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
versionedClient := versionedClientsetForDaemonSet(c)
|
||||
historyList, err := versionedClient.AppsV1beta1().ControllerRevisions(ds.Namespace).List(metav1.ListOptions{LabelSelector: selector.String()})
|
||||
historyList, err := c.AppsV1beta1().ControllerRevisions(ds.Namespace).List(metav1.ListOptions{LabelSelector: selector.String()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
for i := range historyList.Items {
|
||||
history := historyList.Items[i]
|
||||
|
@ -275,7 +277,29 @@ func controlledHistories(c clientset.Interface, ds *extensions.DaemonSet) ([]*ap
|
|||
}
|
||||
result = append(result, &history)
|
||||
}
|
||||
return result, nil
|
||||
return ds, result, nil
|
||||
}
|
||||
|
||||
// applyHistory returns a specific revision of DaemonSet by applying the given history to a copy of the given DaemonSet
|
||||
func applyHistory(ds *extensionsv1beta1.DaemonSet, history *appsv1beta1.ControllerRevision) (*extensionsv1beta1.DaemonSet, error) {
|
||||
obj, err := api.Scheme.New(ds.GroupVersionKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clone := obj.(*extensionsv1beta1.DaemonSet)
|
||||
cloneBytes, err := json.Marshal(clone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
patched, err := strategicpatch.StrategicMergePatch(cloneBytes, history.Data.Raw, clone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(patched, clone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clone, nil
|
||||
}
|
||||
|
||||
// TODO: copied here until this becomes a describer
|
||||
|
|
|
@ -24,10 +24,10 @@ import (
|
|||
"sort"
|
||||
"syscall"
|
||||
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
|
@ -36,7 +36,6 @@ import (
|
|||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
externalextensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/client/retry"
|
||||
"k8s.io/kubernetes/pkg/controller/daemon"
|
||||
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
|
@ -221,7 +220,8 @@ func (r *DaemonSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations ma
|
|||
if !ok {
|
||||
return "", fmt.Errorf("passed object is not a DaemonSet: %#v", obj)
|
||||
}
|
||||
allHistory, err := controlledHistories(r.c, ds)
|
||||
versionedClient := versionedClientsetForDaemonSet(r.c)
|
||||
versionedDS, allHistory, err := controlledHistories(versionedClient, ds.Namespace, ds.Name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to find history controlled by DaemonSet %s: %v", ds.Name, err)
|
||||
}
|
||||
|
@ -249,55 +249,36 @@ func (r *DaemonSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations ma
|
|||
return "", revisionNotFoundErr(toRevision)
|
||||
}
|
||||
|
||||
// Get the template of the history to rollback to
|
||||
toTemplate, err := getInternalTemplate(toHistory)
|
||||
if dryRun {
|
||||
appliedDS, err := applyHistory(versionedDS, toHistory)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
content := bytes.NewBuffer([]byte{})
|
||||
w := printersinternal.NewPrefixWriter(content)
|
||||
printersinternal.DescribePodTemplate(toTemplate, w)
|
||||
internalTemplate := &api.PodTemplateSpec{}
|
||||
if err := v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&appliedDS.Spec.Template, internalTemplate, nil); err != nil {
|
||||
return "", fmt.Errorf("failed to convert podtemplate while printing: %v", err)
|
||||
}
|
||||
printersinternal.DescribePodTemplate(internalTemplate, w)
|
||||
return fmt.Sprintf("will roll back to %s", content.String()), nil
|
||||
}
|
||||
|
||||
// Update DaemonSet template, and retry on conflict
|
||||
skipUpdate := false
|
||||
retryErr := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
var err error
|
||||
ds, err = r.c.Extensions().DaemonSets(ds.Namespace).Get(ds.Name, metav1.GetOptions{})
|
||||
// Skip if the revision already matches current DaemonSet
|
||||
done, err := daemon.Match(versionedDS, toHistory)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
if apiequality.Semantic.DeepEqual(toTemplate, &ds.Spec.Template) {
|
||||
skipUpdate = true
|
||||
return nil
|
||||
}
|
||||
ds.Spec.Template = *toTemplate
|
||||
_, err = r.c.Extensions().DaemonSets(ds.Namespace).Update(ds)
|
||||
return err
|
||||
})
|
||||
if retryErr != nil {
|
||||
return "", retryErr
|
||||
}
|
||||
if skipUpdate {
|
||||
if done {
|
||||
return fmt.Sprintf("%s (current template already matches revision %d)", rollbackSkipped, toRevision), nil
|
||||
}
|
||||
|
||||
return rollbackSuccess, nil
|
||||
// Restore revision
|
||||
if _, err = versionedClient.ExtensionsV1beta1().DaemonSets(ds.Namespace).Patch(ds.Name, types.StrategicMergePatchType, toHistory.Data.Raw); err != nil {
|
||||
return "", fmt.Errorf("failed restoring revision %d: %v", toRevision, err)
|
||||
}
|
||||
|
||||
func getInternalTemplate(toHistory *appsv1beta1.ControllerRevision) (*api.PodTemplateSpec, error) {
|
||||
template, err := daemon.DecodeHistory(toHistory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
internalTemplate := &api.PodTemplateSpec{}
|
||||
if err := v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(template, internalTemplate, nil); err != nil {
|
||||
return nil, fmt.Errorf("failed to convert podtemplate, %v", err)
|
||||
}
|
||||
return internalTemplate, nil
|
||||
return rollbackSuccess, nil
|
||||
}
|
||||
|
||||
func revisionNotFoundErr(r int64) error {
|
||||
|
|
|
@ -40,5 +40,6 @@ func versionedClientsetForDaemonSet(internalClient internalclientset.Interface)
|
|||
}
|
||||
return &externalclientset.Clientset{
|
||||
AppsV1beta1Client: apps.New(internalClient.Apps().RESTClient()),
|
||||
ExtensionsV1beta1Client: extensions.New(internalClient.Extensions().RESTClient()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -268,7 +268,7 @@ var _ = framework.KubeDescribe("Daemon set [Serial]", func() {
|
|||
// Check history and labels
|
||||
ds, err = c.Extensions().DaemonSets(ns).Get(ds.Name, metav1.GetOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
first := curHistory(listDaemonHistories(c, ns, label), &ds.Spec.Template)
|
||||
first := curHistory(listDaemonHistories(c, ns, label), ds)
|
||||
firstHash := ds.Labels[extensions.DefaultDaemonSetUniqueLabelKey]
|
||||
Expect(first.Labels[extensions.DefaultDaemonSetUniqueLabelKey]).To(Equal(firstHash))
|
||||
Expect(first.Revision).To(Equal(int64(1)))
|
||||
|
@ -295,7 +295,7 @@ var _ = framework.KubeDescribe("Daemon set [Serial]", func() {
|
|||
// Check history and labels
|
||||
ds, err = c.Extensions().DaemonSets(ns).Get(ds.Name, metav1.GetOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
cur := curHistory(listDaemonHistories(c, ns, label), &ds.Spec.Template)
|
||||
cur := curHistory(listDaemonHistories(c, ns, label), ds)
|
||||
curHash := ds.Labels[extensions.DefaultDaemonSetUniqueLabelKey]
|
||||
Expect(cur.Labels[extensions.DefaultDaemonSetUniqueLabelKey]).To(Equal(curHash))
|
||||
Expect(cur.Revision).To(Equal(int64(2)))
|
||||
|
@ -325,7 +325,7 @@ var _ = framework.KubeDescribe("Daemon set [Serial]", func() {
|
|||
// Check history and labels
|
||||
ds, err = c.Extensions().DaemonSets(ns).Get(ds.Name, metav1.GetOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
cur := curHistory(listDaemonHistories(c, ns, label), &ds.Spec.Template)
|
||||
cur := curHistory(listDaemonHistories(c, ns, label), ds)
|
||||
hash := ds.Labels[extensions.DefaultDaemonSetUniqueLabelKey]
|
||||
Expect(cur.Labels[extensions.DefaultDaemonSetUniqueLabelKey]).To(Equal(hash))
|
||||
Expect(cur.Revision).To(Equal(int64(1)))
|
||||
|
@ -353,7 +353,7 @@ var _ = framework.KubeDescribe("Daemon set [Serial]", func() {
|
|||
// Check history and labels
|
||||
ds, err = c.Extensions().DaemonSets(ns).Get(ds.Name, metav1.GetOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
cur = curHistory(listDaemonHistories(c, ns, label), &ds.Spec.Template)
|
||||
cur = curHistory(listDaemonHistories(c, ns, label), ds)
|
||||
hash = ds.Labels[extensions.DefaultDaemonSetUniqueLabelKey]
|
||||
Expect(cur.Labels[extensions.DefaultDaemonSetUniqueLabelKey]).To(Equal(hash))
|
||||
Expect(cur.Revision).To(Equal(int64(2)))
|
||||
|
@ -760,14 +760,14 @@ func listDaemonHistories(c clientset.Interface, ns string, label map[string]stri
|
|||
return historyList
|
||||
}
|
||||
|
||||
func curHistory(historyList *apps.ControllerRevisionList, template *v1.PodTemplateSpec) *apps.ControllerRevision {
|
||||
func curHistory(historyList *apps.ControllerRevisionList, ds *extensions.DaemonSet) *apps.ControllerRevision {
|
||||
var curHistory *apps.ControllerRevision
|
||||
foundCurHistories := 0
|
||||
for i := range historyList.Items {
|
||||
history := &historyList.Items[i]
|
||||
// Every history should have the hash label
|
||||
Expect(len(history.Labels[extensions.DefaultDaemonSetUniqueLabelKey])).To(BeNumerically(">", 0))
|
||||
match, err := daemon.Match(template, history)
|
||||
match, err := daemon.Match(ds, history)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
if match {
|
||||
curHistory = history
|
||||
|
|
Loading…
Reference in New Issue