Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bin
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
test:
go test -v `go list ./... | grep -v 'vendor'`

build:
go build -o ./bin/controller ./cmd/controller

codegen:
./hack/update-codegen.sh
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,23 @@
# crd-sample
kubernetes CRD pattern tutorial and sample

## tutorial

TODO

### Run code generator

```
make codegen
```

### Run unit test

```
make test
```

### Build image

```
make build
```
60 changes: 60 additions & 0 deletions cmd/controller/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package main

import (
"flag"
"time"

"github.com/caicloud/crd-sample/pkg/client/clientset"
informers "github.com/caicloud/crd-sample/pkg/client/informers"
"github.com/caicloud/crd-sample/pkg/controller/labels"
"github.com/golang/glog"
kubeinformers "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)

var (
masterURL string
kubeconfig string
)

func main() {
flag.Parse()

stopCh := make(chan struct{})

cfg, err := clientcmd.BuildConfigFromFlags(masterURL, kubeconfig)
if err != nil {
glog.Fatalf("Error building kubeconfig: %s", err.Error())
}

kubeClient, err := kubernetes.NewForConfig(cfg)
if err != nil {
glog.Fatalf("Error building kubernetes clientset: %s", err.Error())
}

labelsClient, err := clientset.NewForConfig(cfg)
if err != nil {
glog.Fatalf("Error building example clientset: %s", err.Error())
}

kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, time.Second*30)
labelsInformerFactory := informers.NewSharedInformerFactory(labelsClient, time.Second*30)

controller := labels.NewLabelCounterController(&labels.LabelCounterControllerOptions{
KubeClient: kubeClient,
LabelsClient: labelsClient,
LabelCounterInformer: labelsInformerFactory.Labels().V1alpha1().LabelCounters(),
NodeInformer: kubeInformerFactory.Core().V1().Nodes(),
})

go kubeInformerFactory.Start(stopCh)
go labelsInformerFactory.Start(stopCh)

controller.Run(2, stopCh)
}

