Merge pull request #29 from wfarr/thirdpartyresources-examples

Thirdpartyresources examples
This commit is contained in:
Chao Xu 2016-11-10 21:45:48 -08:00 committed by GitHub
commit febd848390
3 changed files with 275 additions and 0 deletions

View File

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

View File

@ -0,0 +1,155 @@
package main
import (
"flag"
"fmt"
"k8s.io/client-go/1.5/kubernetes"
"k8s.io/client-go/1.5/pkg/api"
"k8s.io/client-go/1.5/pkg/api/errors"
"k8s.io/client-go/1.5/pkg/api/unversioned"
"k8s.io/client-go/1.5/pkg/api/v1"
"k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1"
"k8s.io/client-go/1.5/pkg/runtime"
"k8s.io/client-go/1.5/pkg/runtime/serializer"
"k8s.io/client-go/1.5/rest"
"k8s.io/client-go/1.5/tools/clientcmd"
// Only required to authenticate against GKE clusters
_ "k8s.io/client-go/1.5/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")
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 := unversioned.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)
}

View File

@ -0,0 +1,77 @@
package main
import (
"encoding/json"
"k8s.io/client-go/1.5/pkg/api"
"k8s.io/client-go/1.5/pkg/api/meta"
"k8s.io/client-go/1.5/pkg/api/unversioned"
)
type ExampleSpec struct {
Foo string `json:"foo"`
Bar bool `json:"bar"`
}
type Example struct {
unversioned.TypeMeta `json:",inline"`
Metadata api.ObjectMeta `json:"metadata"`
Spec ExampleSpec `json:"spec"`
}
type ExampleList struct {
unversioned.TypeMeta `json:",inline"`
Metadata unversioned.ListMeta `json:"metadata"`
Items []Example `json:"items"`
}
// Required to satisfy Object interface
func (e *Example) GetObjectKind() unversioned.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() unversioned.ObjectKind {
return &el.TypeMeta
}
// Required to satisfy ListMetaAccessor interface
func (el *ExampleList) GetListMeta() unversioned.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
}