Skip to content

Commit 5264408

Browse files
author
Mengqi Yu
committed
add an example for CRD
1 parent 5f14019 commit 5264408

File tree

8 files changed

+496
-0
lines changed

8 files changed

+496
-0
lines changed

crdexample/Dockerfile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Build the manager binary
2+
FROM golang:1.11.5 as builder
3+
4+
# Copy in the go src
5+
WORKDIR /go/src/sigs.k8s.io/controller-runtime/
6+
COPY pkg/ pkg/
7+
COPY crdexample/ crdexample/
8+
COPY vendor/ vendor/
9+
10+
# Build
11+
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager ./crdexample
12+
13+
# Copy the controller-manager into a thin image
14+
FROM ubuntu:latest
15+
WORKDIR /
16+
COPY --from=builder /go/src/sigs.k8s.io/controller-runtime/manager .
17+
ENTRYPOINT ["/manager"]

crdexample/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
build docker image
2+
3+
```bash
4+
docker build -f ./crdexample/Dockerfile .
5+
```

crdexample/controller.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"context"
21+
22+
"github.com/go-logr/logr"
23+
24+
appsv1 "k8s.io/api/apps/v1"
25+
corev1 "k8s.io/api/core/v1"
26+
"k8s.io/apimachinery/pkg/api/errors"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
"k8s.io/apimachinery/pkg/runtime"
29+
"sigs.k8s.io/controller-runtime/crdexample/logutil"
30+
"sigs.k8s.io/controller-runtime/crdexample/pkg"
31+
"sigs.k8s.io/controller-runtime/pkg/client"
32+
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
33+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
34+
)
35+
36+
var fmLog = logutil.Log.WithName("firstmate-reconciler")
37+
38+
// FirstMateController reconciles ReplicaSets
39+
type FirstMateController struct {
40+
// client can be used to retrieve objects from the APIServer.
41+
client client.Client
42+
}
43+
44+
func (i *FirstMateController) InjectClient(c client.Client) error {
45+
i.client = c
46+
return nil
47+
}
48+
49+
// Implement reconcile.Reconciler so the controller can reconcile objects
50+
var _ reconcile.Reconciler = &FirstMateController{}
51+
52+
func (r *FirstMateController) Reconcile(request reconcile.Request) (reconcile.Result, error) {
53+
// set up a entryLog object so we don't have to type request over and over again
54+
log := fmLog.WithValues("request", request)
55+
ctx := context.Background()
56+
57+
// Fetch the firstMate from the cache
58+
fm := &pkg.FirstMate{}
59+
if err := r.client.Get(ctx, request.NamespacedName, fm); errors.IsNotFound(err) {
60+
return reconcile.Result{}, nil
61+
} else if err != nil {
62+
log.Error(err, "could not fetch firstMate")
63+
return reconcile.Result{}, err
64+
}
65+
66+
dep := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: request.Name, Namespace: request.Namespace}}
67+
updateFn := (&createOrUpdateDeployment{firstMate: fm, log: fmLog}).do
68+
69+
_, err := controllerutil.CreateOrUpdate(ctx, r.client, dep, updateFn)
70+
if err != nil {
71+
return reconcile.Result{}, err
72+
}
73+
74+
return reconcile.Result{}, nil
75+
}
76+
77+
type createOrUpdateDeployment struct {
78+
firstMate *pkg.FirstMate
79+
log logr.Logger
80+
}
81+
82+
func (r *createOrUpdateDeployment) do(existing runtime.Object) error {
83+
r.log.Info("creating or updating deployment")
84+
dep := existing.(*appsv1.Deployment)
85+
dep.Labels = r.firstMate.Labels
86+
dep.Spec.Replicas = &r.firstMate.Spec.Crew
87+
dep.Spec.Template.Labels = r.firstMate.Labels
88+
dep.Spec.Selector.MatchLabels = r.firstMate.Labels
89+
dep.Spec.Template.Spec.Containers = []corev1.Container{{Name: "nginx", Image: "nginx"}}
90+
return nil
91+
}

crdexample/logutil/log.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package logutil
18+
19+
import (
20+
"github.com/go-logr/logr"
21+
22+
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
23+
)
24+
25+
var Log logr.Logger
26+
27+
func init() {
28+
logf.SetLogger(logf.ZapLogger(true))
29+
Log = logf.Log.WithName("crew-controller")
30+
}

crdexample/main.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"flag"
21+
"os"
22+
23+
appsv1 "k8s.io/api/apps/v1"
24+
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
25+
"sigs.k8s.io/controller-runtime/crdexample/logutil"
26+
"sigs.k8s.io/controller-runtime/crdexample/pkg"
27+
"sigs.k8s.io/controller-runtime/pkg/builder"
28+
"sigs.k8s.io/controller-runtime/pkg/client/config"
29+
"sigs.k8s.io/controller-runtime/pkg/manager"
30+
"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
31+
)
32+
33+
func main() {
34+
flag.Parse()
35+
entryLog := logutil.Log.WithName("entrypoint")
36+
37+
entryLog.Info("setting up manager")
38+
mgr, err := manager.New(config.GetConfigOrDie(), manager.Options{Port: 12789})
39+
if err != nil {
40+
entryLog.Error(err, "unable to set up overall controller manager")
41+
os.Exit(1)
42+
}
43+
44+
entryLog.Info("setting up scheme")
45+
if err := pkg.AddToScheme(mgr.GetScheme()); err != nil {
46+
entryLog.Error(err, "unable add APIs to scheme")
47+
os.Exit(1)
48+
}
49+
50+
entryLog.Info("setting up controllers")
51+
err = builder.ControllerManagedBy(mgr).
52+
For(&pkg.FirstMate{}).
53+
Owns(&appsv1.Deployment{}).
54+
Complete(&FirstMateController{})
55+
if err != nil {
56+
entryLog.Error(err, "unable to set up controllers")
57+
os.Exit(1)
58+
}
59+
60+
entryLog.Info("starting manager")
61+
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
62+
entryLog.Error(err, "unable to run manager")
63+
os.Exit(1)
64+
}
65+
}

