Skip to content

Commit

Permalink
client-go: Add CRUD Deployment sample
Browse files Browse the repository at this point in the history
This sample adds sample code for create/list/replace/delete operations for
Deployment resource for client-go library. This patch also contains
documentation explaining what the example application does and how it can be
executed.

This patch is part of the body of work improving the client library samples.

Signed-off-by: Ahmet Alp Balkan <[email protected]>
  • Loading branch information
ahmetb committed May 30, 2017
1 parent c717a0e commit d75710f
Show file tree
Hide file tree
Showing 4 changed files with 276 additions and 0 deletions.
1 change: 1 addition & 0 deletions hack/.linted_packages
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ staging/src/k8s.io/apiserver/plugin/pkg/authenticator/password
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/password/allow
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/request/basicauth
staging/src/k8s.io/client-go/discovery
staging/src/k8s.io/client-go/examples/create-update-delete-deployment
staging/src/k8s.io/client-go/examples/in-cluster
staging/src/k8s.io/client-go/examples/out-of-cluster
staging/src/k8s.io/client-go/examples/third-party-resources
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package(default_visibility = ["//visibility:public"])

licenses(["notice"])

load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)

go_binary(
name = "create-update-delete-deployment",
library = ":go_default_library",
tags = ["automanaged"],
)

go_library(
name = "go_default_library",
srcs = ["main.go"],
tags = ["automanaged"],
deps = [
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/pkg/api/v1:go_default_library",
"//vendor/k8s.io/client-go/pkg/apis/apps/v1beta1:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Create, Update & Delete Deployment

This example program demonstrates the fundamental operations for managing on
[Deployment][1] resources, such as `Create`, `List`, `Update` and `Delete`.

You can adopt the source code from this example to write programs that manage
other types of resources through the Kubernetes API.

## Running this example

Make sure you have a Kubernetes cluster and `kubectl` is configured:

kubectl get nodes

Compile this example on your workstation:

```
cd create-update-delete-deployment
go build -o ./app
```

Now, run this application on your workstation with your local kubeconfig file:

```
./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`.
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
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`
namespace and print their names and replica counts.
4. **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
<kbd>Return</kbd> key to proceeed to the next step. You can use these prompts as
a break to take time to run `kubectl` and inspect the result of the operations
executed.

You should see an output like the following:

```
Creating deployment...
Created deployment "demo-deployment".
-> Press Return key to continue.
Updating deployment...
Updated deployment...
-> Press Return key to continue.
Listing deployments in namespace "default":
* demo-deployment (1 replicas)
-> Press Return key to continue.
Deleting deployment...
Deleted deployment.
```

## Cleanup

Successfully running this program will clean the created artifacts. If you
terminate the program without completing, you can clean up the created
deployment with:

kubectl delete deploy demo-deployment

## Troubleshooting

If you are getting the following error, make sure Kubernetes version of your
cluster is v1.6 or above in `kubectl version`:

panic: the server could not find the requested resource

[1]: https://kubernetes.io/docs/user-guide/deployments/
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Note: the example only works with the code within the same release/branch.
package main

import (
"bufio"
"flag"
"fmt"
"os"

"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
apiv1 "k8s.io/client-go/pkg/api/v1"
appsv1beta1 "k8s.io/client-go/pkg/apis/apps/v1beta1"
"k8s.io/client-go/tools/clientcmd"
// Uncomment the following line to load the gcp plugin (only required to authenticate against GKE clusters).
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
)

func main() {
kubeconfig := flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
flag.Parse()
if *kubeconfig == "" {
panic("-kubeconfig not specified")
}
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
panic(err)
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err)
}

deploymentsClient := clientset.AppsV1beta1().Deployments(apiv1.NamespaceDefault)

deployment := &appsv1beta1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "demo-deployment",
},
Spec: appsv1beta1.DeploymentSpec{
Replicas: int32Ptr(2),
Template: apiv1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "demo",
},
},
Spec: apiv1.PodSpec{
Containers: []apiv1.Container{
{
Name: "web",
Image: "nginx:1.13",
Ports: []apiv1.ContainerPort{
{
Name: "http",
Protocol: apiv1.ProtocolTCP,
ContainerPort: 80,
},
},
},
},
},
},
},
}

// Create Deployment
fmt.Println("Creating deployment...")
result, err := deploymentsClient.Create(deployment)
if err != nil {
panic(err)
}
fmt.Printf("Created deployment %q.\n", result.GetObjectMeta().GetName())

// Update Deployment
prompt()
fmt.Println("Updating deployment...")
// You have two options to Update() this Deployment:
//
// 1. Modify the "deployment" variable and call: Update(deployment).
// This works like the "kubectl replace" command and it overwrites/loses changes
// made by other clients between you Create() and Update() the object.
// 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:

for {
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))
}
} else if err != nil {
panic(err)
} else {
break
}

// TODO: You should sleep here with an exponential backoff to avoid
// exhausting the apiserver, and add a limit/timeout on the retries to
// avoid getting stuck in this loop indefintiely.
}
fmt.Println("Updated deployment...")

// List Deployments
prompt()
fmt.Printf("Listing deployments in namespace %q:\n", apiv1.NamespaceDefault)
list, err := deploymentsClient.List(metav1.ListOptions{})
if err != nil {
panic(err)
}
for _, d := range list.Items {
fmt.Printf(" * %s (%d replicas)\n", d.Name, *d.Spec.Replicas)
}

// Delete Deployment
prompt()
fmt.Println("Deleting deployment...")
deletePolicy := metav1.DeletePropagationForeground
if err := deploymentsClient.Delete("demo-deployment", &metav1.DeleteOptions{
PropagationPolicy: &deletePolicy,
}); err != nil {
panic(err)
}
fmt.Println("Deleted deployment.")
}

func prompt() {
fmt.Printf("-> Press Return key to continue.")
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
break
}
if err := scanner.Err(); err != nil {
panic(err)
}
fmt.Println()
}

func int32Ptr(i int32) *int32 { return &i }

0 comments on commit d75710f

Please sign in to comment.