diff --git a/staging/src/k8s.io/client-go/examples/create-update-delete-deployment/README.md b/staging/src/k8s.io/client-go/examples/create-update-delete-deployment/README.md index 6b7ed4eac98..7bf30576071 100644 --- a/staging/src/k8s.io/client-go/examples/create-update-delete-deployment/README.md +++ b/staging/src/k8s.io/client-go/examples/create-update-delete-deployment/README.md @@ -22,21 +22,28 @@ go build -o ./app Now, run this application on your workstation with your local kubeconfig file: ``` +./app +# or specify a kubeconfig file with flag ./app -kubeconfig=$HOME/.kube/config ``` Running this command will execute the following operations on your cluster: -1. **Create Deployment:** This will create a 2 replica Deployment. Verify with - `kubectl get pods`. +1. **Create Deployment:** This will create a 2 replica Deployment with + annotation `fizz=buzz`. Verify with `kubectl get pods`. 2. **Update Deployment:** This will update the Deployment resource created in - previous step to set the replica count to 1 and add annotations. You are + previous step to set the replica count to 1 and update annotations. You are encouraged to inspect the retry loop that handles conflicts. Verify the new replica count and `foo=bar` annotation with `kubectl describe deployment demo`. -3. **List Deployments:** This will retrieve Deployments in the `default` +3. **Rollback Deployment:** This will rollback the Deployment to the last + revision, in this case the revision created in Step 1. Use `kubectl describe` + to verify the original annotation `fizz=buzz`. Also note the replica count + is still 1; this is because a Deployment revision is created if and only + if the Deployment's pod template (`.spec.template`) is changed. +4. **List Deployments:** This will retrieve Deployments in the `default` namespace and print their names and replica counts. -4. **Delete Deployment:** This will delete the Deployment object and its +5. **Delete Deployment:** This will delete the Deployment object and its dependent ReplicaSet resource. Verify with `kubectl get deployments`. Each step is separated by an interactive prompt. You must hit the @@ -55,6 +62,10 @@ Updating deployment... Updated deployment... -> Press Return key to continue. +Rolling back deployment... +Rolled back deployment... +-> Press Return key to continue. + Listing deployments in namespace "default": * demo-deployment (1 replicas) -> Press Return key to continue. diff --git a/staging/src/k8s.io/client-go/examples/create-update-delete-deployment/main.go b/staging/src/k8s.io/client-go/examples/create-update-delete-deployment/main.go index 722a39458cd..e026c7e926c 100644 --- a/staging/src/k8s.io/client-go/examples/create-update-delete-deployment/main.go +++ b/staging/src/k8s.io/client-go/examples/create-update-delete-deployment/main.go @@ -22,6 +22,7 @@ import ( "flag" "fmt" "os" + "path/filepath" appsv1beta1 "k8s.io/api/apps/v1beta1" apiv1 "k8s.io/api/core/v1" @@ -34,11 +35,14 @@ import ( ) func main() { - kubeconfig := flag.String("kubeconfig", "", "absolute path to the kubeconfig file") - flag.Parse() - if *kubeconfig == "" { - panic("-kubeconfig not specified") + var kubeconfig *string + if home := homeDir(); home != "" { + kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file") + } else { + kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file") } + flag.Parse() + config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig) if err != nil { panic(err) @@ -61,6 +65,9 @@ func main() { Labels: map[string]string{ "app": "demo", }, + Annotations: map[string]string{ + "fizz": "buzz", + }, }, Spec: apiv1.PodSpec{ Containers: []apiv1.Container{ @@ -100,24 +107,30 @@ func main() { // 2. Modify the "result" returned by Create()/Get() and retry Update(result) until // you no longer get a conflict error. This way, you can preserve changes made // by other clients between Create() and Update(). This is implemented below: + // + // See the API Conventions: + // https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#concurrency-control-and-consistency for { + // Retrieve latest version of Deployment before modifying and updating + result, err = deploymentsClient.Get("demo-deployment", metav1.GetOptions{}) + if err != nil { + panic(fmt.Errorf("Get failed: %+v", err)) + } result.Spec.Replicas = int32Ptr(1) // reduce replica count result.Spec.Template.Annotations = map[string]string{ // add annotations "foo": "bar", } - if _, err := deploymentsClient.Update(result); errors.IsConflict(err) { - // Deployment is modified in the meanwhile, query the latest version - // and modify the retrieved object. - fmt.Println("encountered conflict, retrying") - result, err = deploymentsClient.Get("demo-deployment", metav1.GetOptions{}) - if err != nil { - panic(fmt.Errorf("Get failed: %+v", err)) + if _, err = deploymentsClient.Update(result); err != nil { + if errors.IsConflict(err) { + // Deployment was modified since last retrieved, need to retry + fmt.Println("Conflicting resource versions, retrying") + } else { + panic(err) } - } else if err != nil { - panic(err) } else { + fmt.Println("Updated deployment...") break } @@ -125,7 +138,32 @@ func main() { // exhausting the apiserver, and add a limit/timeout on the retries to // avoid getting stuck in this loop indefintiely. } - fmt.Println("Updated deployment...") + + // Rollback Deployment + prompt() + fmt.Println("Rolling back deployment...") + + // Use same method as above to avoid version conflicts + for { + result, err = deploymentsClient.Get("demo-deployment", metav1.GetOptions{}) + if err != nil { + panic(fmt.Errorf("Get failed: %+v", err)) + } + result.Spec.RollbackTo = &appsv1beta1.RollbackConfig{ + Revision: 0, // Can be specific revision number or 0 for last revision + } + + if _, err = deploymentsClient.Update(result); err != nil { + if errors.IsConflict(err) { + fmt.Println("Conflicting resource versions, retrying") + } else { + panic(err) + } + } else { + fmt.Println("Rolled back deployment...") + break + } + } // List Deployments prompt() @@ -163,3 +201,10 @@ func prompt() { } func int32Ptr(i int32) *int32 { return &i } + +func homeDir() string { + if h := os.Getenv("HOME"); h != "" { + return h + } + return os.Getenv("USERPROFILE") // windows +}