diff --git a/hack/.linted_packages b/hack/.linted_packages index 6f787605487..06151acf41f 100644 --- a/hack/.linted_packages +++ b/hack/.linted_packages @@ -364,6 +364,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 diff --git a/staging/src/k8s.io/client-go/examples/create-update-delete-deployment/BUILD b/staging/src/k8s.io/client-go/examples/create-update-delete-deployment/BUILD new file mode 100644 index 00000000000..8179d0cf146 --- /dev/null +++ b/staging/src/k8s.io/client-go/examples/create-update-delete-deployment/BUILD @@ -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", + ], +) 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 new file mode 100644 index 00000000000..6b7ed4eac98 --- /dev/null +++ b/staging/src/k8s.io/client-go/examples/create-update-delete-deployment/README.md @@ -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 +Return 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/ 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 new file mode 100644 index 00000000000..2913510f2e3 --- /dev/null +++ b/staging/src/k8s.io/client-go/examples/create-update-delete-deployment/main.go @@ -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 }