Skip to content
This repository has been archived by the owner on Nov 19, 2020. It is now read-only.

Commit

Permalink
Merge pull request #73 from ericchiang/rewrite
Browse files Browse the repository at this point in the history
*: refactor generation and add type registration
  • Loading branch information
ericchiang committed Jan 19, 2018
2 parents 3800acd + 7f6e875 commit 72a6e54
Show file tree
Hide file tree
Showing 103 changed files with 71,342 additions and 30,456 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
assets
_output/
16 changes: 13 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
language: go

sudo: required

go:
- 1.7.5
- 1.8
- 1.9

services:
- docker

env:
# Maybe run minikube later?
- K8S_CLIENT_TEST=0
- K8S_CLIENT_TEST=1 KUBECONFIG=scripts/kubeconfig

install:
- go get -v ./...
- go get -v github.com/ghodss/yaml # Required for examples.
- ./scripts/run-kube.sh
- curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.9.1/bin/linux/amd64/kubectl
- chmod +x kubectl
- mv kubectl $GOPATH/bin

script:
- make
- make test
- make test-examples
- make verify-generate


notifications:
Expand Down
45 changes: 45 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,52 @@
KUBE_VERSION=1.9.1

build:
go build -v ./...

test:
go test -v ./...

test-examples:
@for example in $(shell find examples/ -name '*.go'); do \
go build -v $$example || exit 1; \
done

.PHONY: generate
generate: _output/kubernetes _output/bin/protoc _output/bin/gomvpkg _output/bin/protoc-gen-gofast _output/src/github.com/golang/protobuf
./scripts/generate.sh
go run scripts/register.go
cp scripts/time.go.partial apis/meta/v1/time.go

.PHONY: verify-generate
verify-generate: generate
./scripts/git-diff.sh

_output/bin/protoc-gen-gofast:
./scripts/go-install.sh \
https://github.com/gogo/protobuf \
github.com/gogo/protobuf \
github.com/gogo/protobuf/protoc-gen-gofast \
tags/v0.5

_output/bin/gomvpkg:
./scripts/go-install.sh \
https://github.com/golang/tools \
golang.org/x/tools \
golang.org/x/tools/cmd/gomvpkg \
fbec762f837dc349b73d1eaa820552e2ad177942

_output/src/github.com/golang/protobuf:
git clone https://github.com/golang/protobuf _output/src/github.com/golang/protobuf

_output/bin/protoc:
./scripts/get-protoc.sh

_output/kubernetes:
mkdir -p _output
curl -o _output/kubernetes.zip -L https://github.com/kubernetes/kubernetes/archive/v$(KUBE_VERSION).zip
unzip _output/kubernetes.zip -d _output > /dev/null
mv _output/kubernetes-$(KUBE_VERSION) _output/kubernetes

.PHONY: clean
clean:
rm -rf _output
171 changes: 135 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"log"

"github.com/ericchiang/k8s"
corev1 "github.com/ericchiang/k8s/apis/core/v1"
)