func init() {
flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
flag.StringVar(&masterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
}
83 changes: 83 additions & 0 deletions hack/update-codegen.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/usr/bin/env bash

set -o errexit
set -o nounset
set -o pipefail

SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")/..
CODEGEN_PKG=${CODEGEN_PKG:-$(cd ${SCRIPT_ROOT}; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo k8s.io/code-generator)}
ROOT_PATH=${ROOT_PKG:-$(cd ${SCRIPT_ROOT}; pwd -P)}

OUTPUT_DIR=${SCRIPT_ROOT}/_output
mkdir ${OUTPUT_DIR}

# Register function to be called on EXIT to remove generated binary.
function cleanup {
rm -rf "${OUTPUT_DIR:-}"
}
trap cleanup EXIT

EXT_APIS_PKG=${ROOT_PATH#"$GOPATH/src/"}/pkg/apis
OUTPUT_PKG=${ROOT_PATH#"$GOPATH/src/"}/pkg/client
# apps:v1,v2 othergroup:v1alpha1,v1alpha2
GROUP_VERSIONS="labels:v1alpha1"

function join() { local IFS="$1"; shift; echo "$*"; }

EXT_APIS=()

for GVs in ${GROUP_VERSIONS}; do
IFS=: read G Vs <<<"${GVs}"
# enumerate versions
for V in ${Vs//,/ }; do
EXT_APIS+=("${EXT_APIS_PKG}/${G}/${V}")
done
done

echo "Building deepcopy-gen"
DEEPCOPY_GEN="${OUTPUT_DIR}/deepcopy-gen"
go build -o "${DEEPCOPY_GEN}" ${CODEGEN_PKG}/cmd/deepcopy-gen

echo "Generating deepcopy funcs for ${GROUP_VERSIONS}"
${DEEPCOPY_GEN} --input-dirs $(join , "${EXT_APIS[@]}") -O zz_generated.deepcopy --bounding-dirs ${EXT_APIS_PKG}



echo "Building defaulter-gen"
DEFAULTER_GEN="${OUTPUT_DIR}/defaulter-gen"
go build -o "${DEFAULTER_GEN}" ${CODEGEN_PKG}/cmd/defaulter-gen

echo "Generating defaulters for ${GROUP_VERSIONS}"
${DEFAULTER_GEN} --input-dirs $(join , "${EXT_APIS[@]}") -O zz_generated.defaults



echo "Building client-gen"
CLIENT_GEN="${OUTPUT_DIR}/client-gen"
go build -o "${CLIENT_GEN}" ${CODEGEN_PKG}/cmd/client-gen

echo "Generating clientset for ${GROUP_VERSIONS} at ${OUTPUT_PKG}/clientset"
${CLIENT_GEN} --clientset-name clientset --input-base "" --input $(join , "${EXT_APIS[@]}") --output-package ${OUTPUT_PKG}



echo "Building lister-gen"
LISTER_GEN="${OUTPUT_DIR}/lister-gen"
go build -o "${LISTER_GEN}" ${CODEGEN_PKG}/cmd/lister-gen

echo "Generating listers for ${GROUP_VERSIONS} at ${OUTPUT_PKG}/listers"
${LISTER_GEN} --input-dirs $(join , "${EXT_APIS[@]}") --output-package ${OUTPUT_PKG}/listers



echo "Building informer-gen"
INFORMER_GEN="${OUTPUT_DIR}/informer-gen"
go build -o "${INFORMER_GEN}" ${CODEGEN_PKG}/cmd/informer-gen

echo "Generating informers for ${GROUP_VERSIONS} at ${OUTPUT_PKG}/informers"
${INFORMER_GEN} \
--input-dirs $(join , "${EXT_APIS[@]}") \
--versioned-clientset-package ${OUTPUT_PKG}/clientset \
--single-directory \
--listers-package ${OUTPUT_PKG}/listers \
--output-package ${OUTPUT_PKG}/informers
29 changes: 29 additions & 0 deletions pkg/apis/labels/install/install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package install

import (
"github.com/caicloud/crd-sample/pkg/apis/labels/v1alpha1"
"k8s.io/apimachinery/pkg/apimachinery/announced"
"k8s.io/apimachinery/pkg/apimachinery/registered"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
)

// GroupName defines name of the group in this package
const GroupName = "labels.caicloud.io"

// Install registers the API group and adds types to a scheme
func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *registered.APIRegistrationManager, scheme *runtime.Scheme) {
if err := announced.NewGroupMetaFactory(
&announced.GroupMetaFactoryArgs{
GroupName: GroupName,
// RootScopedKinds are resources that are not namespaced
RootScopedKinds: sets.NewString("LabelCounter", "LabelCounterList"),
VersionPreferenceOrder: []string{v1alpha1.SchemeGroupVersion.Version},
},
announced.VersionToSchemeFunc{
v1alpha1.SchemeGroupVersion.Version: v1alpha1.AddToScheme,
},
).Announce(groupFactoryRegistry).RegisterAndEnable(registry, scheme); err != nil {
panic(err)
}
}
6 changes: 6 additions & 0 deletions pkg/apis/labels/v1alpha1/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// +k8s:deepcopy-gen=package
// +k8s:defaulter-gen=TypeMeta

// Package v1alpha1 represents labels v1alpha1 API
// +groupName=labels.caicloud.io
package v1alpha1 // import "github.com/caicloud/crd-sample/pkg/apis/labels/v1alpha1"
43 changes: 43 additions & 0 deletions pkg/apis/labels/v1alpha1/register.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)

// GroupName defines the name of group used in this package
const GroupName = "labels.caicloud.io"

// GroupVersion defines the version of group used in this package
const GroupVersion = "v1alpha1"

// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: GroupVersion}

// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}

// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}

var (
// SchemeBuilder represents a builder to create scheme
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
// AddToScheme represents a function to add types to scheme
AddToScheme = SchemeBuilder.AddToScheme
)

// Adds the list of known types to Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&LabelCounter{},
&LabelCounterList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
69 changes: 69 additions & 0 deletions pkg/apis/labels/v1alpha1/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
// LabelPrefix defines the format of label which is affected by this resource
// Label format "labels.caicloud.io/xxx: yyy"
LabelPrefix = "labels.caicloud.io/"

// LabelFinalizer defines finalizer of the label
LabelFinalizer = "labels.caicloud.io"

// LabelAnnotationKey defines key of the annotation
// Label name refers will be stored in the annotation
// with json array format
LabelAnnotationKey = "labels.caicloud.io"
)

// +genclient
// +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// LabelCounter represents a labelset of resource.
type LabelCounter struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`

// Spec defines the desired identities of label
// +optional
Spec LabelCounterSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
// Status defines the current status of label
// +optional
Status LabelCounterStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=spec"`
}

// LabelCounterSpec represents label attributes
type LabelCounterSpec struct {
// Values defines counted label values
Values []string `json:"values" protobuf:"bytes,1,cap,name=values"`
}

// LabelCounterStatus represents label status
type LabelCounterStatus struct {
Counters []Counter `json:"counters" protobuf:"bytes,1,cap,name=counters"`
}

// Counter defines the resource number
type Counter struct {
// Value defines the value of the label
Value string `json:"value" protobuf:"bytes,1,req,name=value"`

// Count defines count of the resources
Count int `json:"count" protobuf:"bytes,2,req,name=count"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// LabelCounterList is a collection of labels
type LabelCounterList struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`

// Items defines an array of application
Items []LabelCounter `json:"items" protobuf:"bytes,2,rep,name=items"`
}
Loading