mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
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>
This commit is contained in:
parent
8d3a19229f
commit
ce73088a71
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user