func main() {
Expand All @@ -21,8 +22,8 @@ func main() {
log.Fatal(err)
}

nodes, err := client.CoreV1().ListNodes(context.Background())
if err != nil {
var nodes corev1.NodeList
if err := client.List(context.Background(), "", &nodes); err != nil {
log.Fatal(err)
}
for _, node := range nodes.Items {
Expand All @@ -33,9 +34,9 @@ func main() {

## Should I use this or client-go?

client-go is a framework for building production ready controllers, components that regularly watch API resources and push the system towards a desired state. If you're writing a program that watches several resources in a loop for long durations, client-go's informers framework is a battle tested solution which will scale with the size of the cluster.
client-go is a framework for building production ready controllers, components that regularly watch API resources and push the system towards a desired state. If you're writing a program that watches several resources in a loop for long durations, client-go's informers framework is a battle tested solution which will scale with the size of the cluster.

This client should be used by programs that just need to talk to the Kubernetes API without prescriptive solutions for caching, reconciliation on failures, or work queues. This often includes components are relatively Kubernetes agnostic, but use the Kubernetes API for small tasks when running in Kubernetes. For example, performing leader election or persisting small amounts of state in annotations or configmaps.
This client should be used by programs that just need to talk to the Kubernetes API without prescriptive solutions for caching, reconciliation on failures, or work queues. This often includes components are relatively Kubernetes agnostic, but use the Kubernetes API for small tasks when running in Kubernetes. For example, performing leader election or persisting small amounts of state in annotations or configmaps.

TL;DR - Use client-go if you're writing a controller.

Expand All @@ -46,78 +47,173 @@ TL;DR - Use client-go if you're writing a controller.
* [github.com/golang/protobuf/proto][go-proto] (protobuf serialization)
* [golang.org/x/net/http2][go-http2] (HTTP/2 support)

## Versioned supported
## Usage

This client supports every API group version present since 1.3.
### Create, update, delete

## Usage
The type of the object passed to `Create`, `Update`, and `Delete` determine the resource being acted on.

```go
configMap := &corev1.ConfigMap{
Metadata: &metav1.ObjectMeta{
Name: k8s.String("my-configmap"),
Namespace: k8s.String("my-namespace"),
},
Data: map[string]string{"hello": "world"},
}

if err := client.Create(ctx, configMap); err != nil {
// handle error
}

configMap.Data["hello"] = "kubernetes"

if err := client.Update(ctx, configMap); err != nil {
// handle error
}

if err := client.Delete(ctx, configMap); err != nil {
// handle error
}
```

### Get, list, watch

Getting a resource requires providing a namespace (for namespaced objects) and a name.

### Namespace
```go
// Get the "cluster-info" configmap from the "kube-public" namespace
var configMap corev1.ConfigMap
err := client.Get(ctx, "kube-public", "cluster-info", &configMap)
```

When performing a list or watch operation, the namespace to list or watch in is provided as an argument.
When performing a list operation, the namespace to list or watch is also required.

```go
pods, err := core.ListPods(ctx, "custom-namespace") // Pods from the "custom-namespace"
// Pods from the "custom-namespace"
var pods corev1.PodList
err := client.List(ctx, "custom-namespace", &pods)
```

A special value `AllNamespaces` indicates that the list or watch should be performed on all cluster resources.

```go
pods, err := core.ListPods(ctx, k8s.AllNamespaces) // Pods in all namespaces.
// Pods in all namespaces
var pods corev1.PodList
err := client.List(ctx, k8s.AllNamespaces, &pods)
```

Both in-cluster and out-of-cluster clients are initialized with a primary namespace. This is the recommended value to use when listing or watching.
Watches require a example type to determine what resource they're watching. `Watch` returns an type which can be used to receive a stream of events. These events include resources of the same kind and the kind of the event (added, modified, deleted).

```go
client, err := k8s.NewInClusterClient()
// Watch configmaps in the "kube-system" namespace
var configMap corev1.ConfigMap
watcher, err := client.Watch(ctx, "kube-system", &configMap)
if err != nil {
// handle error
}
defer watcher.Close()

// List pods in the namespace the client is running in.
pods, err := client.CoreV1().ListPods(ctx, client.Namespace)
for {
cm := new(corev1.ConfigMap)
eventType, err := watcher.Next(cm)
if err != nil {
// watcher encountered and error, exit or create a new watcher
}
fmt.Println(eventType, *cm.Metadata.Name)
}
```

### Label selectors

Label selectors can be provided to any list operation.
Both in-cluster and out-of-cluster clients are initialized with a primary namespace. This is the recommended value to use when listing or watching.

```go
l := new(k8s.LabelSelector)
l.Eq("tier", "production")
l.In("app", "database", "frontend")
client, err := k8s.NewInClusterClient()
if err != nil {
// handle error
}

pods, err := client.CoreV1().ListPods(ctx, client.Namespace, l.Selector())
// List pods in the namespace the client is running in.
var pods corev1.PodList
err := client.List(ctx, client.Namespace, &pods)
```

### Working with resources
### Custom resources

Use the generated API types directly to create and modify resources.
Client operations support user defined resources, such as resources provided by [CustomResourceDefinitions][crds] and [aggregated API servers][custom-api-servers]. To use a custom resource, define an equivalent Go struct then register it with the `k8s` package. By default the client will use JSON serialization when encoding and decoding custom resources.

```go
import (
"context"

"github.com/ericchiang/k8s"
"github.com/ericchiang/k8s/api/v1"
metav1 "github.com/ericchiang/k8s/apis/meta/v1"
)

func createConfigMap(client *k8s.Client, name string, values map[string]string) error {
cm := &v1.ConfigMap{
type MyResource struct {
Metadata *metav1.ObjectMeta `json:"metadata"`
Foo string `json:"foo"`
Bar int `json:"bar"`
}

// Required for MyResource to implement k8s.Resource
func (m *MyResource) GetMetadata() *metav1.ObjectMeta {
return m.Metadata
}

type MyResourceList struct {
Metadata *metav1.ListMeta `json:"metadata"`
Items []MyResource `json:"items"`
}

// Require for MyResourceList to implement k8s.ResourceList
func (m *MyResourceList) GetMetadata() *metav1.ListMeta {
return m.Metadata
}

func init() {
// Register resources with the k8s package.
k8s.Register("resource.example.com", "v1", "myresources", true, &MyResource{})
k8s.RegisterList("resource.example.com", "v1", "myresources", true, &MyResourceList{})
}
```

Once registered, the library can use the custom resources like any other.

```
func do(ctx context.Context, client *k8s.Client, namespace string) error {
r := &MyResource{
Metadata: &metav1.ObjectMeta{
Name: &name,
Namespace: &client.Namespace,
Name: k8s.String("my-custom-resource"),
Namespace: &namespace,
},
Data: values,
Foo: "hello, world!",
Bar: 42,
}
// Will return the created configmap as well.
_, err := client.CoreV1().CreateConfigMap(context.TODO(), cm)
return err
if err := client.Create(ctx, r); err != nil {
return fmt.Errorf("create: %v", err)
}
r.Bar = -8
if err := client.Update(ctx, r); err != nil {
return fmt.Errorf("update: %v", err)
}
if err := client.Delete(ctx, r); err != nil {
return fmt.Errorf("delete: %v", err)
}
return nil
}
```

API structs use pointers to `int`, `bool`, and `string` types to differentiate between the zero value and an unsupplied one. This package provides [convenience methods][string] for creating pointers to literals of basic types.
If the custom type implements [`proto.Message`][proto-msg], the client will prefer protobuf when encoding and decoding the type.

### Label selectors

Label selectors can be provided to any list operation.

```go
l := new(k8s.LabelSelector)
l.Eq("tier", "production")
l.In("app", "database", "frontend")

pods, err := client.CoreV1().ListPods(ctx, client.Namespace, l.Selector())
```

### Creating out-of-cluster clients

Expand Down Expand Up @@ -188,3 +284,6 @@ func createConfigMap(client *k8s.Client, name string, values map[string]string)
[k8s-error]: https://godoc.org/github.com/ericchiang/k8s#APIError
[config]: https://godoc.org/github.com/ericchiang/k8s#Config
[string]: https://godoc.org/github.com/ericchiang/k8s#String
[crds]: https://kubernetes.io/docs/tasks/access-kubernetes-api/extend-api-custom-resource-definitions/
[custom-api-servers]: https://kubernetes.io/docs/concepts/api-extension/apiserver-aggregation/
[proto-msg]: https://godoc.org/github.com/golang/protobuf/proto#Message
Loading

0 comments on commit 72a6e54

Please sign in to comment.