Skip to content

Commit f0c9562

Browse files
authored
Merge pull request #3 from hongchaodeng/ctl
*: generate controller struct
2 parents 8dd8cc8 + b6228af commit f0c9562

File tree

2 files changed

+146
-3
lines changed

2 files changed

+146
-3
lines changed

pkg/generator/generator.go

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import (
55
"os"
66
"path/filepath"
77
"strings"
8+
"text/template"
9+
10+
"github.com/coreos/operator-sdk/pkg/templates"
811
)
912

1013
const defaultFileMode = 0750
@@ -41,7 +44,6 @@ func (g *Generator) Render() error {
4144
return err
4245
}
4346

44-
// pkg/apis/
4547
groupName, apiVersion := func() (string, string) {
4648
splits := strings.Split(apiGroup, "/")
4749
return strings.Split(splits[0], ".")[0], splits[1]
@@ -51,11 +53,34 @@ func (g *Generator) Render() error {
5153
return err
5254
}
5355

54-
// pkg/controller/
55-
err = os.MkdirAll(filepath.Join(projDir, "pkg/controller"), defaultFileMode)
56+
controllerDir := filepath.Join(projDir, "pkg/controller")
57+
err = os.MkdirAll(controllerDir, defaultFileMode)
58+
if err != nil {
59+
return err
60+
}
61+
62+
err = g.genWorkqueue(controllerDir)
63+
if err != nil {
64+
return err
65+
}
66+
67+
return nil
68+
}
69+
70+
func (g *Generator) genWorkqueue(controllerDir string) error {
71+
f, err := os.OpenFile(filepath.Join(controllerDir, "controller.go"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, defaultFileMode)
5672
if err != nil {
5773
return err
5874
}
75+
defer f.Close()
5976

77+
t, err := template.New("controller").Parse(templates.ControllerTemplate)
78+
if err != nil {
79+
return err
80+
}
81+
err = t.Execute(f, nil)
82+
if err != nil {
83+
return err
84+
}
6085
return nil
6186
}

pkg/templates/workqueue-tmpl.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package templates
2+
3+
// TODO: fix imports
4+
5+
const ControllerTemplate = `
6+
package controller
7+
8+
type Controller struct {
9+
indexer cache.Indexer
10+
queue workqueue.RateLimitingInterface
11+
informer cache.Controller
12+
}
13+
14+
func NewController(queue workqueue.RateLimitingInterface, indexer cache.Indexer, informer cache.Controller) *Controller {
15+
return &Controller{
16+
informer: informer,
17+
indexer: indexer,
18+
queue: queue,
19+
}
20+
}
21+
22+
// sync is the business logic of the controller.
23+
// In case an error happened, it has to simply return the error and will be retried after a backoff.
24+
func (c *Controller) sync(key string) error {
25+
obj, exists, err := c.indexer.GetByKey(key)
26+
if err != nil {
27+
return fmt.Errorf("Fetching object with key %s from store failed with %v", key, err)
28+
}
29+
30+
if !exists {
31+
// We warmed up the cache, so this could only imply the object was deleted
32+
return nil
33+
}
34+
35+
return nil
36+
}
37+
`
38+
39+
const WorkqueueTemplate = `
40+
package controller
41+
42+
const (
43+
// maxRetries is the number of times an event will be retried before it is dropped out of the queue.
44+
// With the current rate-limiter in use (5ms*2^(maxRetries-1)) the following numbers represent the times of requeues:
45+
// 5ms, 10ms, 20ms, 40ms, 80ms, 160ms, 320ms, 640ms, 1.3s, 2.6s, 5.1s, 10.2s, 20.4s, 41s, 82s
46+
maxRetries = 15
47+
)
48+
49+
func (c *Controller) processNextItem() bool {
50+
// Wait until there is a new item in the working queue
51+
key, quit := c.queue.Get()
52+
if quit {
53+
return false
54+
}
55+
// Tell the queue that we are done with processing this key. This unblocks the key for other workers
56+
// This allows safe parallel processing because two pods with the same key are never processed in
57+
// parallel.
58+
defer c.queue.Done(key)
59+
60+
// Invoke the method containing the business logic
61+
err := c.sync(key.(string))
62+
63+
// Handle the error if something went wrong during the execution of the business logic
64+
c.handleErr(err, key)
65+
return true
66+
}
67+
68+
// handleErr checks if an error happened and makes sure we will retry later.
69+
func (c *Controller) handleErr(err error, key interface{}) {
70+
if err == nil {
71+
// Forget about the #AddRateLimited history of the key on every successful synchronization.
72+
// This ensures that future processing of updates for this key is not delayed because of
73+
// an outdated error history.
74+
c.queue.Forget(key)
75+
return
76+
}
77+
78+
// This controller retries maxRetries times if something goes wrong. After that, it stops trying.
79+
if c.queue.NumRequeues(key) < maxRetries {
80+
glog.Infof("Error syncing pod %v: %v", key, err)
81+
82+
// Re-enqueue the key rate limited. Based on the rate limiter on the
83+
// queue and the re-enqueue history, the key will be processed later again.
84+
c.queue.AddRateLimited(key)
85+
return
86+
}
87+
88+
c.queue.Forget(key)
89+
// Report to an external entity that, even after several retries, we could not successfully process this key
90+
runtime.HandleError(err)
91+
glog.Infof("Dropping pod %q out of the queue: %v", key, err)
92+
}
93+
94+
// numWorkers is the number of goroutine workers to process events concurrently.
95+
func (c *Controller) Run(ctx context.Context, numWorkers int) {
96+
// Let the workers stop when we are done
97+
defer c.queue.ShutDown()
98+
99+
go c.informer.Run(stopCh)
100+
101+
// Wait for the cache to be synced, before processing items from the queue is started
102+
if !cache.WaitForCacheSync(stopCh, c.informer.HasSynced) {
103+
runtime.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
104+
return
105+
}
106+
107+
for i := 0; i < numWorkers; i++ {
108+
go wait.Until(c.runWorker, time.Second, stopCh)
109+
}
110+
111+
<-ctx.Done()
112+
}
113+
114+
func (c *Controller) runWorker() {
115+
for c.processNextItem() {
116+
}
117+
}
118+
`

0 commit comments

Comments
 (0)