Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement kubectl rollout undo and history for DaemonSet #46144

Merged
merged 1 commit into from
Jun 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
40 changes: 40 additions & 0 deletions hack/make-rules/test-cmd-util.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ IMAGE_NGINX="gcr.io/google-containers/nginx:1.7.9"
IMAGE_DEPLOYMENT_R1="gcr.io/google-containers/nginx:test-cmd" # deployment-revision1.yaml
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"

# Expose kubectl directly for readability
PATH="${KUBE_OUTPUT_HOSTBIN}":$PATH
Expand Down Expand Up @@ -71,6 +73,7 @@ subjectaccessreviews="subjectaccessreviews"
thirdpartyresources="thirdpartyresources"
customresourcedefinitions="customresourcedefinitions"
daemonsets="daemonsets"
controllerrevisions="controllerrevisions"


# Stops the running kubectl proxy, if there is one.
Expand Down Expand Up @@ -2868,6 +2871,39 @@ run_daemonset_tests() {
kubectl apply -f hack/testdata/rollingupdate-daemonset.yaml "${kube_flags[@]}"
# Template Generation should stay 1
kube::test::get_object_assert 'daemonsets bind' "{{${template_generation_field}}}" '1'
# Clean up
kubectl delete -f hack/testdata/rollingupdate-daemonset.yaml "${kube_flags[@]}"
}

run_daemonset_history_tests() {
kube::log::status "Testing kubectl(v1:daemonsets, v1:controllerrevisions)"

### Test rolling back a DaemonSet
# Pre-condition: no DaemonSet or its pods exists
kube::test::get_object_assert daemonsets "{{range.items}}{{$id_field}}:{{end}}" ''
# Command
# Create a DaemonSet (revision 1)
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}:"
# 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}:"
# 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}:"
# 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}:"
# 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}:"
# 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}:"
# Clean up
kubectl delete -f hack/testdata/rollingupdate-daemonset.yaml "${kube_flags[@]}"
}

Expand Down Expand Up @@ -3103,6 +3139,7 @@ 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"

