From 7aebe7c0791897cca30e4cf2faa31d42659b9bdb Mon Sep 17 00:00:00 2001 From: "Dr. Stefan Schimanski" Date: Tue, 31 Jan 2017 09:02:21 +0100 Subject: [PATCH] client-go: initial examples import --- .../client-go/examples/in-cluster/main.go | 47 ++++++ .../client-go/examples/out-of-cluster/main.go | 53 ++++++ .../examples/third-party-resources/README.md | 43 +++++ .../examples/third-party-resources/main.go | 156 ++++++++++++++++++ .../examples/third-party-resources/types.go | 78 +++++++++ 5 files changed, 377 insertions(+) create mode 100644 staging/src/k8s.io/client-go/examples/in-cluster/main.go create mode 100644 staging/src/k8s.io/client-go/examples/out-of-cluster/main.go create mode 100644 staging/src/k8s.io/client-go/examples/third-party-resources/README.md create mode 100644 staging/src/k8s.io/client-go/examples/third-party-resources/main.go create mode 100644 staging/src/k8s.io/client-go/examples/third-party-resources/types.go diff --git a/staging/src/k8s.io/client-go/examples/in-cluster/main.go b/staging/src/k8s.io/client-go/examples/in-cluster/main.go new file mode 100644 index 00000000000..04b0798e40a --- /dev/null +++ b/staging/src/k8s.io/client-go/examples/in-cluster/main.go @@ -0,0 +1,47 @@ +/* +Copyright 2016 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. +*/ + +package main + +import ( + "fmt" + "time" + + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/pkg/api/v1" + "k8s.io/client-go/rest" +) + +func main() { + // creates the in-cluster config + config, err := rest.InClusterConfig() + if err != nil { + panic(err.Error()) + } + // creates the clientset + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + panic(err.Error()) + } + for { + pods, err := clientset.Core().Pods("").List(v1.ListOptions{}) + if err != nil { + panic(err.Error()) + } + fmt.Printf("There are %d pods in the cluster\n", len(pods.Items)) + time.Sleep(10 * time.Second) + } +} diff --git a/staging/src/k8s.io/client-go/examples/out-of-cluster/main.go b/staging/src/k8s.io/client-go/examples/out-of-cluster/main.go new file mode 100644 index 00000000000..7919d9278e1 --- /dev/null +++ b/staging/src/k8s.io/client-go/examples/out-of-cluster/main.go @@ -0,0 +1,53 @@ +/* +Copyright 2016 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. +*/ + +package main + +import ( + "flag" + "fmt" + "time" + + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/pkg/api/v1" + "k8s.io/client-go/tools/clientcmd" +) + +var ( + kubeconfig = flag.String("kubeconfig", "./config", "absolute path to the kubeconfig file") +) + +func main() { + flag.Parse() + // uses the current context in kubeconfig + config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig) + if err != nil { + panic(err.Error()) + } + // creates the clientset + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + panic(err.Error()) + } + for { + pods, err := clientset.Core().Pods("").List(v1.ListOptions{}) + if err != nil { + panic(err.Error()) + } + fmt.Printf("There are %d pods in the cluster\n", len(pods.Items)) + time.Sleep(10 * time.Second) + } +} diff --git a/staging/src/k8s.io/client-go/examples/third-party-resources/README.md b/staging/src/k8s.io/client-go/examples/third-party-resources/README.md new file mode 100644 index 00000000000..68c25a0c910 --- /dev/null +++ b/staging/src/k8s.io/client-go/examples/third-party-resources/README.md @@ -0,0 +1,43 @@ +# Third Party Resources Example + +This particular example demonstrates how to perform basic operations such as: + +* How to register a new ThirdPartyResource (custom Resource type) +* How to create/get/list instances of your new Resource type (update/delete/etc work as well but are not demonstrated) + +## Running + +``` +# assumes you have a working kubeconfig, not required if operating in-cluster +go run *.go -kubeconfig=$HOME/.kube/config +``` + +## Use Cases + +ThirdPartyResources can be used to implement custom Resource types for your Kubernetes cluster. +These act like most other Resources in Kubernetes, and may be `kubectl apply`'d, etc. + +Some example use cases: + +* Provisioning/Management of external datastores/databases (eg. CloudSQL/RDS instances) +* Higher level abstractions around Kubernetes primitives (eg. a single Resource to define an etcd cluster, backed by a Service and a ReplicationController) + +## Defining types + +Each instance of your ThirdPartyResource has an attached Spec, which should be defined via a `struct{}` to provide data format validation. +In practice, this Spec is arbitrary key-value data that specifies the configuration/behavior of your Resource. + +For example, if you were implementing a ThirdPartyResource for a Database, you might provide a DatabaseSpec like the following: + +``` go +type DatabaseSpec struct { + Databases []string `json:"databases"` + Users []User `json:"users"` + Version string `json:"version"` +} + +type User struct { + Name string `json:"name"` + Password string `json:"password"` +} +``` \ No newline at end of file diff --git a/staging/src/k8s.io/client-go/examples/third-party-resources/main.go b/staging/src/k8s.io/client-go/examples/third-party-resources/main.go new file mode 100644 index 00000000000..6222dd68f95 --- /dev/null +++ b/staging/src/k8s.io/client-go/examples/third-party-resources/main.go @@ -0,0 +1,156 @@ +package main + +import ( + "flag" + "fmt" + + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/pkg/api" + "k8s.io/client-go/pkg/api/errors" + "k8s.io/client-go/pkg/api/v1" + "k8s.io/client-go/pkg/apis/extensions/v1beta1" + metav1 "k8s.io/client-go/pkg/apis/meta/v1" + "k8s.io/client-go/pkg/runtime" + "k8s.io/client-go/pkg/runtime/schema" + "k8s.io/client-go/pkg/runtime/serializer" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + + // Only required to authenticate against GKE clusters + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" +) + +var ( + config *rest.Config +) + +func main() { + kubeconfig := flag.String("kubeconfig", "", "Path to a kube config. Only required if out-of-cluster.") + flag.Parse() + + // Create the client config. Use kubeconfig if given, otherwise assume in-cluster. + config, err := buildConfig(*kubeconfig) + if err != nil { + panic(err) + } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + panic(err) + } + + // initialize third party resource if it does not exist + tpr, err := clientset.Extensions().ThirdPartyResources().Get("example.k8s.io", metav1.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + tpr := &v1beta1.ThirdPartyResource{ + ObjectMeta: v1.ObjectMeta{ + Name: "example.k8s.io", + }, + Versions: []v1beta1.APIVersion{ + {Name: "v1"}, + }, + Description: "An Example ThirdPartyResource", + } + + result, err := clientset.Extensions().ThirdPartyResources().Create(tpr) + if err != nil { + panic(err) + } + fmt.Printf("CREATED: %#v\nFROM: %#v\n", result, tpr) + } else { + panic(err) + } + } else { + fmt.Printf("SKIPPING: already exists %#v\n", tpr) + } + + // make a new config for our extension's API group, using the first config as a baseline + var tprconfig *rest.Config + tprconfig = config + configureClient(tprconfig) + + tprclient, err := rest.RESTClientFor(tprconfig) + if err != nil { + panic(err) + } + + var example Example + + err = tprclient.Get(). + Resource("examples"). + Namespace(api.NamespaceDefault). + Name("example1"). + Do().Into(&example) + + if err != nil { + if errors.IsNotFound(err) { + // Create an instance of our TPR + example := &Example{ + Metadata: api.ObjectMeta{ + Name: "example1", + }, + Spec: ExampleSpec{ + Foo: "hello", + Bar: true, + }, + } + + var result Example + err = tprclient.Post(). + Resource("examples"). + Namespace(api.NamespaceDefault). + Body(example). + Do().Into(&result) + + if err != nil { + panic(err) + } + fmt.Printf("CREATED: %#v\n", result) + } else { + panic(err) + } + } else { + fmt.Printf("GET: %#v\n", example) + } + + // Fetch a list of our TPRs + exampleList := ExampleList{} + err = tprclient.Get().Resource("examples").Do().Into(&exampleList) + if err != nil { + panic(err) + } + fmt.Printf("LIST: %#v\n", exampleList) +} + +func buildConfig(kubeconfig string) (*rest.Config, error) { + if kubeconfig != "" { + return clientcmd.BuildConfigFromFlags("", kubeconfig) + } + return rest.InClusterConfig() +} + +func configureClient(config *rest.Config) { + groupversion := schema.GroupVersion{ + Group: "k8s.io", + Version: "v1", + } + + config.GroupVersion = &groupversion + config.APIPath = "/apis" + config.ContentType = runtime.ContentTypeJSON + config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: api.Codecs} + + schemeBuilder := runtime.NewSchemeBuilder( + func(scheme *runtime.Scheme) error { + scheme.AddKnownTypes( + groupversion, + &Example{}, + &ExampleList{}, + &api.ListOptions{}, + &api.DeleteOptions{}, + ) + return nil + }) + schemeBuilder.AddToScheme(api.Scheme) +} diff --git a/staging/src/k8s.io/client-go/examples/third-party-resources/types.go b/staging/src/k8s.io/client-go/examples/third-party-resources/types.go new file mode 100644 index 00000000000..f4e75b677ca --- /dev/null +++ b/staging/src/k8s.io/client-go/examples/third-party-resources/types.go @@ -0,0 +1,78 @@ +package main + +import ( + "encoding/json" + + "k8s.io/client-go/pkg/api" + "k8s.io/client-go/pkg/api/meta" + metav1 "k8s.io/client-go/pkg/apis/meta/v1" + "k8s.io/client-go/pkg/runtime/schema" +) + +type ExampleSpec struct { + Foo string `json:"foo"` + Bar bool `json:"bar"` +} + +type Example struct { + metav1.TypeMeta `json:",inline"` + Metadata api.ObjectMeta `json:"metadata"` + + Spec ExampleSpec `json:"spec"` +} + +type ExampleList struct { + metav1.TypeMeta `json:",inline"` + Metadata metav1.ListMeta `json:"metadata"` + + Items []Example `json:"items"` +} + +// Required to satisfy Object interface +func (e *Example) GetObjectKind() schema.ObjectKind { + return &e.TypeMeta +} + +// Required to satisfy ObjectMetaAccessor interface +func (e *Example) GetObjectMeta() meta.Object { + return &e.Metadata +} + +// Required to satisfy Object interface +func (el *ExampleList) GetObjectKind() schema.ObjectKind { + return &el.TypeMeta +} + +// Required to satisfy ListMetaAccessor interface +func (el *ExampleList) GetListMeta() metav1.List { + return &el.Metadata +} + +// The code below is used only to work around a known problem with third-party +// resources and ugorji. If/when these issues are resolved, the code below +// should no longer be required. + +type ExampleListCopy ExampleList +type ExampleCopy Example + +func (e *Example) UnmarshalJSON(data []byte) error { + tmp := ExampleCopy{} + err := json.Unmarshal(data, &tmp) + if err != nil { + return err + } + tmp2 := Example(tmp) + *e = tmp2 + return nil +} + +func (el *ExampleList) UnmarshalJSON(data []byte) error { + tmp := ExampleListCopy{} + err := json.Unmarshal(data, &tmp) + if err != nil { + return err + } + tmp2 := ExampleList(tmp) + *el = tmp2 + return nil +}