client-go: Update CRUD example

This updates the create-update-delete-deployment example with the following:
Add rollback step to demonstrate rolling back deployments with client-go.
Modify the for-loops used in both Update steps to Get() the latest version
of the Deployment from the server before attempting Update().
This is necessary because the object returned by Create() does
not have the new resourceVersion, causing the initial Update() to always fail
due to conflicting resource versions. Putting the Get() at the top of the
loop seems to fix this bug.
Make -kubeconfig flag optional if config is in default location, using the
same method found in the out-of-cluster example.

Patch is motivated by effort to improve client-go examples.

Signed-off-by: John Kelly <jekohk@gmail.com>

Kubernetes-commit: ce73088a718c30d8a3577f5d0521584b9c201e69
This commit is contained in:
John Kelly
2017-10-04 18:01:49 -04:00
committed by Kubernetes Publisher
parent accfc9b01e
commit 4e80b27156
2 changed files with 75 additions and 19 deletions

View File

@@ -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
}