# Make sure "default" namespace exists.
if kube::test::if_supports_resource "${namespaces}" ; then
Expand Down Expand Up @@ -3555,6 +3592,9 @@ runTests() {

if kube::test::if_supports_resource "${daemonsets}" ; then
run_daemonset_tests
if kube::test::if_supports_resource "${controllerrevisions}"; then
run_daemonset_history_tests
fi
fi

###########################
Expand Down
27 changes: 27 additions & 0 deletions hack/testdata/rollingupdate-daemonset-rv2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: bind
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 10%
template:
metadata:
labels:
service: bind
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "service"
operator: "In"
values: ["bind"]
topologyKey: "kubernetes.io/hostname"
namespaces: []
containers:
- name: kubernetes-pause
image: gcr.io/google-containers/pause:latest
1 change: 1 addition & 0 deletions pkg/controller/daemon/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/runtime: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/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
Expand Down
16 changes: 5 additions & 11 deletions pkg/controller/daemon/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ limitations under the License.
package daemon

import (
"bytes"
"encoding/json"
"fmt"
"sort"

Expand All @@ -29,6 +27,7 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
intstrutil "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
Expand Down Expand Up @@ -297,23 +296,18 @@ func (dsc *DaemonSetsController) controlledHistories(ds *extensions.DaemonSet) (

// 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)
t, err := DecodeHistory(history)
return apiequality.Semantic.DeepEqual(template, t), err
}

func decodeHistory(history *apps.ControllerRevision) (*v1.PodTemplateSpec, error) {
raw := history.Data.Raw
decoder := json.NewDecoder(bytes.NewBuffer(raw))
func DecodeHistory(history *apps.ControllerRevision) (*v1.PodTemplateSpec, error) {
template := v1.PodTemplateSpec{}
err := decoder.Decode(&template)
err := json.Unmarshal(history.Data.Raw, &template)
return &template, err
}

func encodeTemplate(template *v1.PodTemplateSpec) ([]byte, error) {
buffer := new(bytes.Buffer)
encoder := json.NewEncoder(buffer)
err := encoder.Encode(template)
return buffer.Bytes(), err
return json.Marshal(template)
}

func (dsc *DaemonSetsController) snapshot(ds *extensions.DaemonSet, revision int64) (*apps.ControllerRevision, error) {
Expand Down
3 changes: 3 additions & 0 deletions pkg/kubectl/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ go_library(
"//pkg/apis/policy:go_default_library",
"//pkg/apis/rbac:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/client/clientset_generated/clientset/typed/apps/v1beta1:go_default_library",
"//pkg/client/clientset_generated/clientset/typed/core/v1:go_default_library",
"//pkg/client/clientset_generated/clientset/typed/extensions/v1beta1:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
Expand All @@ -76,6 +77,7 @@ go_library(
"//pkg/client/retry:go_default_library",
"//pkg/client/unversioned:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/controller/daemon:go_default_library",
"//pkg/controller/deployment/util:go_default_library",
"//pkg/credentialprovider:go_default_library",
"//pkg/kubectl/resource:go_default_library",
Expand All @@ -88,6 +90,7 @@ 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",
Expand Down
12 changes: 8 additions & 4 deletions pkg/kubectl/cmd/rollout/rollout.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,28 @@ import (

var (
rollout_long = templates.LongDesc(`
Manage a deployment using subcommands like "kubectl rollout undo deployment/abc"`)
Manage the rollout of a resource.` + rollout_valid_resources)

rollout_example = templates.Examples(`
# Rollback to the previous deployment
kubectl rollout undo deployment/abc`)
kubectl rollout undo deployment/abc

# Check the rollout status of a daemonset
kubectl rollout status daemonset/foo`)

rollout_valid_resources = dedent.Dedent(`
Valid resource types include:

* deployments
* daemonsets
`)
)

func NewCmdRollout(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {

cmd := &cobra.Command{
Use: "rollout SUBCOMMAND",
Short: i18n.T("Manage a deployment rollout"),
Short: i18n.T("Manage the rollout of a resource"),
Long: rollout_long,
Example: rollout_example,
Run: cmdutil.DefaultSubCommandRun(errOut),
Expand All @@ -54,7 +59,6 @@ func NewCmdRollout(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
cmd.AddCommand(NewCmdRolloutPause(f, out))
cmd.AddCommand(NewCmdRolloutResume(f, out))
cmd.AddCommand(NewCmdRolloutUndo(f, out))

cmd.AddCommand(NewCmdRolloutStatus(f, out))

return cmd
Expand Down
6 changes: 3 additions & 3 deletions pkg/kubectl/cmd/rollout/rollout_history.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ var (
# View the rollout history of a deployment
kubectl rollout history deployment/abc

# View the details of deployment revision 3
kubectl rollout history deployment/abc --revision=3`)
# View the details of daemonset revision 3
kubectl rollout history daemonset/abc --revision=3`)
)

func NewCmdRolloutHistory(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &resource.FilenameOptions{}

validArgs := []string{"deployment"}
validArgs := []string{"deployment", "daemonset"}
argAliases := kubectl.ResourceAliases(validArgs)

cmd := &cobra.Command{
Expand Down
2 changes: 1 addition & 1 deletion pkg/kubectl/cmd/rollout/rollout_pause.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ var (
Mark the provided resource as paused

Paused resources will not be reconciled by a controller.
Use \"kubectl rollout resume\" to resume a paused resource.
Use "kubectl rollout resume" to resume a paused resource.
Currently only deployments support being paused.`)

pause_example = templates.Examples(`
Expand Down
2 changes: 1 addition & 1 deletion pkg/kubectl/cmd/rollout/rollout_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ var (
func NewCmdRolloutStatus(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &resource.FilenameOptions{}

validArgs := []string{"deployment"}
validArgs := []string{"deployment", "daemonset"}
argAliases := kubectl.ResourceAliases(validArgs)

cmd := &cobra.Command{
Expand Down
6 changes: 3 additions & 3 deletions pkg/kubectl/cmd/rollout/rollout_undo.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ var (
# Rollback to the previous deployment
kubectl rollout undo deployment/abc

# Rollback to deployment revision 3
kubectl rollout undo deployment/abc --to-revision=3
# Rollback to daemonset revision 3
kubectl rollout undo daemonset/abc --to-revision=3

# Rollback to the previous deployment with dry-run
kubectl rollout undo --dry-run=true deployment/abc`)
Expand All @@ -64,7 +64,7 @@ var (
func NewCmdRolloutUndo(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &UndoOptions{}

validArgs := []string{"deployment"}
validArgs := []string{"deployment", "daemonset"}
argAliases := kubectl.ResourceAliases(validArgs)

cmd := &cobra.Command{
Expand Down