crdexample/pkg/doc.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
Copyright 2018 The Kubernetes authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package v1 contains API Schema definitions for the crew v1 API group
18+
// +k8s:openapi-gen=true
19+
// +k8s:deepcopy-gen=package,register
20+
// +k8s:conversion-gen=sigs.k8s.io/kubebuilder/test/project/pkg/apis/crew
21+
// +k8s:defaulter-gen=TypeMeta
22+
// +groupName=crew.example.com
23+
package pkg

crdexample/pkg/resource.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package pkg
18+
19+
import (
20+
"fmt"
21+
22+
corev1 "k8s.io/api/core/v1"
23+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
"k8s.io/apimachinery/pkg/runtime"
25+
"k8s.io/apimachinery/pkg/runtime/schema"
26+
"sigs.k8s.io/controller-runtime/crdexample/logutil"
27+
"sigs.k8s.io/controller-runtime/pkg/scheme"
28+
"sigs.k8s.io/controller-runtime/pkg/webhook"
29+
)
30+
31+
// +genclient
32+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
33+
34+
var log = logutil.Log.WithName("firstmate-resource")
35+
36+
// firstMate is the Schema for the firstmates API
37+
// +k8s:openapi-gen=true
38+
type FirstMate struct {
39+
metav1.TypeMeta `json:",inline"`
40+
metav1.ObjectMeta `json:"metadata,omitempty"`
41+
42+
Spec FirstMateSpec `json:"spec,omitempty"`
43+
Status FirstMateStatus `json:"status,omitempty"`
44+
}
45+
46+
// FirstMateSpec defines the desired state of firstMate
47+
type FirstMateSpec struct {
48+
Height *int `json:"height,omitempty"`
49+
Experience int `json:"experience,omitempty"`
50+
Crew int32 `json:"crew,omitempty"`
51+
}
52+
53+
// FirstMateStatus defines the observed state of firstMate
54+
type FirstMateStatus struct {
55+
Location string
56+
}
57+
58+
var _ webhook.Validator = &FirstMate{}
59+
60+
// ValidateCreate implements webhookutil.validator so a webhook will be registered for the type
61+
func (f *FirstMate) ValidateCreate() error {
62+
log.Info("validate create", "name", f.Name)
63+
64+
if f.Spec.Crew <= 0 {
65+
return fmt.Errorf("crew must be greater than 0")
66+
}
67+
return nil
68+
}
69+
70+
// ValidateUpdate implements webhookutil.validator so a webhook will be registered for the type
71+
func (f *FirstMate) ValidateUpdate(old runtime.Object) error {
72+
log.Info("validate update", "name", f.Name)
73+
74+
if f.Spec.Crew <= 0 {
75+
return fmt.Errorf("crew must be greater than 0")
76+
}
77+
78+
oldF, ok := old.(*FirstMate)
79+
if !ok {
80+
return fmt.Errorf("expect old object to be a %T instead of %T", oldF, old)
81+
}
82+
if f.Spec.Crew-oldF.Spec.Crew > 1 || oldF.Spec.Crew-f.Spec.Crew > 1 {
83+
return fmt.Errorf("crew must be greater than 0")
84+
}
85+
return nil
86+
}
87+
88+
var _ webhook.Defaulter = &FirstMate{}
89+
90+
// Default implements webhookutil.defaulter so a webhook will be registered for the type
91+
func (f *FirstMate) Default() {
92+
log.Info("default", "name", f.Name)
93+
94+
if *f.Spec.Height == 0 {
95+
height := 10
96+
f.Spec.Height = &height
97+
}
98+
}
99+
100+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
101+
102+
// FirstMateList contains a list of firstMate
103+
type FirstMateList struct {
104+
metav1.TypeMeta `json:",inline"`
105+
metav1.ListMeta `json:"metadata,omitempty"`
106+
Items []FirstMate `json:"items"`
107+
}
108+
109+
func init() {
110+
SchemeBuilder.Register(&FirstMate{}, &FirstMateList{})
111+
}
112+
113+
var (
114+
// SchemeGroupVersion is group version used to register these objects
115+
SchemeGroupVersion = schema.GroupVersion{Group: "crew.example.com", Version: "v1"}
116+
117+
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
118+
SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
119+
120+
// AddToScheme is required by pkg/client/...
121+
AddToScheme = SchemeBuilder.AddToScheme
122+
)
123+
124+
// Resource is required by pkg/client/listers/...
125+
func Resource(resource string) schema.GroupResource {
126+
return SchemeGroupVersion.WithResource(resource).GroupResource()
127+
}
128+
129+
// TODO: figure out should we encourage the users to do a wrapper for a core type object.
130+
type MyPod struct {
131+
corev1.Pod
132+
}
133+
134+
var _ webhook.Defaulter = &MyPod{}
135+
136+
func (p *MyPod) Default() {
137+
log.Info("default", *p)
138+
}

0 commit comments

Comments
 (0)