From 2269bff181bfe79971382acfa3e2b4597b6c4638 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Wed, 18 Mar 2020 11:21:16 +0100 Subject: [PATCH 01/31] Refactor controller code Signed-off-by: Xabier Larrakoetxea --- client/crd/crd.go | 234 ---------- client/crd/crd_test.go | 309 ------------- .../controller => controller}/controller.go | 0 {operator/controller => controller}/doc.go | 0 controller/generic.go | 307 +++++++++++++ .../controller => controller}/generic_test.go | 203 +++------ {operator/handler => controller}/handler.go | 2 +- .../leaderelection/leaderelection.go | 0 {operator/retrieve => controller}/retrieve.go | 2 +- mocks/{operator => }/controller/Controller.go | 0 .../handler => controller}/Handler.go | 2 +- mocks/doc.go | 5 +- mocks/operator/resource/CRD.go | 61 --- operator/controller/config.go | 36 -- operator/controller/generic.go | 424 ------------------ operator/doc.go | 3 - operator/handler/common.go | 34 -- operator/operator.go | 172 ------- operator/operator_test.go | 220 --------- operator/resource/crd.go | 12 - 20 files changed, 370 insertions(+), 1656 deletions(-) delete mode 100644 client/crd/crd.go delete mode 100644 client/crd/crd_test.go rename {operator/controller => controller}/controller.go (100%) rename {operator/controller => controller}/doc.go (100%) create mode 100644 controller/generic.go rename {operator/controller => controller}/generic_test.go (69%) rename {operator/handler => controller}/handler.go (98%) rename {operator/controller => controller}/leaderelection/leaderelection.go (100%) rename {operator/retrieve => controller}/retrieve.go (97%) rename mocks/{operator => }/controller/Controller.go (100%) rename mocks/{operator/handler => controller}/Handler.go (97%) delete mode 100644 mocks/operator/resource/CRD.go delete mode 100644 operator/controller/config.go delete mode 100644 operator/controller/generic.go delete mode 100644 operator/doc.go delete mode 100644 operator/handler/common.go delete mode 100644 operator/operator.go delete mode 100644 operator/operator_test.go delete mode 100644 operator/resource/crd.go diff --git a/client/crd/crd.go b/client/crd/crd.go deleted file mode 100644 index 7c427e8b..00000000 --- a/client/crd/crd.go +++ /dev/null @@ -1,234 +0,0 @@ -package crd - -import ( - "fmt" - "time" - - apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - apiextensionscli "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - kubeversion "k8s.io/apimachinery/pkg/util/version" - - "github.com/spotahome/kooper/log" - wraptime "github.com/spotahome/kooper/wrapper/time" -) - -const ( - checkCRDInterval = 2 * time.Second - crdReadyTimeout = 3 * time.Minute -) - -var ( - clusterMinVersion = kubeversion.MustParseGeneric("v1.7.0") - defCategories = []string{"all", "kooper"} -) - -// Scope is the scope of a CRD. -type Scope = apiextensionsv1beta1.ResourceScope - -const ( - // ClusterScoped represents a type of a cluster scoped CRD. - ClusterScoped = apiextensionsv1beta1.ClusterScoped - // NamespaceScoped represents a type of a namespaced scoped CRD. - NamespaceScoped = apiextensionsv1beta1.NamespaceScoped -) - -// Conf is the configuration required to create a CRD -type Conf struct { - // Kind is the kind of the CRD. - Kind string - // NamePlural is the plural name of the CRD (in most cases the plural of Kind). - NamePlural string - // ShortNames are short names of the CRD. It must be all lowercase. - ShortNames []string - // Group is the group of the CRD. - Group string - // Version is the version of the CRD. - Version string - // Scope is the scode of the CRD (cluster scoped or namespace scoped). - Scope Scope - // Categories is a way of grouping multiple resources (example `kubectl get all`), - // Kooper adds the CRD to `all` and `kooper` categories(apart from the described in Caregories). - Categories []string - // EnableStatus will enable the Status subresource on the CRD. This is feature - // entered in v1.10 with the CRD subresources. - // By default is disabled. - EnableStatusSubresource bool - // EnableScaleSubresource by default will be nil and means disabled, if - // the object is present it will set this scale configuration to the subresource. - EnableScaleSubresource *apiextensionsv1beta1.CustomResourceSubresourceScale -} - -func (c *Conf) getName() string { - return fmt.Sprintf("%s.%s", c.NamePlural, c.Group) -} - -// Interface is the CRD client that knows how to interact with k8s to manage them. -type Interface interface { - // EnsureCreated will ensure the the CRD is present, this also means that - // apart from creating the CRD if is not present it will wait until is - // ready, this is a blocking operation and will return an error if timesout - // waiting. - EnsurePresent(conf Conf) error - // WaitToBePresent will wait until the CRD is present, it will check if - // is present at regular intervals until it timesout, in case of timeout - // will return an error. - WaitToBePresent(name string, timeout time.Duration) error - // Delete will delete the CRD. - Delete(name string) error -} - -// Client is the CRD client implementation using API calls to kubernetes. -type Client struct { - aeClient apiextensionscli.Interface - logger log.Logger - time wraptime.Time // Use a time wrapper so we can control the time on our tests. -} - -// NewClient returns a new CRD client. -func NewClient(aeClient apiextensionscli.Interface, logger log.Logger) *Client { - return NewCustomClient(aeClient, wraptime.Base, logger) -} - -// NewCustomClient returns a new CRD client letting you set all the required parameters -func NewCustomClient(aeClient apiextensionscli.Interface, time wraptime.Time, logger log.Logger) *Client { - return &Client{ - aeClient: aeClient, - logger: logger, - time: time, - } -} - -// EnsurePresent satisfies crd.Interface. -func (c *Client) EnsurePresent(conf Conf) error { - if err := c.validClusterForCRDs(); err != nil { - return err - } - - // Get the generated name of the CRD. - crdName := conf.getName() - - // Create subresources - subres := c.createSubresources(conf) - - crd := &apiextensionsv1beta1.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: crdName, - }, - Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ - Group: conf.Group, - Version: conf.Version, - Scope: conf.Scope, - Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ - Plural: conf.NamePlural, - Kind: conf.Kind, - ShortNames: conf.ShortNames, - Categories: c.addDefaultCaregories(conf.Categories), - }, - Subresources: subres, - }, - } - - _, err := c.aeClient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd) - if err != nil { - if !errors.IsAlreadyExists(err) { - return fmt.Errorf("error creating crd %s: %s", crdName, err) - } - return nil - } - - c.logger.Infof("crd %s created, waiting to be ready...", crdName) - if err := c.WaitToBePresent(crdName, crdReadyTimeout); err != nil { - return err - } - c.logger.Infof("crd %s ready", crdName) - - return nil -} - -func (c *Client) createSubresources(conf Conf) *apiextensionsv1beta1.CustomResourceSubresources { - if !conf.EnableStatusSubresource && conf.EnableScaleSubresource == nil { - return nil - } - - sr := &apiextensionsv1beta1.CustomResourceSubresources{} - - if conf.EnableStatusSubresource { - sr.Status = &apiextensionsv1beta1.CustomResourceSubresourceStatus{} - } - - if conf.EnableScaleSubresource != nil { - sr.Scale = conf.EnableScaleSubresource - } - - return sr -} - -// WaitToBePresent satisfies crd.Interface. -func (c *Client) WaitToBePresent(name string, timeout time.Duration) error { - if err := c.validClusterForCRDs(); err != nil { - return err - } - - tout := c.time.After(timeout) - t := c.time.NewTicker(checkCRDInterval) - - for { - select { - case <-t.C: - _, err := c.aeClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(name, metav1.GetOptions{}) - // Is present, finish. - if err == nil { - return nil - } - case <-tout: - return fmt.Errorf("timeout waiting for CRD") - } - } -} - -// Delete satisfies crd.Interface. -func (c *Client) Delete(name string) error { - if err := c.validClusterForCRDs(); err != nil { - return err - } - - return c.aeClient.ApiextensionsV1beta1().CustomResourceDefinitions().Delete(name, &metav1.DeleteOptions{}) -} - -// validClusterForCRDs returns nil if cluster is ok to be used for CRDs, otherwise error. -func (c *Client) validClusterForCRDs() error { - // Check cluster version. - v, err := c.aeClient.Discovery().ServerVersion() - if err != nil { - return err - } - parsedV, err := kubeversion.ParseGeneric(v.GitVersion) - if err != nil { - return err - } - - if parsedV.LessThan(clusterMinVersion) { - return fmt.Errorf("not a valid cluster version for CRDs (required >=1.7)") - } - - return nil -} - -// addAllCaregory adds the `all` category if isn't present -func (c *Client) addDefaultCaregories(categories []string) []string { - currentCats := make(map[string]bool) - for _, ca := range categories { - currentCats[ca] = true - } - - // Add default categories if required. - for _, ca := range defCategories { - if _, ok := currentCats[ca]; !ok { - categories = append(categories, ca) - } - } - - return categories -} diff --git a/client/crd/crd_test.go b/client/crd/crd_test.go deleted file mode 100644 index 47524037..00000000 --- a/client/crd/crd_test.go +++ /dev/null @@ -1,309 +0,0 @@ -package crd_test - -import ( - "errors" - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - apiextensionscli "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake" - kubeerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/version" - fakediscovery "k8s.io/client-go/discovery/fake" - kubetesting "k8s.io/client-go/testing" - - "github.com/spotahome/kooper/client/crd" - "github.com/spotahome/kooper/log" - mtime "github.com/spotahome/kooper/mocks/wrapper/time" -) - -var ( - crdGroup = schema.GroupVersionResource{Group: "apiextensions.k8s.io", Version: "v1beta1", Resource: "customresourcedefinitions"} - goodClusterVersion = "v1.7" - badClusterVersion = "v1.6" - statusLabelSelector = ".status.labelSelector" -) - -// newClient returns a new mock client. -func newClient() *apiextensionscli.Clientset { - return newVersionedClusterClient(goodClusterVersion) -} - -// newVersionedClusterClient returns a new mock client with the cluster version set -func newVersionedClusterClient(clusterVersion string) *apiextensionscli.Clientset { - cli := apiextensionscli.NewSimpleClientset() - - // Fake cluster version. - fakeDiscovery, _ := cli.Discovery().(*fakediscovery.FakeDiscovery) - fakeDiscovery.FakedServerVersion = &version.Info{ - GitVersion: clusterVersion, - } - - // Use different fake action registry for CRD api calls client and discovery client. By default they - // share the same registry(testing.Fake) and we don't want to mix version check actions with the CRD - // actions. - cli.Fake = kubetesting.Fake{} - fakeDiscovery.Fake = &kubetesting.Fake{} - - return cli -} - -func newCRDGetAction(name string) kubetesting.GetActionImpl { - return kubetesting.NewGetAction(crdGroup, "", name) -} - -func newCRDCreateAction(crd *apiextensionsv1beta1.CustomResourceDefinition) kubetesting.CreateActionImpl { - return kubetesting.NewCreateAction(crdGroup, "", crd) -} - -func TestCRDEnsurePresent(t *testing.T) { - tests := []struct { - name string - clusterVersion string - crd crd.Conf - retErr error - expErr bool - expCalls []kubetesting.Action - }{ - { - name: "Creating a non existen CRD (using custom categories) should create a crd without error", - clusterVersion: goodClusterVersion, - crd: crd.Conf{ - Kind: "Test", - NamePlural: "tests", - ShortNames: []string{"tst"}, - Scope: crd.ClusterScoped, - Group: "toilettesting", - Version: "v99", - Categories: []string{ - "category1", - "categoryA", - }, - }, - retErr: nil, - expErr: false, - expCalls: []kubetesting.Action{ - newCRDCreateAction(&apiextensionsv1beta1.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: "tests.toilettesting", - }, - Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ - Group: "toilettesting", - Version: "v99", - Scope: crd.ClusterScoped, - Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ - Plural: "tests", - Kind: "Test", - ShortNames: []string{"tst"}, - Categories: []string{"category1", "categoryA", "all", "kooper"}, - }, - }, - }), - newCRDGetAction("tests.toilettesting"), - }, - }, - { - name: "Creating a CRD that errors should return an error.", - clusterVersion: goodClusterVersion, - crd: crd.Conf{ - Kind: "Test", - NamePlural: "tests", - ShortNames: []string{"tst"}, - Scope: crd.ClusterScoped, - Group: "toilettesting", - Version: "v99", - }, - retErr: errors.New("wanted error"), - expErr: true, - expCalls: []kubetesting.Action{}, - }, - { - name: "Creating a CRD that exists shouldn't return an error.", - clusterVersion: goodClusterVersion, - crd: crd.Conf{ - Kind: "Test", - NamePlural: "tests", - ShortNames: []string{"tst"}, - Scope: crd.ClusterScoped, - Group: "toilettesting", - Version: "v99", - }, - expCalls: []kubetesting.Action{ - newCRDCreateAction(&apiextensionsv1beta1.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: "tests.toilettesting", - }, - Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ - Group: "toilettesting", - Version: "v99", - Scope: crd.ClusterScoped, - Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ - Plural: "tests", - Kind: "Test", - ShortNames: []string{"tst"}, - Categories: []string{"all", "kooper"}, - }, - }, - }), - }, - retErr: kubeerrors.NewAlreadyExists(schema.GroupResource{}, ""), - expErr: false, - }, - { - name: "Creating a CRD with subresources active should create the CRD with the subresources set.", - clusterVersion: goodClusterVersion, - crd: crd.Conf{ - Kind: "Test", - NamePlural: "tests", - ShortNames: []string{"tst"}, - Scope: crd.ClusterScoped, - Group: "toilettesting", - Version: "v99", - EnableStatusSubresource: true, - EnableScaleSubresource: &apiextensionsv1beta1.CustomResourceSubresourceScale{ - SpecReplicasPath: ".spec.replicas", - StatusReplicasPath: ".status.replicas", - LabelSelectorPath: &statusLabelSelector, - }, - }, - expCalls: []kubetesting.Action{ - newCRDCreateAction(&apiextensionsv1beta1.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: "tests.toilettesting", - }, - Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ - Group: "toilettesting", - Version: "v99", - Scope: crd.ClusterScoped, - Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ - Plural: "tests", - Kind: "Test", - ShortNames: []string{"tst"}, - Categories: []string{"all", "kooper"}, - }, - Subresources: &apiextensionsv1beta1.CustomResourceSubresources{ - Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{}, - Scale: &apiextensionsv1beta1.CustomResourceSubresourceScale{ - SpecReplicasPath: ".spec.replicas", - StatusReplicasPath: ".status.replicas", - LabelSelectorPath: &statusLabelSelector, - }, - }, - }, - }), - newCRDGetAction("tests.toilettesting"), - }, - retErr: nil, - expErr: false, - }, - { - name: "If the cluster version is not supported it should fail.", - clusterVersion: badClusterVersion, - crd: crd.Conf{}, - retErr: nil, - expErr: true, - expCalls: []kubetesting.Action{}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - assert := assert.New(t) - - // Mock calls - cli := newVersionedClusterClient(test.clusterVersion) - cli.AddReactor("create", "customresourcedefinitions", func(action kubetesting.Action) (bool, runtime.Object, error) { - return true, nil, test.retErr - }) - cli.AddReactor("get", "customresourcedefinitions", func(action kubetesting.Action) (bool, runtime.Object, error) { - return true, nil, nil - }) - - crdCli := crd.NewClient(cli, log.Dummy) - err := crdCli.EnsurePresent(test.crd) - if test.expErr { - assert.Error(err) - } else if assert.NoError(err) { - assert.Equal(test.expCalls, cli.Actions()) - } - - }) - } -} - -func TestCRDWaitToBePresent(t *testing.T) { - never := 999999 * time.Hour - tests := []struct { - name string - clusterVersion string - crdName string - timeout time.Duration - interval time.Duration - readyAfter time.Duration - expErr bool - }{ - { - name: "If timeouts it should return an error", - clusterVersion: goodClusterVersion, - crdName: "test", - timeout: 1, - interval: never, - readyAfter: never, - expErr: true, - }, - { - name: "If the cluster version is not supported, it should fail", - clusterVersion: badClusterVersion, - crdName: "test", - timeout: 1, - interval: never, - readyAfter: never, - expErr: true, - }, - { - name: "After some time the CRD will be ready and won't timeout", - clusterVersion: goodClusterVersion, - crdName: "test", - timeout: 50 * time.Millisecond, - interval: 5 * time.Millisecond, - readyAfter: 25 * time.Millisecond, - expErr: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - assert := assert.New(t) - - // Mock calls - mt := &mtime.Time{} - mt.On("After", mock.Anything).Once().Return(time.After(test.timeout)) - mt.On("NewTicker", mock.Anything).Once().Return(time.NewTicker(test.interval)) - cli := newVersionedClusterClient(test.clusterVersion) - start := time.Now() - cli.AddReactor("get", "customresourcedefinitions", func(action kubetesting.Action) (bool, runtime.Object, error) { - // Get how long we've been running and if it passed readyAfter - // our CRD is ready. - runningTime := time.Now().Sub(start) - if runningTime >= test.readyAfter { - return true, nil, nil - } - return true, nil, fmt.Errorf("wanted error") - - }) - - crdCli := crd.NewCustomClient(cli, mt, log.Dummy) - err := crdCli.WaitToBePresent(test.crdName, 0) - if test.expErr { - assert.Error(err) - } else { - assert.NoError(err) - } - }) - } -} diff --git a/operator/controller/controller.go b/controller/controller.go similarity index 100% rename from operator/controller/controller.go rename to controller/controller.go diff --git a/operator/controller/doc.go b/controller/doc.go similarity index 100% rename from operator/controller/doc.go rename to controller/doc.go diff --git a/controller/generic.go b/controller/generic.go new file mode 100644 index 00000000..2eaed9b4 --- /dev/null +++ b/controller/generic.go @@ -0,0 +1,307 @@ +package controller + +import ( + "context" + "errors" + "fmt" + "sync" + "time" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" + + "github.com/spotahome/kooper/controller/leaderelection" + "github.com/spotahome/kooper/log" + "github.com/spotahome/kooper/monitoring/metrics" +) + +var ( + // ErrControllerNotValid will be used when the controller has invalid configuration. + ErrControllerNotValid = errors.New("controller not valid") +) + +// Defaults. +const ( + defResyncInterval = 3 * time.Minute + defConcurrentWorkers = 3 + defProcessingJobRetries = 3 +) + +// Config is the controller configuration. +type Config struct { + // Handler is the controller handler. + Handler Handler + // Retriever is the controller retriever. + Retriever Retriever + // Leader elector will be used to use only one instance, if no set it will be + // leader election will be ignored + LeaderElector leaderelection.Runner + // MetricsRecorder will record the controller metrics. + MetricRecorder metrics.Recorder + // Logger will log messages of the controller. + Logger log.Logger + + // name of the controller. + Name string + // ConcurrentWorkers is the number of concurrent workers the controller will have running processing events. + ConcurrentWorkers int + // ResyncInterval is the interval the controller will process all the selected resources. + ResyncInterval time.Duration + // ProcessingJobRetries is the number of times the job will try to reprocess the event before returning a real error. + ProcessingJobRetries int +} + +func (c *Config) setDefaults() error { + if c.Name == "" { + return fmt.Errorf("a controller name is required") + } + + if c.Handler == nil { + return fmt.Errorf("a handler is required") + } + + if c.Retriever == nil { + return fmt.Errorf("a retriever is required") + } + + if c.Logger == nil { + c.Logger = &log.Std{} + c.Logger.Warningf("no logger specified, fallback to default logger, to disable logging use a explicit Noop logger") + } + + if c.MetricRecorder == nil { + c.MetricRecorder = metrics.Dummy + c.Logger.Warningf("no metrics recorder specified, disabling metrics") + } + + if c.ConcurrentWorkers <= 0 { + c.ConcurrentWorkers = defConcurrentWorkers + } + + if c.ResyncInterval <= 0 { + c.ResyncInterval = defResyncInterval + } + + if c.ProcessingJobRetries <= 0 { + c.ProcessingJobRetries = defProcessingJobRetries + } + + return nil +} + +// generic controller is a controller that can be used to create different kind of controllers. +type generic struct { + queue workqueue.RateLimitingInterface // queue will have the jobs that the controller will get and send to handlers. + informer cache.SharedIndexInformer // informer will notify be inform us about resource changes. + handler Handler // handler is where the logic of resource processing. + running bool + runningMu sync.Mutex + cfg Config + metrics metrics.Recorder + leRunner leaderelection.Runner + logger log.Logger +} + +// New creates a new controller that can be configured using the cfg parameter. +func New(cfg *Config) (Controller, error) { + // Sets the required default configuration. + err := cfg.setDefaults() + if err != nil { + return nil, fmt.Errorf("could no create controller: %w: %v", ErrControllerNotValid, err) + } + + // Create the queue that will have our received job changes. It's rate limited so we don't have problems when + // a job processing errors every time is processed in a loop. + queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()) + + // store is the internal cache where objects will be store. + store := cache.Indexers{} + informer := cache.NewSharedIndexInformer(cfg.Retriever.GetListerWatcher(), cfg.Retriever.GetObject(), cfg.ResyncInterval, store) + + // Set up our informer event handler. + // Objects are already in our local store. Add only keys/jobs on the queue so they can bre processed + // afterwards. + informer.AddEventHandlerWithResyncPeriod(cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + key, err := cache.MetaNamespaceKeyFunc(obj) + if err == nil { + queue.Add(key) + cfg.MetricRecorder.IncResourceEventQueued(cfg.Name, metrics.AddEvent) + } + }, + UpdateFunc: func(old interface{}, new interface{}) { + key, err := cache.MetaNamespaceKeyFunc(new) + if err == nil { + queue.Add(key) + cfg.MetricRecorder.IncResourceEventQueued(cfg.Name, metrics.AddEvent) + } + }, + DeleteFunc: func(obj interface{}) { + key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) + if err == nil { + queue.Add(key) + cfg.MetricRecorder.IncResourceEventQueued(cfg.Name, metrics.DeleteEvent) + } + }, + }, cfg.ResyncInterval) + + // Create our generic controller object. + return &generic{ + queue: queue, + informer: informer, + logger: cfg.Logger, + metrics: cfg.MetricRecorder, + handler: cfg.Handler, + leRunner: cfg.LeaderElector, + cfg: *cfg, + }, nil +} + +func (g *generic) isRunning() bool { + g.runningMu.Lock() + defer g.runningMu.Unlock() + return g.running +} + +func (g *generic) setRunning(running bool) { + g.runningMu.Lock() + defer g.runningMu.Unlock() + g.running = running +} + +// Run will run the controller. +func (g *generic) Run(stopC <-chan struct{}) error { + // Check if leader election is required. + if g.leRunner != nil { + return g.leRunner.Run(func() error { + return g.run(stopC) + }) + } + + return g.run(stopC) +} + +// run is the real run of the controller. +func (g *generic) run(stopC <-chan struct{}) error { + if g.isRunning() { + return fmt.Errorf("controller already running") + } + + g.logger.Infof("starting controller") + // Set state of controller. + g.setRunning(true) + defer g.setRunning(false) + + // Shutdown when Run is stopped so we can process the last items and the queue doesn't + // accept more jobs. + defer g.queue.ShutDown() + + // Run the informer so it starts listening to resource events. + go g.informer.Run(stopC) + + // Wait until our store, jobs... stuff is synced (first list on resource, resources on store and jobs on queue). + if !cache.WaitForCacheSync(stopC, g.informer.HasSynced) { + return fmt.Errorf("timed out waiting for caches to sync") + } + + // Start our resource processing worker, if finishes then restart the worker. The workers should + // not end. + for i := 0; i < g.cfg.ConcurrentWorkers; i++ { + go func() { + wait.Until(g.runWorker, time.Second, stopC) + }() + } + + // Until will be running our workers in a continuous way (and re run if they fail). But + // when stop signal is received we must stop. + <-stopC + g.logger.Infof("stopping controller") + + return nil +} + +// runWorker will start a processing loop on event queue. +func (g *generic) runWorker() { + for { + // Process newxt queue job, if needs to stop processing it will return true. + if g.getAndProcessNextJob() { + break + } + } +} + +// getAndProcessNextJob job will process the next job of the queue job and returns if +// it needs to stop processing. +func (g *generic) getAndProcessNextJob() bool { + // Get next job. + nextJob, exit := g.queue.Get() + if exit { + return true + } + defer g.queue.Done(nextJob) + key := nextJob.(string) + + // Process the job. If errors then enqueue again. + ctx := context.Background() + if err := g.processJob(ctx, key); err == nil { + g.queue.Forget(key) + } else if g.queue.NumRequeues(key) < g.cfg.ProcessingJobRetries { + // Job processing failed, requeue. + g.logger.Warningf("error processing %s job (requeued): %v", key, err) + g.queue.AddRateLimited(key) + g.metrics.IncResourceEventQueued(g.cfg.Name, metrics.RequeueEvent) + } else { + g.logger.Errorf("Error processing %s: %v", key, err) + g.queue.Forget(key) + } + + return false +} + +// processJob is where the real processing logic of the item is. +func (g *generic) processJob(ctx context.Context, key string) error { + // Get the object + obj, exists, err := g.informer.GetIndexer().GetByKey(key) + if err != nil { + return err + } + + // handle the object. + if !exists { // Deleted resource from the cache. + return g.handleDelete(ctx, key) + } + + return g.handleAdd(ctx, key, obj.(runtime.Object)) +} + +func (g *generic) handleAdd(ctx context.Context, objKey string, obj runtime.Object) error { + start := time.Now() + g.metrics.IncResourceEventProcessed(g.cfg.Name, metrics.AddEvent) + defer func() { + g.metrics.ObserveDurationResourceEventProcessed(g.cfg.Name, metrics.AddEvent, start) + }() + + // Handle the job. + if err := g.handler.Add(ctx, obj); err != nil { + g.metrics.IncResourceEventProcessedError(g.cfg.Name, metrics.AddEvent) + return err + } + return nil +} + +func (g *generic) handleDelete(ctx context.Context, objKey string) error { + start := time.Now() + g.metrics.IncResourceEventProcessed(g.cfg.Name, metrics.DeleteEvent) + defer func() { + g.metrics.ObserveDurationResourceEventProcessed(g.cfg.Name, metrics.DeleteEvent, start) + }() + + // Handle the job. + if err := g.handler.Delete(ctx, objKey); err != nil { + g.metrics.IncResourceEventProcessedError(g.cfg.Name, metrics.DeleteEvent) + return err + } + return nil +} diff --git a/operator/controller/generic_test.go b/controller/generic_test.go similarity index 69% rename from operator/controller/generic_test.go rename to controller/generic_test.go index 8115f97f..93ea505e 100644 --- a/operator/controller/generic_test.go +++ b/controller/generic_test.go @@ -1,15 +1,13 @@ package controller_test import ( - "context" "fmt" "testing" "time" - opentracing "github.com/opentracing/opentracing-go" - "github.com/opentracing/opentracing-go/mocktracer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -20,10 +18,9 @@ import ( "k8s.io/client-go/tools/cache" "github.com/spotahome/kooper/log" - mhandler "github.com/spotahome/kooper/mocks/operator/handler" - "github.com/spotahome/kooper/monitoring/metrics" - "github.com/spotahome/kooper/operator/controller" - "github.com/spotahome/kooper/operator/controller/leaderelection" + mcontroller "github.com/spotahome/kooper/mocks/controller" + "github.com/spotahome/kooper/controller" + "github.com/spotahome/kooper/controller/leaderelection" ) // Namespace knows how to retrieve namespaces. @@ -128,6 +125,7 @@ func TestGenericControllerHandleAdds(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { assert := assert.New(t) + require := require.New(t) controllerStopperC := make(chan struct{}) resultC := make(chan error) @@ -137,7 +135,7 @@ func TestGenericControllerHandleAdds(t *testing.T) { // Mock our handler and set expects. callHandling := 0 // used to track the number of calls. - mh := &mhandler.Handler{} + mh := &mcontroller.Handler{} for _, ns := range test.expNSAdds { mh.On("Add", mock.Anything, ns).Once().Return(nil).Run(func(args mock.Arguments) { callHandling++ @@ -149,8 +147,12 @@ func TestGenericControllerHandleAdds(t *testing.T) { }) } - nsret := newNamespaceRetriever(mc) - c := controller.NewSequential(0, mh, nsret, metrics.Dummy, log.Dummy) + c, err := controller.New(&controller.Config{ + Name: "test", + Handler: mh, + Retriever: newNamespaceRetriever(mc), + }) + require.NoError(err) // Run Controller in background. go func() { @@ -194,6 +196,7 @@ func TestGenericControllerHandleDeletes(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { assert := assert.New(t) + require := require.New(t) controllerStopperC := make(chan struct{}) resultC := make(chan error) @@ -205,7 +208,7 @@ func TestGenericControllerHandleDeletes(t *testing.T) { // Mock our handler and set expects. callHandling := 0 // used to track the number of calls. - mh := &mhandler.Handler{} + mh := &mcontroller.Handler{} mh.On("Add", mock.Anything, mock.Anything).Return(nil) for _, ns := range test.expDeleteNs { mh.On("Delete", mock.Anything, ns.ObjectMeta.Name).Once().Return(nil).Run(func(args mock.Arguments) { @@ -218,8 +221,12 @@ func TestGenericControllerHandleDeletes(t *testing.T) { }) } - nsret := newNamespaceRetriever(mc) - c := controller.NewSequential(0, mh, nsret, metrics.Dummy, log.Dummy) + c, err := controller.New(&controller.Config{ + Name: "test", + Handler: mh, + Retriever: newNamespaceRetriever(mc), + }) + require.NoError(err) // Run Controller in background. go func() { @@ -258,6 +265,7 @@ func TestGenericControllerErrorRetries(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { assert := assert.New(t) + require := require.New(t) controllerStopperC := make(chan struct{}) resultC := make(chan error) @@ -268,7 +276,7 @@ func TestGenericControllerErrorRetries(t *testing.T) { // Mock our handler and set expects. totalCalls := len(test.nsList.Items) + len(test.nsList.Items)*test.retryNumber - mh := &mhandler.Handler{} + mh := &mcontroller.Handler{} err := fmt.Errorf("wanted error") // Expect all the retries @@ -284,11 +292,13 @@ func TestGenericControllerErrorRetries(t *testing.T) { }) } - nsret := newNamespaceRetriever(mc) - cfg := &controller.Config{ + c, err := controller.New(&controller.Config{ + Name: "test", + Handler: mh, + Retriever: newNamespaceRetriever(mc), ProcessingJobRetries: test.retryNumber, - } - c := controller.New(cfg, mh, nsret, nil, nil, metrics.Dummy, log.Dummy) + }) + require.NoError(err) // Run Controller in background. go func() { @@ -326,6 +336,7 @@ func TestGenericControllerWithLeaderElection(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { assert := assert.New(t) + require := require.New(t) controllerStopperC := make(chan struct{}) resultC := make(chan error) @@ -333,9 +344,9 @@ func TestGenericControllerWithLeaderElection(t *testing.T) { mc := fake.NewSimpleClientset(nsList) // Mock our handler and set expects. - mh1 := &mhandler.Handler{} - mh2 := &mhandler.Handler{} - mh3 := &mhandler.Handler{} + mh1 := &mcontroller.Handler{} + mh2 := &mcontroller.Handler{} + mh3 := &mcontroller.Handler{} // Expect the calls on the lead (mh1) and no calls on the other ones. totalCalls := len(test.nsList.Items) @@ -349,9 +360,6 @@ func TestGenericControllerWithLeaderElection(t *testing.T) { }) nsret := newNamespaceRetriever(mc) - cfg := &controller.Config{ - ProcessingJobRetries: test.retryNumber, - } // Leader election service. rlCfg := &leaderelection.LockConfig{ @@ -363,9 +371,32 @@ func TestGenericControllerWithLeaderElection(t *testing.T) { lesvc2, _ := leaderelection.New("test", "default", rlCfg, mc, log.Dummy) lesvc3, _ := leaderelection.New("test", "default", rlCfg, mc, log.Dummy) - c1 := controller.New(cfg, mh1, nsret, lesvc1, nil, metrics.Dummy, log.Dummy) - c2 := controller.New(cfg, mh2, nsret, lesvc2, nil, metrics.Dummy, log.Dummy) - c3 := controller.New(cfg, mh3, nsret, lesvc3, nil, metrics.Dummy, log.Dummy) + c1, err := controller.New(&controller.Config{ + Name: "test1", + Handler: mh1, + Retriever: nsret, + LeaderElector: lesvc1, + ProcessingJobRetries: test.retryNumber, + }) + require.NoError(err) + + c2, err := controller.New(&controller.Config{ + Name: "test2", + Handler: mh2, + Retriever: nsret, + LeaderElector: lesvc2, + ProcessingJobRetries: test.retryNumber, + }) + require.NoError(err) + + c3, err := controller.New(&controller.Config{ + Name: "test3", + Handler: mh3, + Retriever: nsret, + LeaderElector: lesvc3, + ProcessingJobRetries: test.retryNumber, + }) + require.NoError(err) // Run multiple controller in background. go func() { resultC <- c1.Run(controllerStopperC) }() @@ -389,121 +420,3 @@ func TestGenericControllerWithLeaderElection(t *testing.T) { }) } } - -func TestGenericControllerTracing(t *testing.T) { - tests := []struct { - name string - addNs corev1.Namespace - addErr error - maxRetries int - expNS string - expTraces int - }{ - { - name: "Listing a namespace should only create a trace.", - addNs: corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-example", - }, - }, - expNS: "test-example", - expTraces: 2, - }, - { - name: "Listing a namespace with a handling error should mark the traces with error.", - addNs: corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-example2", - }, - }, - addErr: fmt.Errorf("wanted error"), - maxRetries: 1, - expNS: "test-example2", - expTraces: 2, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - assert := assert.New(t) - controllerStopperC := make(chan struct{}) - done := make(chan struct{}) - - // Mocks kubernetes client. - mc := &fake.Clientset{} - onKubeClientListNamespaceReturn(mc, &corev1.NamespaceList{ - ListMeta: metav1.ListMeta{ - ResourceVersion: "1", - }, - Items: []corev1.Namespace{test.addNs}}) - - // Mock our handler. - mh := &mhandler.Handler{} - mh.On("Add", mock.Anything, mock.Anything).Return(test.addErr).Run(func(args mock.Arguments) { - // Check the received context has a span. - ctx := args.Get(0).(context.Context) - span := opentracing.SpanFromContext(ctx) - assert.NotNil(span) - - // Done. Send signal so we can check spans. - close(done) - }) - - tracer := mocktracer.New() - nsret := newNamespaceRetriever(mc) - cfg := &controller.Config{ - Name: "test-tracing", - ProcessingJobRetries: test.maxRetries, - } - - c := controller.New(cfg, mh, nsret, nil, tracer, nil, log.Dummy) - - // Run Controller in background. - go func() { - c.Run(controllerStopperC) - }() - - // Wait for different results. If no result means error failure. - select { - case <-done: - // Wait until the parent spans are finished. - time.Sleep(2 * time.Millisecond) - - // Check we have the correct number of finished spans. - finishedSpans := tracer.FinishedSpans() - // Process object and add/delete spans. - if assert.Len(finishedSpans, test.expTraces) { - // Get the spans and check correlation, is in finished order, so it should be reversed. - rootSpan := finishedSpans[1] - currentSpan := finishedSpans[0] - rootSpanCtx := rootSpan.Context().(mocktracer.MockSpanContext) - assert.Equal(rootSpanCtx.SpanID, currentSpan.ParentID) - - // Check span operation names. - assert.Equal("processJob", rootSpan.OperationName) - assert.Equal("handleAddObject", currentSpan.OperationName) - - // Check important tags. - if assert.Contains(rootSpan.Tags(), "kubernetes.object.key") { - assert.Equal(test.expNS, rootSpan.Tags()["kubernetes.object.key"]) - } - if assert.Contains(currentSpan.Tags(), "kubernetes.object.key") { - assert.Equal(test.expNS, currentSpan.Tags()["kubernetes.object.key"]) - } - - // Check if error. - if test.addErr != nil { - if assert.Contains(rootSpan.Tags(), "error") { - assert.Equal(true, rootSpan.Tags()["error"]) - } - if assert.Contains(currentSpan.Tags(), "error") { - assert.Equal(true, currentSpan.Tags()["error"]) - } - } - } - case <-time.After(1 * time.Second): - assert.Fail("timeout waiting for controller handling, this could mean the controller is not receiving resources") - } - }) - } -} diff --git a/operator/handler/handler.go b/controller/handler.go similarity index 98% rename from operator/handler/handler.go rename to controller/handler.go index 508cc818..c6a53077 100644 --- a/operator/handler/handler.go +++ b/controller/handler.go @@ -1,4 +1,4 @@ -package handler +package controller import ( "context" diff --git a/operator/controller/leaderelection/leaderelection.go b/controller/leaderelection/leaderelection.go similarity index 100% rename from operator/controller/leaderelection/leaderelection.go rename to controller/leaderelection/leaderelection.go diff --git a/operator/retrieve/retrieve.go b/controller/retrieve.go similarity index 97% rename from operator/retrieve/retrieve.go rename to controller/retrieve.go index 45e5ab2e..50e568b7 100644 --- a/operator/retrieve/retrieve.go +++ b/controller/retrieve.go @@ -1,4 +1,4 @@ -package retrieve +package controller import ( "k8s.io/apimachinery/pkg/runtime" diff --git a/mocks/operator/controller/Controller.go b/mocks/controller/Controller.go similarity index 100% rename from mocks/operator/controller/Controller.go rename to mocks/controller/Controller.go diff --git a/mocks/operator/handler/Handler.go b/mocks/controller/Handler.go similarity index 97% rename from mocks/operator/handler/Handler.go rename to mocks/controller/Handler.go index ed173713..ae12ac23 100644 --- a/mocks/operator/handler/Handler.go +++ b/mocks/controller/Handler.go @@ -1,6 +1,6 @@ // Code generated by mockery v1.0.0. DO NOT EDIT. -package handler +package controller import ( context "context" diff --git a/mocks/doc.go b/mocks/doc.go index e994bb9f..7930419a 100644 --- a/mocks/doc.go +++ b/mocks/doc.go @@ -5,9 +5,8 @@ testing and integration tests whenever is possible. package mocks // import "github.com/spotahome/kooper/mocks" // Operator tooling mocks. -//go:generate mockery -output ./operator/resource -outpkg resource -dir ../operator/resource -name CRD -//go:generate mockery -output ./operator/controller -outpkg controller -dir ../operator/controller -name Controller -//go:generate mockery -output ./operator/handler -outpkg handler -dir ../operator/handler -name Handler +//go:generate mockery -output ./controller -outpkg controller -dir ../controller -name Controller +//go:generate mockery -output ./controller -outpkg controller -dir ../controller -name Handler // Wrappers mocks //go:generate mockery -output ./wrapper/time -outpkg time -dir ../wrapper/time -name Time diff --git a/mocks/operator/resource/CRD.go b/mocks/operator/resource/CRD.go deleted file mode 100644 index e0cbc3ef..00000000 --- a/mocks/operator/resource/CRD.go +++ /dev/null @@ -1,61 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package resource - -import ( - mock "github.com/stretchr/testify/mock" - cache "k8s.io/client-go/tools/cache" - - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// CRD is an autogenerated mock type for the CRD type -type CRD struct { - mock.Mock -} - -// GetListerWatcher provides a mock function with given fields: -func (_m *CRD) GetListerWatcher() cache.ListerWatcher { - ret := _m.Called() - - var r0 cache.ListerWatcher - if rf, ok := ret.Get(0).(func() cache.ListerWatcher); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(cache.ListerWatcher) - } - } - - return r0 -} - -// GetObject provides a mock function with given fields: -func (_m *CRD) GetObject() runtime.Object { - ret := _m.Called() - - var r0 runtime.Object - if rf, ok := ret.Get(0).(func() runtime.Object); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(runtime.Object) - } - } - - return r0 -} - -// Initialize provides a mock function with given fields: -func (_m *CRD) Initialize() error { - ret := _m.Called() - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/operator/controller/config.go b/operator/controller/config.go deleted file mode 100644 index ff08ed95..00000000 --- a/operator/controller/config.go +++ /dev/null @@ -1,36 +0,0 @@ -package controller - -import "time" - -// Defaults. -const ( - defResyncInterval = 30 * time.Second - defConcurrentWorkers = 1 - defProcessingJobRetries = 3 -) - -// Config is the controller configuration. -type Config struct { - // name of the controller. - Name string - // ConcurrentWorkers is the number of concurrent workers the controller will have running processing events. - ConcurrentWorkers int - // ResyncInterval is the interval the controller will process all the selected resources. - ResyncInterval time.Duration - // ProcessingJobRetries is the number of times the job will try to reprocess the event before returning a real error. - ProcessingJobRetries int -} - -func (c *Config) setDefaults() { - if c.ConcurrentWorkers <= 0 { - c.ConcurrentWorkers = defConcurrentWorkers - } - - if c.ResyncInterval <= 0 { - c.ResyncInterval = defResyncInterval - } - - if c.ProcessingJobRetries <= 0 { - c.ProcessingJobRetries = defProcessingJobRetries - } -} diff --git a/operator/controller/generic.go b/operator/controller/generic.go deleted file mode 100644 index e297eeba..00000000 --- a/operator/controller/generic.go +++ /dev/null @@ -1,424 +0,0 @@ -package controller - -import ( - "context" - "fmt" - "reflect" - "sync" - "time" - - opentracing "github.com/opentracing/opentracing-go" - "github.com/opentracing/opentracing-go/ext" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/util/workqueue" - - "github.com/spotahome/kooper/log" - "github.com/spotahome/kooper/monitoring/metrics" - "github.com/spotahome/kooper/operator/controller/leaderelection" - "github.com/spotahome/kooper/operator/handler" - "github.com/spotahome/kooper/operator/retrieve" -) - -// Span tag and log keys. -const ( - kubernetesObjectKeyKey = "kubernetes.object.key" - kubernetesObjectNSKey = "kubernetes.object.namespace" - kubernetesObjectNameKey = "kubernetes.object.name" - eventKey = "event" - kooperControllerKey = "kooper.controller" - processedTimesKey = "kubernetes.object.total_processed_times" - retriesRemainingKey = "kubernetes.object.retries_remaining" - processingRetryKey = "kubernetes.object.processing_retry" - retriesExecutedKey = "kubernetes.object.retries_consumed" - controllerNameKey = "controller.cfg.name" - controllerResyncKey = "controller.cfg.resync_interval" - controllerMaxRetriesKey = "controller.cfg.max_retries" - controllerConcurrentWorkersKey = "controller.cfg.concurrent_workers" - successKey = "success" - messageKey = "message" -) - -// generic controller is a controller that can be used to create different kind of controllers. -type generic struct { - queue workqueue.RateLimitingInterface // queue will have the jobs that the controller will get and send to handlers. - informer cache.SharedIndexInformer // informer will notify be inform us about resource changes. - handler handler.Handler // handler is where the logic of resource processing. - running bool - runningMu sync.Mutex - cfg Config - tracer opentracing.Tracer // use directly opentracing API because it's not an implementation. - metrics metrics.Recorder - leRunner leaderelection.Runner - logger log.Logger -} - -// NewSequential creates a new controller that will process the received events sequentially. -// This constructor is just a wrapper to help bootstrapping default sequential controller. -func NewSequential(resync time.Duration, handler handler.Handler, retriever retrieve.Retriever, metricRecorder metrics.Recorder, logger log.Logger) Controller { - cfg := &Config{ - ConcurrentWorkers: 1, - ResyncInterval: resync, - } - return New(cfg, handler, retriever, nil, nil, metricRecorder, logger) -} - -// NewConcurrent creates a new controller that will process the received events concurrently. -// This constructor is just a wrapper to help bootstrapping default concurrent controller. -func NewConcurrent(concurrentWorkers int, resync time.Duration, handler handler.Handler, retriever retrieve.Retriever, metricRecorder metrics.Recorder, logger log.Logger) (Controller, error) { - if concurrentWorkers < 2 { - return nil, fmt.Errorf("%d is not a valid concurrency workers ammount for a concurrent controller", concurrentWorkers) - } - - cfg := &Config{ - ConcurrentWorkers: concurrentWorkers, - ResyncInterval: resync, - } - return New(cfg, handler, retriever, nil, nil, metricRecorder, logger), nil -} - -// New creates a new controller that can be configured using the cfg parameter. -func New(cfg *Config, handler handler.Handler, retriever retrieve.Retriever, leaderElector leaderelection.Runner, tracer opentracing.Tracer, metricRecorder metrics.Recorder, logger log.Logger) Controller { - // Sets the required default configuration. - cfg.setDefaults() - - // Default logger. - if logger == nil { - logger = &log.Std{} - logger.Warningf("no logger specified, fallback to default logger, to disable logging use dummy logger") - } - - // Default metrics recorder. - if metricRecorder == nil { - metricRecorder = metrics.Dummy - logger.Warningf("no metrics recorder specified, disabling metrics") - } - - // Default tracer. - if tracer == nil { - tracer = &opentracing.NoopTracer{} - } - - // If no name on controller do our best to infer a name based on the handler. - if cfg.Name == "" { - cfg.Name = reflect.TypeOf(handler).String() - logger.Warningf("controller name not provided, it should have a name, fallback name to: %s", cfg.Name) - } - - // Create the queue that will have our received job changes. It's rate limited so we don't have problems when - // a job processing errors every time is processed in a loop. - queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()) - - // store is the internal cache where objects will be store. - store := cache.Indexers{} - informer := cache.NewSharedIndexInformer(retriever.GetListerWatcher(), retriever.GetObject(), cfg.ResyncInterval, store) - - // Set up our informer event handler. - // Objects are already in our local store. Add only keys/jobs on the queue so they can bre processed - // afterwards. - informer.AddEventHandlerWithResyncPeriod(cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - key, err := cache.MetaNamespaceKeyFunc(obj) - if err == nil { - queue.Add(key) - metricRecorder.IncResourceEventQueued(cfg.Name, metrics.AddEvent) - } - }, - UpdateFunc: func(old interface{}, new interface{}) { - key, err := cache.MetaNamespaceKeyFunc(new) - if err == nil { - queue.Add(key) - metricRecorder.IncResourceEventQueued(cfg.Name, metrics.AddEvent) - } - }, - DeleteFunc: func(obj interface{}) { - key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) - if err == nil { - queue.Add(key) - metricRecorder.IncResourceEventQueued(cfg.Name, metrics.DeleteEvent) - } - }, - }, cfg.ResyncInterval) - - // Create our generic controller object. - return &generic{ - queue: queue, - informer: informer, - logger: logger, - metrics: metricRecorder, - tracer: tracer, - handler: handler, - leRunner: leaderElector, - cfg: *cfg, - } -} - -func (g *generic) isRunning() bool { - g.runningMu.Lock() - defer g.runningMu.Unlock() - return g.running -} - -func (g *generic) setRunning(running bool) { - g.runningMu.Lock() - defer g.runningMu.Unlock() - g.running = running -} - -// Run will run the controller. -func (g *generic) Run(stopC <-chan struct{}) error { - // Check if leader election is required. - if g.leRunner != nil { - return g.leRunner.Run(func() error { - return g.run(stopC) - }) - } - - return g.run(stopC) -} - -// run is the real run of the controller. -func (g *generic) run(stopC <-chan struct{}) error { - if g.isRunning() { - return fmt.Errorf("controller already running") - } - - g.logger.Infof("starting controller") - // Set state of controller. - g.setRunning(true) - defer g.setRunning(false) - - // Shutdown when Run is stopped so we can process the last items and the queue doesn't - // accept more jobs. - defer g.queue.ShutDown() - - // Run the informer so it starts listening to resource events. - go g.informer.Run(stopC) - - // Wait until our store, jobs... stuff is synced (first list on resource, resources on store and jobs on queue). - if !cache.WaitForCacheSync(stopC, g.informer.HasSynced) { - return fmt.Errorf("timed out waiting for caches to sync") - } - - // Start our resource processing worker, if finishes then restart the worker. The workers should - // not end. - for i := 0; i < g.cfg.ConcurrentWorkers; i++ { - go func() { - wait.Until(g.runWorker, time.Second, stopC) - }() - } - - // Until will be running our workers in a continuous way (and re run if they fail). But - // when stop signal is received we must stop. - <-stopC - g.logger.Infof("stopping controller") - - return nil -} - -// runWorker will start a processing loop on event queue. -func (g *generic) runWorker() { - for { - // Process newxt queue job, if needs to stop processing it will return true. - if g.getAndProcessNextJob() { - break - } - } -} - -// getAndProcessNextJob job will process the next job of the queue job and returns if -// it needs to stop processing. -func (g *generic) getAndProcessNextJob() bool { - // Get next job. - nextJob, exit := g.queue.Get() - if exit { - return true - } - defer g.queue.Done(nextJob) - key := nextJob.(string) - - // Our root span will start here. - span := g.tracer.StartSpan("processJob") - defer span.Finish() - ctx := opentracing.ContextWithSpan(context.Background(), span) - g.setRootSpanInfo(key, span) - - // Process the job. If errors then enqueue again. - if err := g.processJob(ctx, key); err == nil { - g.queue.Forget(key) - g.setForgetSpanInfo(key, span, err) - } else if g.queue.NumRequeues(key) < g.cfg.ProcessingJobRetries { - // Job processing failed, requeue. - g.logger.Warningf("error processing %s job (requeued): %v", key, err) - g.queue.AddRateLimited(key) - g.metrics.IncResourceEventQueued(g.cfg.Name, metrics.RequeueEvent) - g.setReenqueueSpanInfo(key, span, err) - } else { - g.logger.Errorf("Error processing %s: %v", key, err) - g.queue.Forget(key) - g.setForgetSpanInfo(key, span, err) - } - - return false -} - -// processJob is where the real processing logic of the item is. -func (g *generic) processJob(ctx context.Context, key string) error { - // Get the object - obj, exists, err := g.informer.GetIndexer().GetByKey(key) - if err != nil { - return err - } - - // handle the object. - if !exists { // Deleted resource from the cache. - return g.handleDelete(ctx, key) - } - - return g.handleAdd(ctx, key, obj.(runtime.Object)) -} - -func (g *generic) handleAdd(ctx context.Context, objKey string, obj runtime.Object) error { - start := time.Now() - g.metrics.IncResourceEventProcessed(g.cfg.Name, metrics.AddEvent) - defer func() { - g.metrics.ObserveDurationResourceEventProcessed(g.cfg.Name, metrics.AddEvent, start) - }() - - // Create the span. - pSpan := opentracing.SpanFromContext(ctx) - span := g.tracer.StartSpan("handleAddObject", opentracing.ChildOf(pSpan.Context())) - ctx = opentracing.ContextWithSpan(ctx, span) - defer span.Finish() - - // Set span data. - ext.SpanKindConsumer.Set(span) - span.SetTag(kubernetesObjectKeyKey, objKey) - g.setCommonSpanInfo(span) - span.LogKV( - eventKey, "add", - kubernetesObjectKeyKey, objKey, - ) - - // Handle the job. - if err := g.handler.Add(ctx, obj); err != nil { - ext.Error.Set(span, true) // Mark error as true. - span.LogKV( - eventKey, "error", - messageKey, err, - ) - - g.metrics.IncResourceEventProcessedError(g.cfg.Name, metrics.AddEvent) - return err - } - return nil -} - -func (g *generic) handleDelete(ctx context.Context, objKey string) error { - start := time.Now() - g.metrics.IncResourceEventProcessed(g.cfg.Name, metrics.DeleteEvent) - defer func() { - g.metrics.ObserveDurationResourceEventProcessed(g.cfg.Name, metrics.DeleteEvent, start) - }() - - // Create the span. - pSpan := opentracing.SpanFromContext(ctx) - span := g.tracer.StartSpan("handleDeleteObject", opentracing.ChildOf(pSpan.Context())) - ctx = opentracing.ContextWithSpan(ctx, span) - defer span.Finish() - - // Set span data. - ext.SpanKindConsumer.Set(span) - span.SetTag(kubernetesObjectKeyKey, objKey) - g.setCommonSpanInfo(span) - span.LogKV( - eventKey, "delete", - kubernetesObjectKeyKey, objKey, - ) - - // Handle the job. - if err := g.handler.Delete(ctx, objKey); err != nil { - ext.Error.Set(span, true) // Mark error as true. - span.LogKV( - eventKey, "error", - messageKey, err, - ) - - g.metrics.IncResourceEventProcessedError(g.cfg.Name, metrics.DeleteEvent) - return err - } - return nil -} - -func (g *generic) setCommonSpanInfo(span opentracing.Span) { - ext.Component.Set(span, "kooper") - span.SetTag(kooperControllerKey, g.cfg.Name) - span.SetTag(controllerNameKey, g.cfg.Name) - span.SetTag(controllerResyncKey, g.cfg.ResyncInterval) - span.SetTag(controllerMaxRetriesKey, g.cfg.ProcessingJobRetries) - span.SetTag(controllerConcurrentWorkersKey, g.cfg.ConcurrentWorkers) -} - -func (g *generic) setRootSpanInfo(key string, span opentracing.Span) { - numberRetries := g.queue.NumRequeues(key) - - // Try to set the namespace and resource name. - if ns, name, err := cache.SplitMetaNamespaceKey(key); err == nil { - span.SetTag(kubernetesObjectNSKey, ns) - span.SetTag(kubernetesObjectNameKey, name) - } - - g.setCommonSpanInfo(span) - span.SetTag(kubernetesObjectKeyKey, key) - span.SetTag(processedTimesKey, numberRetries+1) - span.SetTag(processingRetryKey, numberRetries > 0) - span.SetBaggageItem(kubernetesObjectKeyKey, key) - ext.SpanKindConsumer.Set(span) - span.LogKV( - eventKey, "process_object", - kubernetesObjectKeyKey, key, - ) -} - -func (g *generic) setReenqueueSpanInfo(key string, span opentracing.Span, err error) { - // Mark root span with error. - ext.Error.Set(span, true) - span.LogKV( - eventKey, "error", - messageKey, err, - ) - - rt := g.queue.NumRequeues(key) - span.LogKV( - eventKey, "reenqueued", - retriesRemainingKey, g.cfg.ProcessingJobRetries-rt, - retriesExecutedKey, rt, - kubernetesObjectKeyKey, key, - ) - span.LogKV(successKey, false) -} - -func (g *generic) setForgetSpanInfo(key string, span opentracing.Span, err error) { - success := true - message := "object processed correctly" - - // Error data. - if err != nil { - // Mark root span with error. - ext.Error.Set(span, true) - span.LogKV( - eventKey, "error", - messageKey, err, - ) - success = false - message = "max number of retries reached after failing, forgetting object key" - } - - span.LogKV( - eventKey, "forget", - messageKey, message, - kubernetesObjectKeyKey, key, - ) - span.LogKV(successKey, success) -} diff --git a/operator/doc.go b/operator/doc.go deleted file mode 100644 index 057f70ea..00000000 --- a/operator/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -// Package operator contains implementations and definitions to create Kubernetes operators -// and controllers -package operator // import "github.com/spotahome/kooper/operator" diff --git a/operator/handler/common.go b/operator/handler/common.go deleted file mode 100644 index e175ee94..00000000 --- a/operator/handler/common.go +++ /dev/null @@ -1,34 +0,0 @@ -package handler - -import ( - "context" - - "k8s.io/apimachinery/pkg/runtime" - - "github.com/spotahome/kooper/log" -) - -// Logger will log the handling events. This handler can be sued to test that a -// controller receives resource events.. -type Logger struct { - logger log.Logger -} - -// NewLogger returns a new logger. -func NewLogger(logger log.Logger) *Logger { - return &Logger{ - logger: logger, - } -} - -// Add satisfies Handler interface. -func (l *Logger) Add(_ context.Context, obj runtime.Object) error { - l.logger.Infof("event add: %#v", obj) - return nil -} - -// Delete satisfies Handler interface. -func (l *Logger) Delete(_ context.Context, objKey string) error { - l.logger.Infof("event delete: %#v", objKey) - return nil -} diff --git a/operator/operator.go b/operator/operator.go deleted file mode 100644 index 8cfa74fa..00000000 --- a/operator/operator.go +++ /dev/null @@ -1,172 +0,0 @@ -package operator - -import ( - "fmt" - "sync" - "time" - - "golang.org/x/sync/errgroup" - - "github.com/spotahome/kooper/log" - "github.com/spotahome/kooper/operator/controller" - "github.com/spotahome/kooper/operator/resource" -) - -const ( - tiemoutInitialization = 1 * time.Minute -) - -// Operator is a controller, at code level have almost same contract of behaviour -// but at a higher level it need to initialize some resources (usually CRDs) before -// start its execution. -type Operator interface { - // Initialize knows how to initialize the resources. - Initialize() error - controller.Controller -} - -// simpleOperator is an operator that initializes CRDs before starting -// the execution of controllers. -type simpleOperator struct { - crds []resource.CRD - controllers []controller.Controller - initialized bool - running bool - stateMu sync.Mutex - logger log.Logger -} - -// NewOperator will return an operator that only manages one CRD -// and one Controller. -func NewOperator(crd resource.CRD, ctrlr controller.Controller, logger log.Logger) Operator { - return NewMultiOperator([]resource.CRD{crd}, []controller.Controller{ctrlr}, logger) -} - -// NewMultiOperator returns an operator that has multiple CRDs and controllers. -func NewMultiOperator(crds []resource.CRD, ctrlrs []controller.Controller, logger log.Logger) Operator { - return &simpleOperator{ - crds: crds, - controllers: ctrlrs, - logger: logger, - } -} - -// Initialize will initializer all the CRDs and return. Satisfies Operator interface. -func (s *simpleOperator) Initialize() error { - if s.isInitialized() { - return nil - } - - // Initialize CRDs. - var g errgroup.Group - for _, crd := range s.crds { - crd := crd - g.Go(func() error { - return crd.Initialize() - }) - } - - // Wait until everything is initialized. - errC := make(chan error) - go func() { - errC <- g.Wait() - }() - - select { - case err := <-errC: - if err != nil { - return err - } - case <-time.After(tiemoutInitialization): - return fmt.Errorf("timeout initializing operator") - } - - // All ok, we are ready to run. - s.logger.Infof("operator initialized") - s.setInitialized(true) - return nil -} - -// Run will run the operator (a.k.a) the controllers and Initialize the CRDs. -// It's a blocking operation. Satisfies Operator interface. The client that uses an operator -// has the responsibility of closing the stop channel if the operator ends execution -// unexpectly so all the goroutines (controllers running) end its execution -func (s *simpleOperator) Run(stopC <-chan struct{}) error { - if s.isRunning() { - return fmt.Errorf("operator already running") - } - - s.logger.Infof("starting operator") - s.setRunning(true) - defer s.setRunning(false) - - if err := s.Initialize(); err != nil { - return err - } - - errC := make(chan error) - go func() { - errC <- s.runAllControllers(stopC) - }() - - // When stop signal is received we must stop or when an error is received from - // one of the controllers. - select { - case err := <-errC: - if err != nil { - return err - } - case <-stopC: - } - s.logger.Infof("stopping operator") - return nil -} - -// runAllControllers will run controllers and block execution. -func (s *simpleOperator) runAllControllers(stopC <-chan struct{}) error { - errC := make(chan error) - - for _, ctrl := range s.controllers { - ctrl := ctrl - go func() { - errC <- ctrl.Run(stopC) - }() - } - - // Wait until any of the controllers end execution. All the controllers should be executing so - // if we receive any result (error or not) we should return an error - select { - case <-stopC: - return nil - case err := <-errC: - if err != nil { - return fmt.Errorf("a controller ended with an error: %s", err) - } - s.logger.Warningf("a controller stopped execution without error before operator received stop signal, stopping operator.") - return nil - } -} - -func (s *simpleOperator) isInitialized() bool { - s.stateMu.Lock() - defer s.stateMu.Unlock() - return s.initialized -} - -func (s *simpleOperator) setInitialized(value bool) { - s.stateMu.Lock() - defer s.stateMu.Unlock() - s.initialized = value -} - -func (s *simpleOperator) isRunning() bool { - s.stateMu.Lock() - defer s.stateMu.Unlock() - return s.running -} - -func (s *simpleOperator) setRunning(value bool) { - s.stateMu.Lock() - defer s.stateMu.Unlock() - s.running = value -} diff --git a/operator/operator_test.go b/operator/operator_test.go deleted file mode 100644 index 05ff2ab5..00000000 --- a/operator/operator_test.go +++ /dev/null @@ -1,220 +0,0 @@ -package operator_test - -import ( - "errors" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - - "github.com/spotahome/kooper/log" - mcontroller "github.com/spotahome/kooper/mocks/operator/controller" - mresource "github.com/spotahome/kooper/mocks/operator/resource" - "github.com/spotahome/kooper/operator" - "github.com/spotahome/kooper/operator/controller" - "github.com/spotahome/kooper/operator/resource" -) - -func TestMultiOperatorInitialization(t *testing.T) { - tests := []struct { - name string - errInit bool - expErr bool - }{ - { - name: "Calling multiple initializations should only ininitialize the once.", - errInit: false, - expErr: false, - }, - { - name: "Error on initialization should return error.", - errInit: true, - expErr: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - assert := assert.New(t) - qCRD := 5 - - var errInit error - if test.errInit { - errInit = errors.New("wanted error") - } - - // Mocks. - mcrds := []resource.CRD{} - for i := 0; i < qCRD; i++ { - crd := &mresource.CRD{} - crd.On("Initialize").Once().Return(errInit) - mcrds = append(mcrds, crd) - } - - // Operator. - op := operator.NewMultiOperator(mcrds, nil, log.Dummy) - err := op.Initialize() - - if test.expErr { - assert.Error(err) - } else { - assert.NoError(err) - assert.NoError(op.Initialize(), "calling multiple times after initialization should not error") - } - for _, crd := range mcrds { - mcrd := crd.(*mresource.CRD) - mcrd.AssertExpectations(t) - } - }) - } -} - -// controllerBehaviour is the way of controlling the beehaviour expected of a controller. -type controllerBehaviour struct { - returnErr bool - returnAfter time.Duration -} - -func createControllerMocks(cb []*controllerBehaviour) []controller.Controller { - mctrls := make([]controller.Controller, len(cb)) - - for i, b := range cb { - // If we need to return an error return an error. - var ctrlErr error - if b.returnErr { - ctrlErr = errors.New("wanted error") - } - - ctrl := &mcontroller.Controller{} - c := ctrl.On("Run", mock.Anything).Once().Return(ctrlErr) - - // If we need to wait anytime then delay the return. - if b.returnAfter > 0 { - c.After(b.returnAfter) - } - - mctrls[i] = ctrl - } - return mctrls -} - -func TestMultiOperatorRun(t *testing.T) { - tests := []struct { - name string - controllers []*controllerBehaviour - expErr bool - expEndOfOperator bool - }{ - { - name: "Running the operator should run without error if the controllers dont end.", - controllers: []*controllerBehaviour{ - { - returnErr: false, - returnAfter: 9999 * time.Hour, - }, - { - returnErr: false, - returnAfter: 9999 * time.Hour, - }, - { - returnErr: false, - returnAfter: 9999 * time.Hour, - }, - { - returnErr: false, - returnAfter: 9999 * time.Hour, - }, - }, - expErr: false, - expEndOfOperator: false, - }, - { - name: "Running the operator should end without error if one of the controllers ends.", - controllers: []*controllerBehaviour{ - { - returnErr: false, - returnAfter: 9999 * time.Hour, - }, - { - returnErr: false, - returnAfter: 9999 * time.Hour, - }, - { - returnErr: false, - returnAfter: 0, // The one that ends. - }, - { - returnErr: false, - returnAfter: 9999 * time.Hour, - }, - }, - expErr: false, - expEndOfOperator: true, - }, - { - name: "Running the operator should end with error if one of the controllers ends with an error.", - controllers: []*controllerBehaviour{ - { - returnErr: false, - returnAfter: 9999 * time.Hour, - }, - { - returnErr: false, - returnAfter: 9999 * time.Hour, - }, - { - returnErr: true, - returnAfter: 0, // The one that ends. - }, - { - returnErr: false, - returnAfter: 9999 * time.Hour, - }, - }, - expErr: true, - expEndOfOperator: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - assert := assert.New(t) - operatorStopperC := make(chan struct{}) - - // Mocks. - crd := &mresource.CRD{} - crd.On("Initialize").Return(nil) - mcrds := []resource.CRD{crd} - - mctrls := createControllerMocks(test.controllers) - - // Operator. - op := operator.NewMultiOperator(mcrds, mctrls, log.Dummy) - - // Run in background and wait to the signals so it can test. - errC := make(chan error) - go func() { - errC <- op.Run(operatorStopperC) - }() - - // If the operator isn't expected to end we should end on our side. - if !test.expEndOfOperator { - // Wait a bit so the calls to controllers are done and then stop operator. - time.Sleep(10 * time.Millisecond) - close(operatorStopperC) - } - select { - case err := <-errC: - // Check. - if test.expErr { - assert.Error(err) - } else { - assert.NoError(err) - } - case <-time.After(1 * time.Second): - assert.Fail("timeout waiting for controllers execution") - } - }) - } -} diff --git a/operator/resource/crd.go b/operator/resource/crd.go deleted file mode 100644 index 0fa4f682..00000000 --- a/operator/resource/crd.go +++ /dev/null @@ -1,12 +0,0 @@ -package resource - -import ( - "github.com/spotahome/kooper/operator/retrieve" -) - -// CRD represents a non stadandard resource or custom resource definition. -type CRD interface { - retrieve.Retriever - // Initialize knows how to ensure that the CRD is initialized. - Initialize() error -} From fc08518e4b9da684bc17653ca6a9d8695be6b36b Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Wed, 18 Mar 2020 11:49:16 +0100 Subject: [PATCH 02/31] Fix examples Signed-off-by: Xabier Larrakoetxea --- examples/config-custom-controller/main.go | 41 ++- .../main.go | 82 ++++-- .../echo-pod-controller/controller/echo.go | 26 +- examples/leader-election-controller/main.go | 27 +- examples/metrics-controller/main.go | 45 +-- examples/onefile-echo-pod-controller/main.go | 83 ------ examples/pod-terminator-operator/Makefile | 30 +- .../chaos/v1alpha1/zz_generated.deepcopy.go | 2 +- .../k8s/clientset/versioned/clientset.go | 8 - .../versioned/fake/clientset_generated.go | 12 +- .../typed/chaos/v1alpha1/chaos_client.go | 3 +- examples/pod-terminator-operator/cmd/main.go | 25 +- examples/pod-terminator-operator/go.mod | 14 + examples/pod-terminator-operator/go.sum | 257 +++++++++++++++++ .../chaos.spotahome.com_podterminators.yaml | 74 +++++ .../operator/config.go | 11 - .../pod-terminator-operator/operator/crd.go | 60 ---- .../operator/factory.go | 27 -- .../operator/handler.go | 43 --- .../operator/operator.go | 90 ++++++ examples/traced-controller/fakedservice.go | 33 --- examples/traced-controller/main.go | 265 ------------------ 22 files changed, 613 insertions(+), 645 deletions(-) delete mode 100644 examples/onefile-echo-pod-controller/main.go create mode 100644 examples/pod-terminator-operator/go.mod create mode 100644 examples/pod-terminator-operator/go.sum create mode 100644 examples/pod-terminator-operator/manifests/chaos.spotahome.com_podterminators.yaml delete mode 100644 examples/pod-terminator-operator/operator/config.go delete mode 100644 examples/pod-terminator-operator/operator/crd.go delete mode 100644 examples/pod-terminator-operator/operator/factory.go delete mode 100644 examples/pod-terminator-operator/operator/handler.go create mode 100644 examples/pod-terminator-operator/operator/operator.go delete mode 100644 examples/traced-controller/fakedservice.go delete mode 100644 examples/traced-controller/main.go diff --git a/examples/config-custom-controller/main.go b/examples/config-custom-controller/main.go index 44d2d2ab..e02d1344 100644 --- a/examples/config-custom-controller/main.go +++ b/examples/config-custom-controller/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "fmt" "os" "path/filepath" "time" @@ -17,13 +18,11 @@ import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" + "github.com/spotahome/kooper/controller" "github.com/spotahome/kooper/log" - "github.com/spotahome/kooper/operator/controller" - "github.com/spotahome/kooper/operator/handler" - "github.com/spotahome/kooper/operator/retrieve" ) -func main() { +func run() error { // Initialize logger. log := &log.Std{} @@ -34,18 +33,16 @@ func main() { kubehome := filepath.Join(homedir.HomeDir(), ".kube", "config") k8scfg, err = clientcmd.BuildConfigFromFlags("", kubehome) if err != nil { - log.Errorf("error loading kubernetes configuration: %s", err) - os.Exit(1) + return fmt.Errorf("error loading kubernetes configuration: %w", err) } } k8scli, err := kubernetes.NewForConfig(k8scfg) if err != nil { - log.Errorf("error creating kubernetes client: %s", err) - os.Exit(1) + return fmt.Errorf("error creating kubernetes client: %w", err) } // Create our retriever so the controller knows how to get/listen for pod events. - retr := &retrieve.Resource{ + retr := &controller.Resource{ Object: &corev1.Pod{}, ListerWatcher: &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { @@ -58,7 +55,7 @@ func main() { } // Our domain logic that will print every add/sync/update and delete event we . - hand := &handler.HandlerFunc{ + hand := &controller.HandlerFunc{ AddFunc: func(_ context.Context, obj runtime.Object) error { pod := obj.(*corev1.Pod) log.Infof("Pod added: %s/%s", pod.Namespace, pod.Name) @@ -72,17 +69,35 @@ func main() { // Create the controller with custom configuration. cfg := &controller.Config{ + Handler: hand, + Retriever: retr, + Logger: log, + ProcessingJobRetries: 5, ResyncInterval: 45 * time.Second, ConcurrentWorkers: 1, } - ctrl := controller.New(cfg, hand, retr, nil, nil, nil, log) + ctrl, err := controller.New(cfg) + if err != nil { + return fmt.Errorf("could not create controller: %w", err) + } // Start our controller. stopC := make(chan struct{}) - if err := ctrl.Run(stopC); err != nil { - log.Errorf("error running controller: %s", err) + err = ctrl.Run(stopC) + if err != nil { + return fmt.Errorf("error running controller: %w", err) + } + + return nil +} + +func main() { + err := run() + if err != nil { + fmt.Fprintf(os.Stderr, "error running app: %s", err) os.Exit(1) } + os.Exit(0) } diff --git a/examples/controller-concurrency-handling-example/main.go b/examples/controller-concurrency-handling-example/main.go index dfc62c94..16812847 100644 --- a/examples/controller-concurrency-handling-example/main.go +++ b/examples/controller-concurrency-handling-example/main.go @@ -3,6 +3,7 @@ package main import ( "context" "flag" + "fmt" "os" "path/filepath" "time" @@ -19,35 +20,57 @@ import ( "k8s.io/client-go/util/homedir" "github.com/spotahome/kooper/log" - "github.com/spotahome/kooper/operator/controller" - "github.com/spotahome/kooper/operator/handler" - "github.com/spotahome/kooper/operator/retrieve" + "github.com/spotahome/kooper/controller" ) var ( concurrentWorkers int sleepMS int + intervalS int + retries int ) func initFlags() error { fg := flag.NewFlagSet(os.Args[0], flag.ExitOnError) - fg.IntVar(&concurrentWorkers, "concurrency", 0, "The number of concurrent event handling") + fg.IntVar(&concurrentWorkers, "concurrency", 3, "The number of concurrent event handling") fg.IntVar(&sleepMS, "sleep-ms", 25, "The number of milliseconds to sleep on each event handling") - return fg.Parse(os.Args[1:]) + fg.IntVar(&intervalS, "interval-s", 300, "The number of seconds to for reconciliation loop intervals") + fg.IntVar(&retries, "retries", 3, "The number of retries in case of error") + + err := fg.Parse(os.Args[1:]) + if err != nil { + return err + } + + if concurrentWorkers < 1 { + concurrentWorkers = 1 + } + + if sleepMS < 1 { + sleepMS = 25 + } + + if intervalS < 1 { + intervalS = 300 + } + if retries < 0 { + retries = 0 + } + + return nil } func sleep() { time.Sleep(time.Duration(sleepMS) * time.Millisecond) } -func main() { +func run() error { // Initialize logger. log := &log.Std{} // Init flags. if err := initFlags(); err != nil { - log.Errorf("error parsing arguments: %s", err) - os.Exit(1) + return fmt.Errorf("error parsing arguments: %w", err) } // Get k8s client. @@ -57,18 +80,16 @@ func main() { kubehome := filepath.Join(homedir.HomeDir(), ".kube", "config") k8scfg, err = clientcmd.BuildConfigFromFlags("", kubehome) if err != nil { - log.Errorf("error loading kubernetes configuration: %s", err) - os.Exit(1) + return fmt.Errorf("error loading kubernetes configuration: %w", err) } } k8scli, err := kubernetes.NewForConfig(k8scfg) if err != nil { - log.Errorf("error creating kubernetes client: %s", err) - os.Exit(1) + return fmt.Errorf("error creating kubernetes client: %w", err) } // Create our retriever so the controller knows how to get/listen for pod events. - retr := &retrieve.Resource{ + retr := &controller.Resource{ Object: &corev1.Pod{}, ListerWatcher: &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { @@ -81,7 +102,7 @@ func main() { } // Our domain logic that will print every add/sync/update and delete event we . - hand := &handler.HandlerFunc{ + hand := &controller.HandlerFunc{ AddFunc: func(_ context.Context, obj runtime.Object) error { pod := obj.(*corev1.Pod) sleep() @@ -96,20 +117,35 @@ func main() { } // Create the controller that will refresh every 30 seconds. - var ctrl controller.Controller - if concurrentWorkers < 2 { - log.Infof("sequential controller created") - ctrl = controller.NewSequential(30*time.Second, hand, retr, nil, log) - } else { - log.Infof("sequential controller created") - ctrl, _ = controller.NewConcurrent(concurrentWorkers, 30*time.Second, hand, retr, nil, log) + cfg := &controller.Config{ + Handler: hand, + Retriever: retr, + Logger: log, + + ProcessingJobRetries: retries, + ResyncInterval: time.Duration(intervalS) * time.Second, + ConcurrentWorkers: concurrentWorkers, + } + ctrl, err := controller.New(cfg) + if err != nil { + return fmt.Errorf("could not create controller: %w", err) } // Start our controller. stopC := make(chan struct{}) if err := ctrl.Run(stopC); err != nil { - log.Errorf("error running controller: %s", err) + return fmt.Errorf("error running controller: %w", err) + } + + return nil +} + +func main() { + err := run() + if err != nil { + fmt.Fprintf(os.Stderr, "error running app: %s", err) os.Exit(1) } + os.Exit(0) -} +} \ No newline at end of file diff --git a/examples/echo-pod-controller/controller/echo.go b/examples/echo-pod-controller/controller/echo.go index d636e2a8..d6038fa0 100644 --- a/examples/echo-pod-controller/controller/echo.go +++ b/examples/echo-pod-controller/controller/echo.go @@ -3,7 +3,7 @@ package controller import ( "context" - "github.com/spotahome/kooper/operator/controller" + "github.com/spotahome/kooper/controller" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" @@ -11,28 +11,22 @@ import ( "github.com/spotahome/kooper/examples/echo-pod-controller/service" ) -// Controller is a controller that echoes pod events. -type Controller struct { - controller.Controller - - config Config - logger log.Logger -} - // New returns a new Echo controller. -func New(config Config, k8sCli kubernetes.Interface, logger log.Logger) (*Controller, error) { +func New(config Config, k8sCli kubernetes.Interface, logger log.Logger) (controller.Controller, error) { ret := NewPodRetrieve(config.Namespace, k8sCli) echoSrv := service.NewSimpleEcho(logger) handler := &handler{echoSrv: echoSrv} - ctrl := controller.NewSequential(config.ResyncPeriod, handler, ret, nil, logger) + cfg := &controller.Config{ + Handler: handler, + Retriever: ret, + Logger: logger, + + ResyncInterval: config.ResyncPeriod, + } - return &Controller{ - Controller: ctrl, - config: config, - logger: logger, - }, nil + return controller.New(cfg) } const ( diff --git a/examples/leader-election-controller/main.go b/examples/leader-election-controller/main.go index 5c4dbc3a..7e1dbe6e 100644 --- a/examples/leader-election-controller/main.go +++ b/examples/leader-election-controller/main.go @@ -21,11 +21,9 @@ import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" + "github.com/spotahome/kooper/controller" + "github.com/spotahome/kooper/controller/leaderelection" "github.com/spotahome/kooper/log" - "github.com/spotahome/kooper/operator/controller" - "github.com/spotahome/kooper/operator/controller/leaderelection" - "github.com/spotahome/kooper/operator/handler" - "github.com/spotahome/kooper/operator/retrieve" ) const ( @@ -53,8 +51,7 @@ func NewFlags() *Flags { return flags } -// Main runs the main application. -func Main() error { +func run() error { // Flags fl := NewFlags() @@ -64,7 +61,7 @@ func Main() error { // Get k8s client. k8scfg, err := rest.InClusterConfig() if err != nil { - // No in cluster? letr's try locally + // No in cluster? lets try locally kubehome := filepath.Join(homedir.HomeDir(), ".kube", "config") k8scfg, err = clientcmd.BuildConfigFromFlags("", kubehome) if err != nil { @@ -77,7 +74,7 @@ func Main() error { } // Create our retriever so the controller knows how to get/listen for pod events. - retr := &retrieve.Resource{ + retr := &controller.Resource{ Object: &corev1.Pod{}, ListerWatcher: &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { @@ -90,7 +87,7 @@ func Main() error { } // Our domain logic that will print every add/sync/update and delete event we . - hand := &handler.HandlerFunc{ + hand := &controller.HandlerFunc{ AddFunc: func(_ context.Context, obj runtime.Object) error { pod := obj.(*corev1.Pod) logger.Infof("Pod added: %s/%s", pod.Namespace, pod.Name) @@ -110,11 +107,19 @@ func Main() error { // Create the controller and run. cfg := &controller.Config{ + Handler: hand, + Retriever: retr, + LeaderElector: lesvc, + Logger: logger, + ProcessingJobRetries: 5, ResyncInterval: time.Duration(fl.ResyncIntervalSeconds) * time.Second, ConcurrentWorkers: 1, } - ctrl := controller.New(cfg, hand, retr, lesvc, nil, nil, logger) + ctrl, err := controller.New(cfg) + if err != nil { + return fmt.Errorf("error creating controller: %w", err) + } stopC := make(chan struct{}) errC := make(chan error) go func() { @@ -142,7 +147,7 @@ func Main() error { } func main() { - if err := Main(); err != nil { + if err := run(); err != nil { fmt.Fprintf(os.Stderr, "error executing controller: %s", err) os.Exit(1) } diff --git a/examples/metrics-controller/main.go b/examples/metrics-controller/main.go index ac5b5629..98ded27e 100644 --- a/examples/metrics-controller/main.go +++ b/examples/metrics-controller/main.go @@ -23,11 +23,9 @@ import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" + "github.com/spotahome/kooper/controller" "github.com/spotahome/kooper/log" "github.com/spotahome/kooper/monitoring/metrics" - "github.com/spotahome/kooper/operator/controller" - "github.com/spotahome/kooper/operator/handler" - "github.com/spotahome/kooper/operator/retrieve" ) const ( @@ -89,14 +87,13 @@ func getMetricRecorder(backend string, logger log.Logger) (metrics.Recorder, err return nil, fmt.Errorf("wrong metrics backend") } -func main() { +func run() error { // Initialize logger. log := &log.Std{} // Init flags. if err := initFlags(); err != nil { - log.Errorf("error parsing arguments: %s", err) - os.Exit(1) + return fmt.Errorf("error parsing arguments: %w", err) } // Get k8s client. @@ -106,18 +103,16 @@ func main() { kubehome := filepath.Join(homedir.HomeDir(), ".kube", "config") k8scfg, err = clientcmd.BuildConfigFromFlags("", kubehome) if err != nil { - log.Errorf("error loading kubernetes configuration: %s", err) - os.Exit(1) + return fmt.Errorf("error loading kubernetes configuration: %w", err) } } k8scli, err := kubernetes.NewForConfig(k8scfg) if err != nil { - log.Errorf("error creating kubernetes client: %s", err) - os.Exit(1) + return fmt.Errorf("error creating kubernetes client: %w", err) } // Create our retriever so the controller knows how to get/listen for pod events. - retr := &retrieve.Resource{ + retr := &controller.Resource{ Object: &corev1.Pod{}, ListerWatcher: &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { @@ -130,7 +125,7 @@ func main() { } // Our domain logic that will print every add/sync/update and delete event we . - hand := &handler.HandlerFunc{ + hand := &controller.HandlerFunc{ AddFunc: func(_ context.Context, obj runtime.Object) error { sleepRandomly() return errRandomly() @@ -144,19 +139,35 @@ func main() { // Create the controller that will refresh every 30 seconds. m, err := getMetricRecorder(metricsBackend, log) if err != nil { - log.Errorf("errors getting metrics backend: %s", err) - os.Exit(1) + return fmt.Errorf("errors getting metrics backend: %w", err) } cfg := &controller.Config{ - Name: "metricsControllerTest", + Name: "metricsControllerTest", + Handler: hand, + Retriever: retr, + MetricRecorder: m, + Logger: log, + } + ctrl, err := controller.New(cfg) + if err != nil { + return fmt.Errorf("could not create controller: %w", err) } - ctrl := controller.New(cfg, hand, retr, nil, nil, m, log) // Start our controller. stopC := make(chan struct{}) if err := ctrl.Run(stopC); err != nil { - log.Errorf("error running controller: %s", err) + return fmt.Errorf("error running controller: %w", err) + } + + return nil +} + +func main() { + err := run() + if err != nil { + fmt.Fprintf(os.Stderr, "error running app: %s", err) os.Exit(1) } + os.Exit(0) } diff --git a/examples/onefile-echo-pod-controller/main.go b/examples/onefile-echo-pod-controller/main.go deleted file mode 100644 index edbeb3bb..00000000 --- a/examples/onefile-echo-pod-controller/main.go +++ /dev/null @@ -1,83 +0,0 @@ -package main - -import ( - "context" - "os" - "path/filepath" - "time" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes" - _ "k8s.io/client-go/plugin/pkg/client/auth" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/util/homedir" - - "github.com/spotahome/kooper/log" - "github.com/spotahome/kooper/operator/controller" - "github.com/spotahome/kooper/operator/handler" - "github.com/spotahome/kooper/operator/retrieve" -) - -func main() { - // Initialize logger. - log := &log.Std{} - - // Get k8s client. - k8scfg, err := rest.InClusterConfig() - if err != nil { - // No in cluster? letr's try locally - kubehome := filepath.Join(homedir.HomeDir(), ".kube", "config") - k8scfg, err = clientcmd.BuildConfigFromFlags("", kubehome) - if err != nil { - log.Errorf("error loading kubernetes configuration: %s", err) - os.Exit(1) - } - } - k8scli, err := kubernetes.NewForConfig(k8scfg) - if err != nil { - log.Errorf("error creating kubernetes client: %s", err) - os.Exit(1) - } - - // Create our retriever so the controller knows how to get/listen for pod events. - retr := &retrieve.Resource{ - Object: &corev1.Pod{}, - ListerWatcher: &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - return k8scli.CoreV1().Pods("").List(options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - return k8scli.CoreV1().Pods("").Watch(options) - }, - }, - } - - // Our domain logic that will print every add/sync/update and delete event we . - hand := &handler.HandlerFunc{ - AddFunc: func(_ context.Context, obj runtime.Object) error { - pod := obj.(*corev1.Pod) - log.Infof("Pod added: %s/%s", pod.Namespace, pod.Name) - return nil - }, - DeleteFunc: func(_ context.Context, s string) error { - log.Infof("Pod deleted: %s", s) - return nil - }, - } - - // Create the controller that will refresh every 30 seconds. - ctrl := controller.NewSequential(30*time.Second, hand, retr, nil, log) - - // Start our controller. - stopC := make(chan struct{}) - if err := ctrl.Run(stopC); err != nil { - log.Errorf("error running controller: %s", err) - os.Exit(1) - } - os.Exit(0) -} diff --git a/examples/pod-terminator-operator/Makefile b/examples/pod-terminator-operator/Makefile index 1a63bddb..605efdaf 100644 --- a/examples/pod-terminator-operator/Makefile +++ b/examples/pod-terminator-operator/Makefile @@ -1,14 +1,26 @@ -CODE_GENERATOR_IMAGE := quay.io/slok/kube-code-generator:v1.13.5 +CODE_GENERATOR_IMAGE := quay.io/slok/kube-code-generator:v1.15.10 +CRD_GENERATOR_IMAGE := quay.io/slok/kube-code-generator:latest DIRECTORY := $(PWD) +ROOT_DIRECTORY := $(DIRECTORY)/../.. CODE_GENERATOR_PACKAGE := github.com/spotahome/kooper/examples/pod-terminator-operator -generate: +generate: generate-client generate-crd + +generate-client: docker run --rm -it \ - -v $(DIRECTORY):/go/src/$(CODE_GENERATOR_PACKAGE) \ - -e PROJECT_PACKAGE=$(CODE_GENERATOR_PACKAGE) \ - -e CLIENT_GENERATOR_OUT=$(CODE_GENERATOR_PACKAGE)/client/k8s \ - -e APIS_ROOT=$(CODE_GENERATOR_PACKAGE)/apis \ - -e GROUPS_VERSION="chaos:v1alpha1" \ - -e GENERATION_TARGETS="deepcopy,client" \ - $(CODE_GENERATOR_IMAGE) \ No newline at end of file + -v $(DIRECTORY):/go/src/$(CODE_GENERATOR_PACKAGE) \ + -e PROJECT_PACKAGE=$(CODE_GENERATOR_PACKAGE) \ + -e CLIENT_GENERATOR_OUT=$(CODE_GENERATOR_PACKAGE)/client/k8s \ + -e APIS_ROOT=$(CODE_GENERATOR_PACKAGE)/apis \ + -e GROUPS_VERSION="chaos:v1alpha1" \ + -e GENERATION_TARGETS="deepcopy,client" \ + $(CODE_GENERATOR_IMAGE) + +generate-crd: + docker run -it --rm \ + -v $(ROOT_DIRECTORY):/src \ + -e GO_PROJECT_ROOT=/src/examples/pod-terminator-operator \ + -e CRD_TYPES_PATH=/src/examples/pod-terminator-operator/apis \ + -e CRD_OUT_PATH=/src/examples/pod-terminator-operator/manifests \ + $(CRD_GENERATOR_IMAGE) update-crd.sh \ No newline at end of file diff --git a/examples/pod-terminator-operator/apis/chaos/v1alpha1/zz_generated.deepcopy.go b/examples/pod-terminator-operator/apis/chaos/v1alpha1/zz_generated.deepcopy.go index 3062860b..70c55551 100644 --- a/examples/pod-terminator-operator/apis/chaos/v1alpha1/zz_generated.deepcopy.go +++ b/examples/pod-terminator-operator/apis/chaos/v1alpha1/zz_generated.deepcopy.go @@ -55,7 +55,7 @@ func (in *PodTerminator) DeepCopyObject() runtime.Object { func (in *PodTerminatorList) DeepCopyInto(out *PodTerminatorList) { *out = *in out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items *out = make([]PodTerminator, len(*in)) diff --git a/examples/pod-terminator-operator/client/k8s/clientset/versioned/clientset.go b/examples/pod-terminator-operator/client/k8s/clientset/versioned/clientset.go index c611d67f..6ee779a4 100644 --- a/examples/pod-terminator-operator/client/k8s/clientset/versioned/clientset.go +++ b/examples/pod-terminator-operator/client/k8s/clientset/versioned/clientset.go @@ -28,8 +28,6 @@ import ( type Interface interface { Discovery() discovery.DiscoveryInterface ChaosV1alpha1() chaosv1alpha1.ChaosV1alpha1Interface - // Deprecated: please explicitly pick a version if possible. - Chaos() chaosv1alpha1.ChaosV1alpha1Interface } // Clientset contains the clients for groups. Each group has exactly one @@ -44,12 +42,6 @@ func (c *Clientset) ChaosV1alpha1() chaosv1alpha1.ChaosV1alpha1Interface { return c.chaosV1alpha1 } -// Deprecated: Chaos retrieves the default version of ChaosClient. -// Please explicitly pick a version. -func (c *Clientset) Chaos() chaosv1alpha1.ChaosV1alpha1Interface { - return c.chaosV1alpha1 -} - // Discovery retrieves the DiscoveryClient func (c *Clientset) Discovery() discovery.DiscoveryInterface { if c == nil { diff --git a/examples/pod-terminator-operator/client/k8s/clientset/versioned/fake/clientset_generated.go b/examples/pod-terminator-operator/client/k8s/clientset/versioned/fake/clientset_generated.go index f162233f..683fd7ac 100644 --- a/examples/pod-terminator-operator/client/k8s/clientset/versioned/fake/clientset_generated.go +++ b/examples/pod-terminator-operator/client/k8s/clientset/versioned/fake/clientset_generated.go @@ -41,7 +41,7 @@ func NewSimpleClientset(objects ...runtime.Object) *Clientset { } } - cs := &Clientset{} + cs := &Clientset{tracker: o} cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} cs.AddReactor("*", "*", testing.ObjectReaction(o)) cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { @@ -63,20 +63,20 @@ func NewSimpleClientset(objects ...runtime.Object) *Clientset { type Clientset struct { testing.Fake discovery *fakediscovery.FakeDiscovery + tracker testing.ObjectTracker } func (c *Clientset) Discovery() discovery.DiscoveryInterface { return c.discovery } +func (c *Clientset) Tracker() testing.ObjectTracker { + return c.tracker +} + var _ clientset.Interface = &Clientset{} // ChaosV1alpha1 retrieves the ChaosV1alpha1Client func (c *Clientset) ChaosV1alpha1() chaosv1alpha1.ChaosV1alpha1Interface { return &fakechaosv1alpha1.FakeChaosV1alpha1{Fake: &c.Fake} } - -// Chaos retrieves the ChaosV1alpha1Client -func (c *Clientset) Chaos() chaosv1alpha1.ChaosV1alpha1Interface { - return &fakechaosv1alpha1.FakeChaosV1alpha1{Fake: &c.Fake} -} diff --git a/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/chaos_client.go b/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/chaos_client.go index 0257153a..35a10b8c 100644 --- a/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/chaos_client.go +++ b/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/chaos_client.go @@ -21,7 +21,6 @@ package v1alpha1 import ( v1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/apis/chaos/v1alpha1" "github.com/spotahome/kooper/examples/pod-terminator-operator/client/k8s/clientset/versioned/scheme" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" rest "k8s.io/client-go/rest" ) @@ -71,7 +70,7 @@ func setConfigDefaults(config *rest.Config) error { gv := v1alpha1.SchemeGroupVersion config.GroupVersion = &gv config.APIPath = "/apis" - config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() if config.UserAgent == "" { config.UserAgent = rest.DefaultKubernetesUserAgent() diff --git a/examples/pod-terminator-operator/cmd/main.go b/examples/pod-terminator-operator/cmd/main.go index 019e2114..e67044ef 100644 --- a/examples/pod-terminator-operator/cmd/main.go +++ b/examples/pod-terminator-operator/cmd/main.go @@ -7,9 +7,7 @@ import ( "syscall" "time" - "github.com/spotahome/kooper/client/crd" applogger "github.com/spotahome/kooper/log" - apiextensionscli "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/client-go/rest" @@ -42,13 +40,13 @@ func (m *Main) Run(stopC <-chan struct{}) error { m.logger.Infof("initializing pod termination operator") // Get kubernetes rest client. - ptCli, crdCli, k8sCli, err := m.getKubernetesClients() + ptCli, k8sCli, err := m.getKubernetesClients() if err != nil { return err } // Create the operator and run - op, err := operator.New(m.config, ptCli, crdCli, k8sCli, m.logger) + op, err := operator.New(m.config, ptCli, k8sCli, m.logger) if err != nil { return err } @@ -58,7 +56,7 @@ func (m *Main) Run(stopC <-chan struct{}) error { // getKubernetesClients returns all the required clients to communicate with // kubernetes cluster: CRD type client, pod terminator types client, kubernetes core types client. -func (m *Main) getKubernetesClients() (podtermk8scli.Interface, crd.Interface, kubernetes.Interface, error) { +func (m *Main) getKubernetesClients() (podtermk8scli.Interface, kubernetes.Interface, error) { var err error var cfg *rest.Config @@ -66,35 +64,28 @@ func (m *Main) getKubernetesClients() (podtermk8scli.Interface, crd.Interface, k if m.flags.Development { cfg, err = clientcmd.BuildConfigFromFlags("", m.flags.KubeConfig) if err != nil { - return nil, nil, nil, fmt.Errorf("could not load configuration: %s", err) + return nil, nil, fmt.Errorf("could not load configuration: %s", err) } } else { cfg, err = rest.InClusterConfig() if err != nil { - return nil, nil, nil, fmt.Errorf("error loading kubernetes configuration inside cluster, check app is running outside kubernetes cluster or run in development mode: %s", err) + return nil, nil, fmt.Errorf("error loading kubernetes configuration inside cluster, check app is running outside kubernetes cluster or run in development mode: %s", err) } } // Create clients. k8sCli, err := kubernetes.NewForConfig(cfg) if err != nil { - return nil, nil, nil, err + return nil, nil, err } // App CRD k8s types client. ptCli, err := podtermk8scli.NewForConfig(cfg) if err != nil { - return nil, nil, nil, err + return nil, nil, err } - // CRD cli. - aexCli, err := apiextensionscli.NewForConfig(cfg) - if err != nil { - return nil, nil, nil, err - } - crdCli := crd.NewClient(aexCli, m.logger) - - return ptCli, crdCli, k8sCli, nil + return ptCli, k8sCli, nil } func main() { diff --git a/examples/pod-terminator-operator/go.mod b/examples/pod-terminator-operator/go.mod new file mode 100644 index 00000000..a52773e0 --- /dev/null +++ b/examples/pod-terminator-operator/go.mod @@ -0,0 +1,14 @@ +module github.com/spotahome/kooper/examples/pod-terminator-operator + +go 1.14 + +replace github.com/spotahome/kooper => ../../ + +require ( + github.com/spotahome/kooper v0.0.0 + github.com/stretchr/testify v1.4.0 + k8s.io/api v0.15.10 + k8s.io/apiextensions-apiserver v0.15.10 + k8s.io/apimachinery v0.15.12-beta.0 + k8s.io/client-go v0.15.10 +) diff --git a/examples/pod-terminator-operator/go.sum b/examples/pod-terminator-operator/go.sum new file mode 100644 index 00000000..d6bd010b --- /dev/null +++ b/examples/pod-terminator-operator/go.sum @@ -0,0 +1,257 @@ +cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v11.1.2+incompatible h1:viZ3tV5l4gE2Sw0xrasFHytCGtzYCrT+um/rrSQ1BfA= +github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-oidc v0.0.0-20180117170138-065b426bd416/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.0.0-20180108230905-e214231b295a/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda h1:NyywMz59neOoVRFDz+ccfKWxn784fiHMDnZSy6T+JXY= +github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac= +github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.17.2/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8 h1:L9JPKrtsHMQ4VCRQfHvbbHBfB2Urn8xf6QZeXZ+OrN4= +github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v0.0.0-20170330212424-2500245aa611/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jonboulle/clockwork v0.0.0-20141017032234-72f9bd7c4e0c/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= +github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3 h1:EooPXg51Tn+xmWPXJUGCnJhJSpeuMlBmfJVcqIRmmv8= +github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spf13/cobra v0.0.0-20180319062004-c439c4fa0937/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68= +golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20161028155119-f51c12702a4d h1:TnM+PKb3ylGmZvyPXmo9m/wktg7Jn/a/fNmr33HSj8g= +golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20170731182057-09f6ed296fc6/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o= +gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0-20150622162204-20b71e5b60d7/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/square/go-jose.v2 v2.0.0-20180411045311-89060dee6a84/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +k8s.io/api v0.15.10 h1:g6t2OLNjupSeoepE0zlIcvoT6Q+QDfdfEUwk5lwHXAo= +k8s.io/api v0.15.10/go.mod h1:PffiEKNf0aFiv2naEYSGTFHIGA9V8Qwt22DZIAokOzQ= +k8s.io/apiextensions-apiserver v0.15.10 h1:S5CJaYVIceZ58yg/AkyPPS+X2CPLkB6DpY9+pFHoHd0= +k8s.io/apiextensions-apiserver v0.15.10/go.mod h1:W0KZ5vYUt8GuAgEVfRcx4NUBXs/Gp5X76hrjvi7nTZE= +k8s.io/apimachinery v0.15.10/go.mod h1:ZRw+v83FjgEqlzqaBkxL3XB21MSLYdzjsY9Bgxclhdw= +k8s.io/apimachinery v0.15.12-beta.0 h1:aGvobE1kXnMyyAgzsYe6bfyyAcoIy2vqwPtSO/PgGBg= +k8s.io/apimachinery v0.15.12-beta.0/go.mod h1:ZRw+v83FjgEqlzqaBkxL3XB21MSLYdzjsY9Bgxclhdw= +k8s.io/apiserver v0.15.10/go.mod h1:6KNMxtnZD0q3kmaRiBF8xyuBeF/D6HlAHgtOR0CsNBc= +k8s.io/client-go v0.15.10 h1:WyH6P2wgULDTFqqClm8cP1E2ELj2qbrxJGR76/SLBQw= +k8s.io/client-go v0.15.10/go.mod h1:UkLY6FcnDCJhI7sabgHI+WovVUTZKvmcOxFrSLOVr4g= +k8s.io/code-generator v0.15.10/go.mod h1:G8bQwmHm2eafm5bgtX67XDZQ8CWKSGu9DekI+yN4Y5I= +k8s.io/component-base v0.15.10/go.mod h1:cYv0GMIaJQEHd1Pgdva5eBRxu1ZXwi1dyYAM0vRhyW0= +k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68= +k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30 h1:TRb4wNWoBVrH9plmkp2q86FIDppkbrEXdXlxU3a3BMI= +k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +k8s.io/utils v0.0.0-20190221042446-c2654d5206da h1:ElyM7RPonbKnQqOcw7dG2IK5uvQQn3b/WPHqD5mBvP4= +k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= +modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= +sigs.k8s.io/structured-merge-diff v0.0.0-20190302045857-e85c7b244fd2/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/examples/pod-terminator-operator/manifests/chaos.spotahome.com_podterminators.yaml b/examples/pod-terminator-operator/manifests/chaos.spotahome.com_podterminators.yaml new file mode 100644 index 00000000..b7db9ddd --- /dev/null +++ b/examples/pod-terminator-operator/manifests/chaos.spotahome.com_podterminators.yaml @@ -0,0 +1,74 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: podterminators.chaos.spotahome.com +spec: + group: chaos.spotahome.com + names: + kind: PodTerminator + listKind: PodTerminatorList + plural: podterminators + singular: podterminator + scope: Namespaced + validation: + openAPIV3Schema: + description: PodTerminator represents a pod terminator. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' + type: object + spec: + description: 'Specification of the ddesired behaviour of the pod terminator. + More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status' + properties: + dryRun: + description: DryRun will set the killing in dryrun mode or not. + type: boolean + minimumInstances: + description: MinimumInstances is the number of minimum instances that + need to be alive. + format: int32 + type: integer + periodSeconds: + description: PeriodSeconds is how often (in seconds) to perform the + attack. + format: int32 + type: integer + selector: + additionalProperties: + type: string + description: Selector is how the target will be selected. + type: object + terminationPercent: + description: TerminationPercent is the percent of pods that will be + killed randomly. + format: int32 + type: integer + type: object + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/examples/pod-terminator-operator/operator/config.go b/examples/pod-terminator-operator/operator/config.go deleted file mode 100644 index b899667c..00000000 --- a/examples/pod-terminator-operator/operator/config.go +++ /dev/null @@ -1,11 +0,0 @@ -package operator - -import ( - "time" -) - -// Config is the controller configuration. -type Config struct { - // ResyncPeriod is the resync period of the operator. - ResyncPeriod time.Duration -} diff --git a/examples/pod-terminator-operator/operator/crd.go b/examples/pod-terminator-operator/operator/crd.go deleted file mode 100644 index 70afa19c..00000000 --- a/examples/pod-terminator-operator/operator/crd.go +++ /dev/null @@ -1,60 +0,0 @@ -package operator - -import ( - "github.com/spotahome/kooper/client/crd" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/cache" - - chaosv1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/apis/chaos/v1alpha1" - podtermk8scli "github.com/spotahome/kooper/examples/pod-terminator-operator/client/k8s/clientset/versioned" -) - -// podTerminatorCRD is the crd pod terminator. -type podTerminatorCRD struct { - crdCli crd.Interface - kubecCli kubernetes.Interface - podTermCli podtermk8scli.Interface -} - -func newPodTermiantorCRD(podTermCli podtermk8scli.Interface, crdCli crd.Interface, kubeCli kubernetes.Interface) *podTerminatorCRD { - return &podTerminatorCRD{ - crdCli: crdCli, - podTermCli: podTermCli, - kubecCli: kubeCli, - } -} - -// podTerminatorCRD satisfies resource.crd interface. -func (p *podTerminatorCRD) Initialize() error { - crd := crd.Conf{ - Kind: chaosv1alpha1.PodTerminatorKind, - NamePlural: chaosv1alpha1.PodTerminatorNamePlural, - ShortNames: chaosv1alpha1.PodTerminatorShortNames, - Group: chaosv1alpha1.SchemeGroupVersion.Group, - Version: chaosv1alpha1.SchemeGroupVersion.Version, - Scope: chaosv1alpha1.PodTerminatorScope, - Categories: []string{"chaos", "podterm"}, - } - - return p.crdCli.EnsurePresent(crd) -} - -// GetListerWatcher satisfies resource.crd interface (and retrieve.Retriever). -func (p *podTerminatorCRD) GetListerWatcher() cache.ListerWatcher { - return &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - return p.podTermCli.ChaosV1alpha1().PodTerminators().List(options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - return p.podTermCli.ChaosV1alpha1().PodTerminators().Watch(options) - }, - } -} - -// GetObject satisfies resource.crd interface (and retrieve.Retriever). -func (p *podTerminatorCRD) GetObject() runtime.Object { - return &chaosv1alpha1.PodTerminator{} -} diff --git a/examples/pod-terminator-operator/operator/factory.go b/examples/pod-terminator-operator/operator/factory.go deleted file mode 100644 index b2f4fd4f..00000000 --- a/examples/pod-terminator-operator/operator/factory.go +++ /dev/null @@ -1,27 +0,0 @@ -package operator - -import ( - "github.com/spotahome/kooper/client/crd" - "github.com/spotahome/kooper/operator" - "github.com/spotahome/kooper/operator/controller" - "k8s.io/client-go/kubernetes" - - podtermk8scli "github.com/spotahome/kooper/examples/pod-terminator-operator/client/k8s/clientset/versioned" - "github.com/spotahome/kooper/examples/pod-terminator-operator/log" -) - -// New returns pod terminator operator. -func New(cfg Config, podTermCli podtermk8scli.Interface, crdCli crd.Interface, kubeCli kubernetes.Interface, logger log.Logger) (operator.Operator, error) { - - // Create crd. - ptCRD := newPodTermiantorCRD(podTermCli, crdCli, kubeCli) - - // Create handler. - handler := newHandler(kubeCli, logger) - - // Create controller. - ctrl := controller.NewSequential(cfg.ResyncPeriod, handler, ptCRD, nil, logger) - - // Assemble CRD and controller to create the operator. - return operator.NewOperator(ptCRD, ctrl, logger), nil -} diff --git a/examples/pod-terminator-operator/operator/handler.go b/examples/pod-terminator-operator/operator/handler.go deleted file mode 100644 index 49df85ff..00000000 --- a/examples/pod-terminator-operator/operator/handler.go +++ /dev/null @@ -1,43 +0,0 @@ -package operator - -import ( - "context" - "fmt" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes" - - chaosv1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/apis/chaos/v1alpha1" - "github.com/spotahome/kooper/examples/pod-terminator-operator/log" - "github.com/spotahome/kooper/examples/pod-terminator-operator/service/chaos" -) - -// Handler is the pod terminator handler that will handle the -// events received from kubernetes. -type handler struct { - chaosService chaos.Syncer - logger log.Logger -} - -// newHandler returns a new handler. -func newHandler(k8sCli kubernetes.Interface, logger log.Logger) *handler { - return &handler{ - chaosService: chaos.NewChaos(k8sCli, logger), - logger: logger, - } -} - -// Add will ensure that the required pod terminator is running. -func (h *handler) Add(_ context.Context, obj runtime.Object) error { - pt, ok := obj.(*chaosv1alpha1.PodTerminator) - if !ok { - return fmt.Errorf("%v is not a pod terminator object", obj.GetObjectKind()) - } - - return h.chaosService.EnsurePodTerminator(pt) -} - -// Delete will ensure the reuited pod terminator is not running. -func (h *handler) Delete(_ context.Context, name string) error { - return h.chaosService.DeletePodTerminator(name) -} diff --git a/examples/pod-terminator-operator/operator/operator.go b/examples/pod-terminator-operator/operator/operator.go new file mode 100644 index 00000000..7d4280ae --- /dev/null +++ b/examples/pod-terminator-operator/operator/operator.go @@ -0,0 +1,90 @@ +package operator + +import ( + "context" + "fmt" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + "k8s.io/apimachinery/pkg/runtime" + "github.com/spotahome/kooper/controller" + + chaosv1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/apis/chaos/v1alpha1" + podtermk8scli "github.com/spotahome/kooper/examples/pod-terminator-operator/client/k8s/clientset/versioned" + "github.com/spotahome/kooper/examples/pod-terminator-operator/log" + "github.com/spotahome/kooper/examples/pod-terminator-operator/service/chaos" +) + +// Config is the controller configuration. +type Config struct { + // ResyncPeriod is the resync period of the operator. + ResyncPeriod time.Duration +} + + +// New returns pod terminator operator. +func New(cfg Config, podTermCli podtermk8scli.Interface, kubeCli kubernetes.Interface, logger log.Logger) (controller.Controller, error) { + return controller.New(&controller.Config{ + Handler: newHandler(kubeCli, logger), + Retriever: newRetriever(podTermCli), + Logger: logger, + + ResyncInterval: cfg.ResyncPeriod, + }) +} + +type retriever struct { + podTermCli podtermk8scli.Interface +} + +func newRetriever(podTermCli podtermk8scli.Interface) *retriever { + return &retriever{ + podTermCli: podTermCli, + } +} + +// GetListerWatcher satisfies resource.crd interface (and retrieve.Retriever). +func (r retriever) GetListerWatcher() cache.ListerWatcher { + return &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + return r.podTermCli.ChaosV1alpha1().PodTerminators().List(options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + return r.podTermCli.ChaosV1alpha1().PodTerminators().Watch(options) + }, + } +} + +// GetObject satisfies resource.crd interface (and retrieve.Retriever). +func (r retriever) GetObject() runtime.Object { + return &chaosv1alpha1.PodTerminator{} +} + +type handler struct { + chaosService chaos.Syncer + logger log.Logger +} + +func newHandler(k8sCli kubernetes.Interface, logger log.Logger) *handler { + return &handler{ + chaosService: chaos.NewChaos(k8sCli, logger), + logger: logger, + } +} + +func (h handler) Add(_ context.Context, obj runtime.Object) error { + pt, ok := obj.(*chaosv1alpha1.PodTerminator) + if !ok { + return fmt.Errorf("%v is not a pod terminator object", obj.GetObjectKind()) + } + + return h.chaosService.EnsurePodTerminator(pt) +} + + +func (h handler) Delete(_ context.Context, name string) error { + return h.chaosService.DeletePodTerminator(name) +} diff --git a/examples/traced-controller/fakedservice.go b/examples/traced-controller/fakedservice.go deleted file mode 100644 index 76283619..00000000 --- a/examples/traced-controller/fakedservice.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - "context" - "fmt" - "time" - - opentracing "github.com/opentracing/opentracing-go" - "github.com/opentracing/opentracing-go/ext" -) - -type fakeService struct { - tracer opentracing.Tracer -} - -func (f *fakeService) makeOperation(ctx context.Context, opName string, duration time.Duration, failRandomly bool) (context.Context, error) { - - // Create the span. - pSpan := opentracing.SpanFromContext(ctx) - span := f.tracer.StartSpan(opName, opentracing.ChildOf(pSpan.Context())) - newCtx := opentracing.ContextWithSpan(ctx, span) - defer span.Finish() - - // Fail sometimes (crappy and fast version). - var err error - if time.Now().Nanosecond()%7 == 0 { - ext.Error.Set(span, true) - err = fmt.Errorf("randomly failed") - } - - time.Sleep(duration) - return newCtx, err -} diff --git a/examples/traced-controller/main.go b/examples/traced-controller/main.go deleted file mode 100644 index ac429f41..00000000 --- a/examples/traced-controller/main.go +++ /dev/null @@ -1,265 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "io" - "os" - "os/signal" - "path/filepath" - "syscall" - "time" - - opentracing "github.com/opentracing/opentracing-go" - jaeger "github.com/uber/jaeger-client-go" - jaegerconfig "github.com/uber/jaeger-client-go/config" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes" - _ "k8s.io/client-go/plugin/pkg/client/auth" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/util/homedir" - - "github.com/spotahome/kooper/log" - "github.com/spotahome/kooper/operator/controller" - "github.com/spotahome/kooper/operator/handler" - "github.com/spotahome/kooper/operator/retrieve" -) - -// Important. Run a jaeger development instance to see the traces. -// -// docker run --rm -it -p5775:5775/udp -p6831:6831/udp -p6832:6832/udp -p5778:5778 -p16686:16686 -p14268:14268 -p9411:9411 jaegertracing/all-in-one:latest - -const ( - namespaceDef = "default" - controllerNameDef = "traced-pod-controller" - resyncIntervalSecondsDef = 120 -) - -// Flags are the flags of the program. -type Flags struct { - ControllerName string - ResyncIntervalSeconds int - Namespace string -} - -// NewFlags returns the flags of the commandline. -func NewFlags() *Flags { - flags := &Flags{} - fl := flag.NewFlagSet(os.Args[0], flag.ExitOnError) - - fl.IntVar(&flags.ResyncIntervalSeconds, "resync-interval", resyncIntervalSecondsDef, "resync seconds of the controller") - fl.StringVar(&flags.Namespace, "namespace", namespaceDef, "kubernetes namespace where the controller is running") - fl.StringVar(&flags.ControllerName, "controller-name", controllerNameDef, "controller name (service name)") - - fl.Parse(os.Args[1:]) - - return flags -} - -// Main runs the main application. -func Main() error { - // Flags - fl := NewFlags() - - // Initialize logger. - logger := &log.Std{} - - // Get k8s client. - k8scfg, err := rest.InClusterConfig() - if err != nil { - // No in cluster? letr's try locally - kubehome := filepath.Join(homedir.HomeDir(), ".kube", "config") - k8scfg, err = clientcmd.BuildConfigFromFlags("", kubehome) - if err != nil { - return fmt.Errorf("error loading kubernetes configuration: %s", err) - } - } - k8scli, err := kubernetes.NewForConfig(k8scfg) - if err != nil { - return fmt.Errorf("error creating kubernetes client: %s", err) - } - - // Create AWS service. - aws, closer, err := createFakeService("aws") - if err != nil { - return err - } - defer closer.Close() - - // Create Redis service. - redis, closer, err := createFakeService("redis") - if err != nil { - return err - } - defer closer.Close() - - // Create Redis service. - github, closer, err := createFakeService("github") - if err != nil { - return err - } - defer closer.Close() - - // Create controller tracer. - tracer, closer, err := createTracer(fl.ControllerName) - if err != nil { - return err - } - defer closer.Close() - - // Create our retriever so the controller knows how to get/listen for pod events. - retr := &retrieve.Resource{ - Object: &corev1.Pod{}, - ListerWatcher: &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - return k8scli.CoreV1().Pods("").List(options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - return k8scli.CoreV1().Pods("").Watch(options) - }, - }, - } - - // Our domain logic that will print every add/sync/update and delete event we . - hand := &handler.HandlerFunc{ - AddFunc: func(ctx context.Context, obj runtime.Object) error { - pod := obj.(*corev1.Pod) - logger.Infof("Pod added: %s/%s", pod.Namespace, pod.Name) - - // Create span - pSpan := opentracing.SpanFromContext(ctx) - span := tracer.StartSpan("AddFunc", opentracing.ChildOf(pSpan.Context())) - defer span.Finish() - ctx = opentracing.ContextWithSpan(ctx, span) - - // Execute. - if _, err := redis.makeOperation(ctx, "getOrg", 10*time.Millisecond, true); err != nil { - return err - } - if _, err := github.makeOperation(ctx, "getOrg", 75*time.Millisecond, true); err != nil { - return err - } - - if _, err := redis.makeOperation(ctx, "getEc2List", 14*time.Millisecond, true); err != nil { - return err - } - - // AWS stuff - { - var newCtx context.Context - newCtx, err := aws.makeOperation(ctx, "ensureAWSResources", 120*time.Millisecond, true) - if err != nil { - return err - } - - if _, err := aws.makeOperation(newCtx, "getEc2List", 672*time.Millisecond, true); err != nil { - return err - } - - if _, err := aws.makeOperation(newCtx, "getALBs", 232*time.Millisecond, true); err != nil { - return err - } - if _, err := aws.makeOperation(newCtx, "linkSGInALB", 157*time.Millisecond, true); err != nil { - return err - } - } - - if _, err := redis.makeOperation(ctx, "storeResult", 34*time.Millisecond, true); err != nil { - return err - } - - return nil - }, - DeleteFunc: func(ctx context.Context, s string) error { - logger.Infof("Pod deleted: %s", s) - - // Create span - pSpan := opentracing.SpanFromContext(ctx) - span := tracer.StartSpan("DeleteFunc", opentracing.ChildOf(pSpan.Context())) - defer span.Finish() - ctx = opentracing.ContextWithSpan(ctx, span) - - // Execute. - if _, err := aws.makeOperation(ctx, "deleteCloudformationStack", 698*time.Millisecond, true); err != nil { - return err - } - if _, err := redis.makeOperation(ctx, "storeResult", 26*time.Millisecond, true); err != nil { - return err - } - - return nil - }, - } - - // Create the controller and run. - cfg := &controller.Config{ - Name: fl.ControllerName, - ProcessingJobRetries: 5, - ResyncInterval: time.Duration(fl.ResyncIntervalSeconds) * time.Second, - ConcurrentWorkers: 30, - } - ctrl := controller.New(cfg, hand, retr, nil, tracer, nil, logger) - stopC := make(chan struct{}) - errC := make(chan error) - go func() { - errC <- ctrl.Run(stopC) - }() - - sigC := make(chan os.Signal, 1) - signal.Notify(sigC, syscall.SIGTERM, syscall.SIGINT) - - select { - case err := <-errC: - if err != nil { - logger.Infof("controller finished with error: %s", err) - return err - } - logger.Infof("controller finished successfuly") - case s := <-sigC: - logger.Infof("signal %s received", s) - close(stopC) - } - - time.Sleep(5 * time.Second) - - return nil -} - -func createFakeService(service string) (*fakeService, io.Closer, error) { - t, cl, err := createTracer(service) - return &fakeService{ - tracer: t, - }, cl, err -} - -func createTracer(service string) (opentracing.Tracer, io.Closer, error) { - cfg := &jaegerconfig.Configuration{ - ServiceName: service, - Sampler: &jaegerconfig.SamplerConfig{ - Type: "const", - Param: 1, - }, - Reporter: &jaegerconfig.ReporterConfig{ - LogSpans: true, - }, - } - tracer, closer, err := cfg.NewTracer(jaegerconfig.Logger(jaeger.NullLogger)) - if err != nil { - return nil, nil, fmt.Errorf("cannot init Jaeger: %s", err) - } - return tracer, closer, nil -} - -func main() { - if err := Main(); err != nil { - fmt.Fprintf(os.Stderr, "error executing controller: %s", err) - os.Exit(1) - } - os.Exit(0) -} From 07cb9feece06b9df5ba4bb50d84b26e8ab531d74 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Wed, 18 Mar 2020 11:49:44 +0100 Subject: [PATCH 03/31] Clean and fix integration tests after refactor Signed-off-by: Xabier Larrakoetxea --- .../integration/controller/controller_test.go | 21 ++- test/integration/controller/generic_test.go | 30 +-- test/integration/helper/cli/cli.go | 28 +-- test/integration/operator/Makefile | 17 -- .../operator/apis/superhero/doc.go | 5 - .../operator/apis/superhero/v1alpha1/doc.go | 6 - .../apis/superhero/v1alpha1/register.go | 49 ----- .../operator/apis/superhero/v1alpha1/types.go | 37 ---- .../v1alpha1/zz_generated.deepcopy.go | 101 ---------- .../k8s/clientset/versioned/clientset.go | 98 ---------- .../client/k8s/clientset/versioned/doc.go | 20 -- .../versioned/fake/clientset_generated.go | 82 --------- .../k8s/clientset/versioned/fake/doc.go | 20 -- .../k8s/clientset/versioned/fake/register.go | 56 ------ .../k8s/clientset/versioned/scheme/doc.go | 20 -- .../clientset/versioned/scheme/register.go | 56 ------ .../versioned/typed/superhero/v1alpha1/doc.go | 20 -- .../typed/superhero/v1alpha1/fake/doc.go | 20 -- .../superhero/v1alpha1/fake/fake_spiderman.go | 128 ------------- .../v1alpha1/fake/fake_superhero_client.go | 40 ---- .../superhero/v1alpha1/generated_expansion.go | 21 --- .../typed/superhero/v1alpha1/spiderman.go | 174 ------------------ .../superhero/v1alpha1/superhero_client.go | 90 --------- test/integration/operator/doc.go | 1 - test/integration/operator/operator_test.go | 148 --------------- 25 files changed, 34 insertions(+), 1254 deletions(-) delete mode 100644 test/integration/operator/Makefile delete mode 100644 test/integration/operator/apis/superhero/doc.go delete mode 100644 test/integration/operator/apis/superhero/v1alpha1/doc.go delete mode 100644 test/integration/operator/apis/superhero/v1alpha1/register.go delete mode 100644 test/integration/operator/apis/superhero/v1alpha1/types.go delete mode 100644 test/integration/operator/apis/superhero/v1alpha1/zz_generated.deepcopy.go delete mode 100644 test/integration/operator/client/k8s/clientset/versioned/clientset.go delete mode 100644 test/integration/operator/client/k8s/clientset/versioned/doc.go delete mode 100644 test/integration/operator/client/k8s/clientset/versioned/fake/clientset_generated.go delete mode 100644 test/integration/operator/client/k8s/clientset/versioned/fake/doc.go delete mode 100644 test/integration/operator/client/k8s/clientset/versioned/fake/register.go delete mode 100644 test/integration/operator/client/k8s/clientset/versioned/scheme/doc.go delete mode 100644 test/integration/operator/client/k8s/clientset/versioned/scheme/register.go delete mode 100644 test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/doc.go delete mode 100644 test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/fake/doc.go delete mode 100644 test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/fake/fake_spiderman.go delete mode 100644 test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/fake/fake_superhero_client.go delete mode 100644 test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/generated_expansion.go delete mode 100644 test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/spiderman.go delete mode 100644 test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/superhero_client.go delete mode 100644 test/integration/operator/doc.go delete mode 100644 test/integration/operator/operator_test.go diff --git a/test/integration/controller/controller_test.go b/test/integration/controller/controller_test.go index accc86fe..a6516acb 100644 --- a/test/integration/controller/controller_test.go +++ b/test/integration/controller/controller_test.go @@ -17,10 +17,8 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/cache" + "github.com/spotahome/kooper/controller" "github.com/spotahome/kooper/log" - "github.com/spotahome/kooper/operator/controller" - "github.com/spotahome/kooper/operator/handler" - "github.com/spotahome/kooper/operator/retrieve" "github.com/spotahome/kooper/test/integration/helper/cli" "github.com/spotahome/kooper/test/integration/helper/prepare" ) @@ -75,7 +73,7 @@ func TestControllerHandleEvents(t *testing.T) { var gotDeletedServices []string // Create the kubernetes client. - k8scli, _, _, err := cli.GetK8sClients("") + k8scli, err := cli.GetK8sClient("") require.NoError(err, "kubernetes client is required") @@ -85,7 +83,7 @@ func TestControllerHandleEvents(t *testing.T) { defer prep.TearDown() // Create the reitrever. - rt := &retrieve.Resource{ + rt := &controller.Resource{ ListerWatcher: cache.NewListWatchFromClient(k8scli.CoreV1().RESTClient(), "services", prep.Namespace().Name, fields.Everything()), Object: &corev1.Service{}, } @@ -96,7 +94,7 @@ func TestControllerHandleEvents(t *testing.T) { var mx sync.Mutex // Create the handler. - hl := &handler.HandlerFunc{ + hl := &controller.HandlerFunc{ AddFunc: func(_ context.Context, obj runtime.Object) error { mx.Lock() calledTimes++ @@ -125,8 +123,15 @@ func TestControllerHandleEvents(t *testing.T) { } // Create a Pod controller. - ctrl := controller.NewSequential(resync, hl, rt, nil, log.Dummy) - require.NotNil(ctrl, "controller is required") + cfg := &controller.Config{ + Name: "test-controller", + Handler: hl, + Retriever: rt, + Logger: log.Dummy, + ResyncInterval: resync, + } + ctrl, err := controller.New(cfg) + require.NoError(err, "controller is required, can't have error on creation") go ctrl.Run(stopC) // Create the required services. diff --git a/test/integration/controller/generic_test.go b/test/integration/controller/generic_test.go index af715fe4..e4a2e1c9 100644 --- a/test/integration/controller/generic_test.go +++ b/test/integration/controller/generic_test.go @@ -16,11 +16,8 @@ import ( "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/tools/cache" + "github.com/spotahome/kooper/controller" "github.com/spotahome/kooper/log" - "github.com/spotahome/kooper/monitoring/metrics" - "github.com/spotahome/kooper/operator/controller" - "github.com/spotahome/kooper/operator/handler" - "github.com/spotahome/kooper/operator/retrieve" ) const ( @@ -53,7 +50,7 @@ func runTimedController(sleepDuration time.Duration, concurrencyLevel int, numbe // Create the faked retriever that will only return N pods. podList := returnPodList(numberOfEvents) - r := &retrieve.Resource{ + r := &controller.Resource{ Object: &corev1.Pod{}, ListerWatcher: &cache.ListWatch{ ListFunc: func(_ metav1.ListOptions) (runtime.Object, error) { @@ -70,7 +67,7 @@ func runTimedController(sleepDuration time.Duration, concurrencyLevel int, numbe var wg sync.WaitGroup wg.Add(numberOfEvents) - h := &handler.HandlerFunc{ + h := &controller.HandlerFunc{ AddFunc: func(_ context.Context, _ runtime.Object) error { time.Sleep(sleepDuration) wg.Done() @@ -83,15 +80,18 @@ func runTimedController(sleepDuration time.Duration, concurrencyLevel int, numbe } // Create the controller type depending on the concurrency level. - var ctrl controller.Controller - var err error - if concurrencyLevel < 2 { - ctrl = controller.NewSequential(noResync, h, r, metrics.Dummy, log.Dummy) - } else { - ctrl, err = controller.NewConcurrent(concurrencyLevel, noResync, h, r, metrics.Dummy, log.Dummy) - if !assert.NoError(err) { - return 0 - } + cfg := &controller.Config{ + Name: "test-controller", + Handler: h, + Retriever: r, + Logger: log.Dummy, + ProcessingJobRetries: concurrencyLevel, + ResyncInterval: noResync, + ConcurrentWorkers: concurrencyLevel, + } + ctrl, err := controller.New(cfg) + if !assert.NoError(err) { + return 0 } // Run handling diff --git a/test/integration/helper/cli/cli.go b/test/integration/helper/cli/cli.go index a0d8a9f3..a9171c6b 100644 --- a/test/integration/helper/cli/cli.go +++ b/test/integration/helper/cli/cli.go @@ -5,19 +5,15 @@ import ( "os" "path/filepath" - integrationtestk8scli "github.com/spotahome/kooper/test/integration/operator/client/k8s/clientset/versioned" - apiextensionscli "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" // Load oidc authentication when creating the kubernetes client. "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" ) -// GetK8sClients returns a all k8s clients. -// * Kubernetes core resources client. -// * Kubernetes api extensions client. -// * Custom test integration CR client. -func GetK8sClients(kubehome string) (kubernetes.Interface, apiextensionscli.Interface, integrationtestk8scli.Interface, error) { + +// GetK8sClient returns k8s client. +func GetK8sClient(kubehome string) (kubernetes.Interface, error) { // Try fallbacks. if kubehome == "" { if kubehome = os.Getenv("KUBECONFIG"); kubehome == "" { @@ -28,26 +24,14 @@ func GetK8sClients(kubehome string) (kubernetes.Interface, apiextensionscli.Inte // Load kubernetes local connection. config, err := clientcmd.BuildConfigFromFlags("", kubehome) if err != nil { - return nil, nil, nil, fmt.Errorf("could not load configuration: %s", err) + return nil, fmt.Errorf("could not load configuration: %s", err) } // Get the client. k8sCli, err := kubernetes.NewForConfig(config) if err != nil { - return nil, nil, nil, err - } - - // App CRD k8s types client. - itCli, err := integrationtestk8scli.NewForConfig(config) - if err != nil { - return nil, nil, nil, err - } - - // api extensions cli. - aexCli, err := apiextensionscli.NewForConfig(config) - if err != nil { - return nil, nil, nil, err + return nil, err } - return k8sCli, aexCli, itCli, nil + return k8sCli, nil } diff --git a/test/integration/operator/Makefile b/test/integration/operator/Makefile deleted file mode 100644 index 74cf4dab..00000000 --- a/test/integration/operator/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -DIRECTORY := $(PWD) -CODE_GENERATOR_IMAGE=quay.io/slok/kube-code-generator:v1.13.5 -CODE_GENERATOR_PACKAGE=github.com/spotahome/kooper/test/integration/operator -GROUPS_VERSION="superhero:v1alpha1" - -default: generate - -.PHONY: generate -generate: - docker run --rm -it \ - -v $(DIRECTORY):/go/src/$(CODE_GENERATOR_PACKAGE) \ - -e PROJECT_PACKAGE=$(CODE_GENERATOR_PACKAGE) \ - -e CLIENT_GENERATOR_OUT=$(CODE_GENERATOR_PACKAGE)/client/k8s \ - -e APIS_ROOT=$(CODE_GENERATOR_PACKAGE)/apis \ - -e GROUPS_VERSION=$(GROUPS_VERSION) \ - -e GENERATION_TARGETS="deepcopy,client" \ - $(CODE_GENERATOR_IMAGE) \ No newline at end of file diff --git a/test/integration/operator/apis/superhero/doc.go b/test/integration/operator/apis/superhero/doc.go deleted file mode 100644 index 3ba092ce..00000000 --- a/test/integration/operator/apis/superhero/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -package superhero - -const ( - GroupName = "superhero.comic.kooper" -) diff --git a/test/integration/operator/apis/superhero/v1alpha1/doc.go b/test/integration/operator/apis/superhero/v1alpha1/doc.go deleted file mode 100644 index f94213dc..00000000 --- a/test/integration/operator/apis/superhero/v1alpha1/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// +k8s:deepcopy-gen=package,register -// +k8s:openapi-gen=true - -// Package v1alpha1 is the v1alpha1 version of the API. -// +groupName=superhero.comic.kooper -package v1alpha1 diff --git a/test/integration/operator/apis/superhero/v1alpha1/register.go b/test/integration/operator/apis/superhero/v1alpha1/register.go deleted file mode 100644 index dc5a4504..00000000 --- a/test/integration/operator/apis/superhero/v1alpha1/register.go +++ /dev/null @@ -1,49 +0,0 @@ -package v1alpha1 - -import ( - apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - - "github.com/spotahome/kooper/test/integration/operator/apis/superhero" -) - -const ( - version = "v1alpha1" -) - -// Spiderman constants -const ( - SpidermanKind = "Spiderman" - SpidermanName = "spiderman" - SpidermanNamePlural = "spidermans" - SpidermanNameMin = "spd" - SpidermanScope = apiextensionsv1beta1.NamespaceScoped -) - -// SpidermanShortName is used to register resource short names -var SpidermanShortNames = []string{"spd", "spm"} - -// SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = schema.GroupVersion{Group: superhero.GroupName, Version: version} - -// Resource takes an unqualified resource and returns a Group qualified GroupResource -func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} - -var ( - SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) - AddToScheme = SchemeBuilder.AddToScheme -) - -// Adds the list of known types to Scheme. -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &Spiderman{}, - &SpidermanList{}, - ) - metav1.AddToGroupVersion(scheme, SchemeGroupVersion) - return nil -} diff --git a/test/integration/operator/apis/superhero/v1alpha1/types.go b/test/integration/operator/apis/superhero/v1alpha1/types.go deleted file mode 100644 index 58a7af2f..00000000 --- a/test/integration/operator/apis/superhero/v1alpha1/types.go +++ /dev/null @@ -1,37 +0,0 @@ -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// +genclient -// +k8s:openapi-gen=true -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +resource:path=spidermans - -// Spiderman is a superhero -type Spiderman struct { - metav1.TypeMeta `json:",inline"` - // Standard object's metadata. - // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata - // +optional - metav1.ObjectMeta `json:"metadata,omitempty"` - // Spec is the Spiderman spec. - Spec SpidermanSpec `json:"spec,omitempty"` -} - -// SpidermanSpec contains the specification for Spiderman. -type SpidermanSpec struct { - Name string `json:"name"` - Year int `json:"year,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// SpidermanList is a collection of spidermans. -type SpidermanList struct { - metav1.TypeMeta `json:",inline"` - // +optional - metav1.ListMeta `json:"metadata,omitempty"` - Items []Spiderman `json:"items"` -} diff --git a/test/integration/operator/apis/superhero/v1alpha1/zz_generated.deepcopy.go b/test/integration/operator/apis/superhero/v1alpha1/zz_generated.deepcopy.go deleted file mode 100644 index 34dff4b0..00000000 --- a/test/integration/operator/apis/superhero/v1alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,101 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by deepcopy-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Spiderman) DeepCopyInto(out *Spiderman) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Spiderman. -func (in *Spiderman) DeepCopy() *Spiderman { - if in == nil { - return nil - } - out := new(Spiderman) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Spiderman) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SpidermanList) DeepCopyInto(out *SpidermanList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Spiderman, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SpidermanList. -func (in *SpidermanList) DeepCopy() *SpidermanList { - if in == nil { - return nil - } - out := new(SpidermanList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *SpidermanList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SpidermanSpec) DeepCopyInto(out *SpidermanSpec) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SpidermanSpec. -func (in *SpidermanSpec) DeepCopy() *SpidermanSpec { - if in == nil { - return nil - } - out := new(SpidermanSpec) - in.DeepCopyInto(out) - return out -} diff --git a/test/integration/operator/client/k8s/clientset/versioned/clientset.go b/test/integration/operator/client/k8s/clientset/versioned/clientset.go deleted file mode 100644 index e03f7e38..00000000 --- a/test/integration/operator/client/k8s/clientset/versioned/clientset.go +++ /dev/null @@ -1,98 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package versioned - -import ( - superherov1alpha1 "github.com/spotahome/kooper/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1" - discovery "k8s.io/client-go/discovery" - rest "k8s.io/client-go/rest" - flowcontrol "k8s.io/client-go/util/flowcontrol" -) - -type Interface interface { - Discovery() discovery.DiscoveryInterface - SuperheroV1alpha1() superherov1alpha1.SuperheroV1alpha1Interface - // Deprecated: please explicitly pick a version if possible. - Superhero() superherov1alpha1.SuperheroV1alpha1Interface -} - -// Clientset contains the clients for groups. Each group has exactly one -// version included in a Clientset. -type Clientset struct { - *discovery.DiscoveryClient - superheroV1alpha1 *superherov1alpha1.SuperheroV1alpha1Client -} - -// SuperheroV1alpha1 retrieves the SuperheroV1alpha1Client -func (c *Clientset) SuperheroV1alpha1() superherov1alpha1.SuperheroV1alpha1Interface { - return c.superheroV1alpha1 -} - -// Deprecated: Superhero retrieves the default version of SuperheroClient. -// Please explicitly pick a version. -func (c *Clientset) Superhero() superherov1alpha1.SuperheroV1alpha1Interface { - return c.superheroV1alpha1 -} - -// Discovery retrieves the DiscoveryClient -func (c *Clientset) Discovery() discovery.DiscoveryInterface { - if c == nil { - return nil - } - return c.DiscoveryClient -} - -// NewForConfig creates a new Clientset for the given config. -func NewForConfig(c *rest.Config) (*Clientset, error) { - configShallowCopy := *c - if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { - configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) - } - var cs Clientset - var err error - cs.superheroV1alpha1, err = superherov1alpha1.NewForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - - cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - return &cs, nil -} - -// NewForConfigOrDie creates a new Clientset for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *Clientset { - var cs Clientset - cs.superheroV1alpha1 = superherov1alpha1.NewForConfigOrDie(c) - - cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) - return &cs -} - -// New creates a new Clientset for the given RESTClient. -func New(c rest.Interface) *Clientset { - var cs Clientset - cs.superheroV1alpha1 = superherov1alpha1.New(c) - - cs.DiscoveryClient = discovery.NewDiscoveryClient(c) - return &cs -} diff --git a/test/integration/operator/client/k8s/clientset/versioned/doc.go b/test/integration/operator/client/k8s/clientset/versioned/doc.go deleted file mode 100644 index 41721ca5..00000000 --- a/test/integration/operator/client/k8s/clientset/versioned/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated clientset. -package versioned diff --git a/test/integration/operator/client/k8s/clientset/versioned/fake/clientset_generated.go b/test/integration/operator/client/k8s/clientset/versioned/fake/clientset_generated.go deleted file mode 100644 index e3132511..00000000 --- a/test/integration/operator/client/k8s/clientset/versioned/fake/clientset_generated.go +++ /dev/null @@ -1,82 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - clientset "github.com/spotahome/kooper/test/integration/operator/client/k8s/clientset/versioned" - superherov1alpha1 "github.com/spotahome/kooper/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1" - fakesuperherov1alpha1 "github.com/spotahome/kooper/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/fake" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/discovery" - fakediscovery "k8s.io/client-go/discovery/fake" - "k8s.io/client-go/testing" -) - -// NewSimpleClientset returns a clientset that will respond with the provided objects. -// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, -// without applying any validations and/or defaults. It shouldn't be considered a replacement -// for a real clientset and is mostly useful in simple unit tests. -func NewSimpleClientset(objects ...runtime.Object) *Clientset { - o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) - for _, obj := range objects { - if err := o.Add(obj); err != nil { - panic(err) - } - } - - cs := &Clientset{} - cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} - cs.AddReactor("*", "*", testing.ObjectReaction(o)) - cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { - gvr := action.GetResource() - ns := action.GetNamespace() - watch, err := o.Watch(gvr, ns) - if err != nil { - return false, nil, err - } - return true, watch, nil - }) - - return cs -} - -// Clientset implements clientset.Interface. Meant to be embedded into a -// struct to get a default implementation. This makes faking out just the method -// you want to test easier. -type Clientset struct { - testing.Fake - discovery *fakediscovery.FakeDiscovery -} - -func (c *Clientset) Discovery() discovery.DiscoveryInterface { - return c.discovery -} - -var _ clientset.Interface = &Clientset{} - -// SuperheroV1alpha1 retrieves the SuperheroV1alpha1Client -func (c *Clientset) SuperheroV1alpha1() superherov1alpha1.SuperheroV1alpha1Interface { - return &fakesuperherov1alpha1.FakeSuperheroV1alpha1{Fake: &c.Fake} -} - -// Superhero retrieves the SuperheroV1alpha1Client -func (c *Clientset) Superhero() superherov1alpha1.SuperheroV1alpha1Interface { - return &fakesuperherov1alpha1.FakeSuperheroV1alpha1{Fake: &c.Fake} -} diff --git a/test/integration/operator/client/k8s/clientset/versioned/fake/doc.go b/test/integration/operator/client/k8s/clientset/versioned/fake/doc.go deleted file mode 100644 index 9b99e716..00000000 --- a/test/integration/operator/client/k8s/clientset/versioned/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated fake clientset. -package fake diff --git a/test/integration/operator/client/k8s/clientset/versioned/fake/register.go b/test/integration/operator/client/k8s/clientset/versioned/fake/register.go deleted file mode 100644 index 7fd9a43c..00000000 --- a/test/integration/operator/client/k8s/clientset/versioned/fake/register.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - superherov1alpha1 "github.com/spotahome/kooper/test/integration/operator/apis/superhero/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" -) - -var scheme = runtime.NewScheme() -var codecs = serializer.NewCodecFactory(scheme) -var parameterCodec = runtime.NewParameterCodec(scheme) -var localSchemeBuilder = runtime.SchemeBuilder{ - superherov1alpha1.AddToScheme, -} - -// AddToScheme adds all types of this clientset into the given scheme. This allows composition -// of clientsets, like in: -// -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) -// -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) -// -// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types -// correctly. -var AddToScheme = localSchemeBuilder.AddToScheme - -func init() { - v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) - utilruntime.Must(AddToScheme(scheme)) -} diff --git a/test/integration/operator/client/k8s/clientset/versioned/scheme/doc.go b/test/integration/operator/client/k8s/clientset/versioned/scheme/doc.go deleted file mode 100644 index 7dc37561..00000000 --- a/test/integration/operator/client/k8s/clientset/versioned/scheme/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package contains the scheme of the automatically generated clientset. -package scheme diff --git a/test/integration/operator/client/k8s/clientset/versioned/scheme/register.go b/test/integration/operator/client/k8s/clientset/versioned/scheme/register.go deleted file mode 100644 index c7310d9d..00000000 --- a/test/integration/operator/client/k8s/clientset/versioned/scheme/register.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package scheme - -import ( - superherov1alpha1 "github.com/spotahome/kooper/test/integration/operator/apis/superhero/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" -) - -var Scheme = runtime.NewScheme() -var Codecs = serializer.NewCodecFactory(Scheme) -var ParameterCodec = runtime.NewParameterCodec(Scheme) -var localSchemeBuilder = runtime.SchemeBuilder{ - superherov1alpha1.AddToScheme, -} - -// AddToScheme adds all types of this clientset into the given scheme. This allows composition -// of clientsets, like in: -// -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) -// -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) -// -// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types -// correctly. -var AddToScheme = localSchemeBuilder.AddToScheme - -func init() { - v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) - utilruntime.Must(AddToScheme(Scheme)) -} diff --git a/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/doc.go b/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/doc.go deleted file mode 100644 index df51baa4..00000000 --- a/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated typed clients. -package v1alpha1 diff --git a/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/fake/doc.go b/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/fake/doc.go deleted file mode 100644 index 16f44399..00000000 --- a/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// Package fake has the automatically generated clients. -package fake diff --git a/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/fake/fake_spiderman.go b/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/fake/fake_spiderman.go deleted file mode 100644 index dbfe5e4d..00000000 --- a/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/fake/fake_spiderman.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/spotahome/kooper/test/integration/operator/apis/superhero/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeSpidermans implements SpidermanInterface -type FakeSpidermans struct { - Fake *FakeSuperheroV1alpha1 - ns string -} - -var spidermansResource = schema.GroupVersionResource{Group: "superhero.comic.kooper", Version: "v1alpha1", Resource: "spidermans"} - -var spidermansKind = schema.GroupVersionKind{Group: "superhero.comic.kooper", Version: "v1alpha1", Kind: "Spiderman"} - -// Get takes name of the spiderman, and returns the corresponding spiderman object, and an error if there is any. -func (c *FakeSpidermans) Get(name string, options v1.GetOptions) (result *v1alpha1.Spiderman, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(spidermansResource, c.ns, name), &v1alpha1.Spiderman{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Spiderman), err -} - -// List takes label and field selectors, and returns the list of Spidermans that match those selectors. -func (c *FakeSpidermans) List(opts v1.ListOptions) (result *v1alpha1.SpidermanList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(spidermansResource, spidermansKind, c.ns, opts), &v1alpha1.SpidermanList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.SpidermanList{ListMeta: obj.(*v1alpha1.SpidermanList).ListMeta} - for _, item := range obj.(*v1alpha1.SpidermanList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested spidermans. -func (c *FakeSpidermans) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(spidermansResource, c.ns, opts)) - -} - -// Create takes the representation of a spiderman and creates it. Returns the server's representation of the spiderman, and an error, if there is any. -func (c *FakeSpidermans) Create(spiderman *v1alpha1.Spiderman) (result *v1alpha1.Spiderman, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(spidermansResource, c.ns, spiderman), &v1alpha1.Spiderman{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Spiderman), err -} - -// Update takes the representation of a spiderman and updates it. Returns the server's representation of the spiderman, and an error, if there is any. -func (c *FakeSpidermans) Update(spiderman *v1alpha1.Spiderman) (result *v1alpha1.Spiderman, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(spidermansResource, c.ns, spiderman), &v1alpha1.Spiderman{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Spiderman), err -} - -// Delete takes name of the spiderman and deletes it. Returns an error if one occurs. -func (c *FakeSpidermans) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(spidermansResource, c.ns, name), &v1alpha1.Spiderman{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeSpidermans) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(spidermansResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.SpidermanList{}) - return err -} - -// Patch applies the patch and returns the patched spiderman. -func (c *FakeSpidermans) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Spiderman, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(spidermansResource, c.ns, name, pt, data, subresources...), &v1alpha1.Spiderman{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Spiderman), err -} diff --git a/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/fake/fake_superhero_client.go b/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/fake/fake_superhero_client.go deleted file mode 100644 index 638dc200..00000000 --- a/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/fake/fake_superhero_client.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/spotahome/kooper/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1" - rest "k8s.io/client-go/rest" - testing "k8s.io/client-go/testing" -) - -type FakeSuperheroV1alpha1 struct { - *testing.Fake -} - -func (c *FakeSuperheroV1alpha1) Spidermans(namespace string) v1alpha1.SpidermanInterface { - return &FakeSpidermans{c, namespace} -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FakeSuperheroV1alpha1) RESTClient() rest.Interface { - var ret *rest.RESTClient - return ret -} diff --git a/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/generated_expansion.go b/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/generated_expansion.go deleted file mode 100644 index e8bda0ed..00000000 --- a/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/generated_expansion.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -type SpidermanExpansion interface{} diff --git a/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/spiderman.go b/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/spiderman.go deleted file mode 100644 index e8e56d87..00000000 --- a/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/spiderman.go +++ /dev/null @@ -1,174 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "time" - - v1alpha1 "github.com/spotahome/kooper/test/integration/operator/apis/superhero/v1alpha1" - scheme "github.com/spotahome/kooper/test/integration/operator/client/k8s/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// SpidermansGetter has a method to return a SpidermanInterface. -// A group's client should implement this interface. -type SpidermansGetter interface { - Spidermans(namespace string) SpidermanInterface -} - -// SpidermanInterface has methods to work with Spiderman resources. -type SpidermanInterface interface { - Create(*v1alpha1.Spiderman) (*v1alpha1.Spiderman, error) - Update(*v1alpha1.Spiderman) (*v1alpha1.Spiderman, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.Spiderman, error) - List(opts v1.ListOptions) (*v1alpha1.SpidermanList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Spiderman, err error) - SpidermanExpansion -} - -// spidermans implements SpidermanInterface -type spidermans struct { - client rest.Interface - ns string -} - -// newSpidermans returns a Spidermans -func newSpidermans(c *SuperheroV1alpha1Client, namespace string) *spidermans { - return &spidermans{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the spiderman, and returns the corresponding spiderman object, and an error if there is any. -func (c *spidermans) Get(name string, options v1.GetOptions) (result *v1alpha1.Spiderman, err error) { - result = &v1alpha1.Spiderman{} - err = c.client.Get(). - Namespace(c.ns). - Resource("spidermans"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Spidermans that match those selectors. -func (c *spidermans) List(opts v1.ListOptions) (result *v1alpha1.SpidermanList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.SpidermanList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("spidermans"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested spidermans. -func (c *spidermans) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("spidermans"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a spiderman and creates it. Returns the server's representation of the spiderman, and an error, if there is any. -func (c *spidermans) Create(spiderman *v1alpha1.Spiderman) (result *v1alpha1.Spiderman, err error) { - result = &v1alpha1.Spiderman{} - err = c.client.Post(). - Namespace(c.ns). - Resource("spidermans"). - Body(spiderman). - Do(). - Into(result) - return -} - -// Update takes the representation of a spiderman and updates it. Returns the server's representation of the spiderman, and an error, if there is any. -func (c *spidermans) Update(spiderman *v1alpha1.Spiderman) (result *v1alpha1.Spiderman, err error) { - result = &v1alpha1.Spiderman{} - err = c.client.Put(). - Namespace(c.ns). - Resource("spidermans"). - Name(spiderman.Name). - Body(spiderman). - Do(). - Into(result) - return -} - -// Delete takes name of the spiderman and deletes it. Returns an error if one occurs. -func (c *spidermans) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("spidermans"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *spidermans) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("spidermans"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched spiderman. -func (c *spidermans) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Spiderman, err error) { - result = &v1alpha1.Spiderman{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("spidermans"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/superhero_client.go b/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/superhero_client.go deleted file mode 100644 index 9437ac69..00000000 --- a/test/integration/operator/client/k8s/clientset/versioned/typed/superhero/v1alpha1/superhero_client.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/spotahome/kooper/test/integration/operator/apis/superhero/v1alpha1" - "github.com/spotahome/kooper/test/integration/operator/client/k8s/clientset/versioned/scheme" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - rest "k8s.io/client-go/rest" -) - -type SuperheroV1alpha1Interface interface { - RESTClient() rest.Interface - SpidermansGetter -} - -// SuperheroV1alpha1Client is used to interact with features provided by the superhero.comic.kooper group. -type SuperheroV1alpha1Client struct { - restClient rest.Interface -} - -func (c *SuperheroV1alpha1Client) Spidermans(namespace string) SpidermanInterface { - return newSpidermans(c, namespace) -} - -// NewForConfig creates a new SuperheroV1alpha1Client for the given config. -func NewForConfig(c *rest.Config) (*SuperheroV1alpha1Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - return &SuperheroV1alpha1Client{client}, nil -} - -// NewForConfigOrDie creates a new SuperheroV1alpha1Client for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *SuperheroV1alpha1Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} - -// New creates a new SuperheroV1alpha1Client for the given RESTClient. -func New(c rest.Interface) *SuperheroV1alpha1Client { - return &SuperheroV1alpha1Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v1alpha1.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} - - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } - - return nil -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *SuperheroV1alpha1Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} diff --git a/test/integration/operator/doc.go b/test/integration/operator/doc.go deleted file mode 100644 index 18b97f2b..00000000 --- a/test/integration/operator/doc.go +++ /dev/null @@ -1 +0,0 @@ -package operator diff --git a/test/integration/operator/operator_test.go b/test/integration/operator/operator_test.go deleted file mode 100644 index 5dae6ae8..00000000 --- a/test/integration/operator/operator_test.go +++ /dev/null @@ -1,148 +0,0 @@ -// +build integration - -package operator_test - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/cache" - - "github.com/spotahome/kooper/client/crd" - "github.com/spotahome/kooper/log" - "github.com/spotahome/kooper/operator" - "github.com/spotahome/kooper/operator/controller" - "github.com/spotahome/kooper/operator/handler" - "github.com/spotahome/kooper/test/integration/helper/cli" - "github.com/spotahome/kooper/test/integration/helper/prepare" - superherov1alpha1 "github.com/spotahome/kooper/test/integration/operator/apis/superhero/v1alpha1" - integrationtestk8scli "github.com/spotahome/kooper/test/integration/operator/client/k8s/clientset/versioned" - apiextensionscli "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" -) - -type spidermanCRD struct { - aexcli apiextensionscli.Interface - crdcli crd.Interface - kubeccli kubernetes.Interface - integrationtestk8scli integrationtestk8scli.Interface -} - -func (s *spidermanCRD) GetListerWatcher() cache.ListerWatcher { - return &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - return s.integrationtestk8scli.SuperheroV1alpha1().Spidermans("").List(options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - return s.integrationtestk8scli.SuperheroV1alpha1().Spidermans("").Watch(options) - }, - } -} - -func (s *spidermanCRD) GetObject() runtime.Object { - return &superherov1alpha1.Spiderman{} -} - -// podTerminatorCRD satisfies resource.crd interface. -func (s *spidermanCRD) Initialize() error { - crd := crd.Conf{ - Kind: superherov1alpha1.SpidermanKind, - NamePlural: superherov1alpha1.SpidermanNamePlural, - ShortNames: superherov1alpha1.SpidermanShortNames, - Group: superherov1alpha1.SchemeGroupVersion.Group, - Version: superherov1alpha1.SchemeGroupVersion.Version, - Scope: superherov1alpha1.SpidermanScope, - } - - return s.crdcli.EnsurePresent(crd) -} - -func (s *spidermanCRD) deleteCRD() error { - crdName := fmt.Sprintf("%s.%s", superherov1alpha1.SpidermanNamePlural, superherov1alpha1.SchemeGroupVersion.Group) - return s.aexcli.ApiextensionsV1beta1().CustomResourceDefinitions().Delete(crdName, &metav1.DeleteOptions{}) -} - -// TestCRDRegister will test the CRD is registered on the cluster. -func TestCRDRegister(t *testing.T) { - tests := []struct { - name string - }{ - { - name: "Starting the operator should register the CRD.", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - require := require.New(t) - assert := assert.New(t) - - resync := 30 * time.Second - stopC := make(chan struct{}) - - // Create the kubernetes client. - k8scli, aexcli, itcli, err := cli.GetK8sClients("") - require.NoError(err, "kubernetes client is required") - - // Prepare the environment on the cluster. - prep := prepare.New(k8scli, t) - prep.SetUp() - defer prep.TearDown() - - // Create the CRD. - spcrd := &spidermanCRD{ - aexcli: aexcli, - crdcli: crd.NewClient(aexcli, log.Dummy), - kubeccli: k8scli, - integrationtestk8scli: itcli, - } - - // Create the handler. - hl := &handler.HandlerFunc{ - AddFunc: func(_ context.Context, obj runtime.Object) error { - return nil - }, - DeleteFunc: func(_ context.Context, id string) error { - return nil - }, - } - - // Create a controller. - ctrl := controller.NewSequential(resync, hl, spcrd, nil, log.Dummy) - require.NotNil(ctrl, "controller is required") - - // Check no CRD. - _, err = itcli.Discovery().ServerResourcesForGroupVersion(superherov1alpha1.SchemeGroupVersion.String()) - require.Error(err, "the resource shouldn't be registered") - // At the end of the test the resource shouldn't be there. - defer spcrd.deleteCRD() - - // Starting the operator should register the CRD. - op := operator.NewOperator(spcrd, ctrl, log.Dummy) - go op.Run(stopC) - // Stop operator when the test is done. - defer func() { - close(stopC) - }() - - // Wait some time until the registration takes effect. - <-time.After(2 * time.Second) - - // Check. - rl, err := itcli.Discovery().ServerResourcesForGroupVersion(superherov1alpha1.SchemeGroupVersion.String()) - if assert.NoError(err) { - // Check the only resource available is spiderman. - rr := rl.APIResources - assert.Len(rr, 1) - assert.Equal(superherov1alpha1.SpidermanKind, rr[0].Kind) - } - }) - } -} From 025c80aecd5e4b51d1e5216e1e9b3f17e5a311bc Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Wed, 18 Mar 2020 12:11:28 +0100 Subject: [PATCH 04/31] Update dependencies Signed-off-by: Xabier Larrakoetxea --- .travis.yml | 4 +- CHANGELOG.md | 23 ++- Makefile | 9 +- go.mod | 94 +----------- go.sum | 208 +++----------------------- hack/scripts/integration-test-kind.sh | 2 +- 6 files changed, 57 insertions(+), 283 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7ff623bd..92b824f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,5 +17,5 @@ script: env: global: - GO111MODULE=on - - KIND_VERSION=v0.6.1 - - KUBERNETES_VERSION=1.15.6 + - KIND_VERSION=v0.7.0 + - KUBERNETES_VERSION=1.15.10 diff --git a/CHANGELOG.md b/CHANGELOG.md index da6ad1d1..3f0bfa2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,23 @@ ## [unreleased] -- Support for Kubernetes 1.13. +NOTE: Breaking release in controllers. + +- Refactor controller package. +- Refactor metrics package. +- Refactor log package. +- Remove operator concept and remove CRD initialization in favor of using only + controllers and let the CRD initialization outside Kooper (e.g CRD yaml). +- Default resync time to 3 minutes. +- Default workers to 3. +- Remove tracing. + +## [0.8.0] - 2019-12-11 + +- Support for Kubernetes 1.15. + +## [0.7.0] - 2019-11-26 + +- Support for Kubernetes 1.14. ## [0.6.0] - 2019-06-01 @@ -83,7 +100,9 @@ This release breaks controllers constructors to allow passing a metrics recorder - sequential controller implementation. - Dependencies managed by dep and vendored. -[unreleased]: https://github.com/spotahome/kooper/compare/v0.6.0...HEAD +[unreleased]: https://github.com/spotahome/kooper/compare/v0.8.0...HEAD +[0.8.0]: https://github.com/spotahome/kooper/compare/v0.7.0...v0.8.0 +[0.7.0]: https://github.com/spotahome/kooper/compare/v0.6.0...v0.7.0 [0.6.0]: https://github.com/spotahome/kooper/compare/v0.5.1...v0.6.0 [0.5.1]: https://github.com/spotahome/kooper/compare/v0.5.0...v0.5.1 [0.5.0]: https://github.com/spotahome/kooper/compare/v0.4.1...v0.5.0 diff --git a/Makefile b/Makefile index 2bc67b77..cfe6aa59 100644 --- a/Makefile +++ b/Makefile @@ -26,14 +26,13 @@ DOCKER_RUN_CMD := docker run -v ${PWD}:$(DOCKER_GO_SERVICE_PATH) --rm -it $(SERV RUN_EXAMPLE_POD_ECHO := go run ./examples/echo-pod-controller/cmd/* --development RUN_EXAMPLE_POD_ECHO_ONEFILE := go run ./examples/onefile-echo-pod-controller/main.go --development RUN_EXAMPLE_POD_TERM := go run ./examples/pod-terminator-operator/cmd/* --development -DEPS_CMD := GO111MODULE=on go mod tidy && GO111MODULE=on go mod vendor -K8S_VERSION := "1.15.6" -SET_K8S_DEPS_CMD := GO111MODULE=on go mod edit \ +DEPS_CMD := go mod tidy +K8S_VERSION := "1.15.10" +SET_K8S_DEPS_CMD := go mod edit \ -require=k8s.io/apiextensions-apiserver@kubernetes-${K8S_VERSION} \ -require=k8s.io/client-go@kubernetes-${K8S_VERSION} \ -require=k8s.io/apimachinery@kubernetes-${K8S_VERSION} \ - -require=k8s.io/api@kubernetes-${K8S_VERSION} \ - -require=k8s.io/kubernetes@v${K8S_VERSION} && \ + -require=k8s.io/api@kubernetes-${K8S_VERSION} && \ $(DEPS_CMD) # environment dirs diff --git a/go.mod b/go.mod index 206e48ec..3c29dc82 100644 --- a/go.mod +++ b/go.mod @@ -1,96 +1,12 @@ module github.com/spotahome/kooper -// Dependencies we don't really need, except that kubernetes specifies them as v0.0.0 which confuses go.mod -//replace k8s.io/apiserver => k8s.io/apiserver kubernetes-1.15.6 -//replace k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver kubernetes-1.15.6 -//replace k8s.io/api => k8s.io/api kubernetes-1.15.6 -//replace k8s.io/component-base => k8s.io/component-base kubernetes-1.15.6 -//replace k8s.io/client-go => k8s.io/client-go kubernetes-1.15.6 -//replace k8s.io/kube-scheduler => k8s.io/kube-scheduler kubernetes-1.15.6 -//replace k8s.io/apimachinery => k8s.io/apimachinery kubernetes-1.15.6 -//replace k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers kubernetes-1.15.6 -//replace k8s.io/kubelet => k8s.io/kubelet kubernetes-1.15.6 -//replace k8s.io/cloud-provider => k8s.io/cloud-provider kubernetes-1.15.6 -//replace k8s.io/csi-translation-lib => k8s.io/csi-translation-lib kubernetes-1.15.6 -//replace k8s.io/cli-runtime => k8s.io/cli-runtime kubernetes-1.15.6 -//replace k8s.io/kube-aggregator => k8s.io/kube-aggregator kubernetes-1.15.6 -//replace k8s.io/sample-apiserver => k8s.io/sample-apiserver kubernetes-1.15.6 -//replace k8s.io/metrics => k8s.io/metrics kubernetes-1.15.6 -//replace k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap kubernetes-1.15.6 -//replace k8s.io/kube-controller-manager => k8s.io/kube-controller-manager kubernetes-1.15.6 -//replace k8s.io/kube-proxy => k8s.io/kube-proxy kubernetes-1.15.6 -//replace k8s.io/cri-api => k8s.io/cri-api kubernetes-1.15.6 -//replace k8s.io/code-generator => k8s.io/code-generator kubernetes-1.15.6 - -replace k8s.io/apiserver => k8s.io/apiserver v0.0.0-20191114102923-bf973bc1a46c - -replace k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.0.0-20191114105316-e8706470940d - -replace k8s.io/api => k8s.io/api v0.0.0-20191114100237-2cd11237263f - -replace k8s.io/component-base => k8s.io/component-base v0.0.0-20191114102239-843ff05e8ff4 - -replace k8s.io/client-go => k8s.io/client-go v0.0.0-20191114101336-8cba805ad12d - -replace k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.0.0-20191114111147-29226eb67741 - -replace k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20191004115701-31ade1b30762 - -replace k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.0.0-20191114112557-fb8eac6d1d79 - -replace k8s.io/kubelet => k8s.io/kubelet v0.0.0-20191114110913-8a0729368279 - -replace k8s.io/cloud-provider => k8s.io/cloud-provider v0.0.0-20191114111940-b2efa58ca04c - -replace k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.0.0-20191114112225-e438b10da852 - -replace k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20191114110057-22fabc8113ba - -replace k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.0.0-20191114103707-3917fe134eab - -replace k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.0.0-20191114104325-4dc280b03897 - -replace k8s.io/metrics => k8s.io/metrics v0.0.0-20191114105745-bf91bab17669 - -replace k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.0.0-20191114111701-466976f32df4 - -replace k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.0.0-20191114111427-e269b4a0667c - -replace k8s.io/kube-proxy => k8s.io/kube-proxy v0.0.0-20191114110636-5b9a03eee945 - -replace k8s.io/cri-api => k8s.io/cri-api v0.0.0-20190817025403-3ae76f584e79 - -replace k8s.io/code-generator => k8s.io/code-generator v0.0.0-20190612205613-18da4a14b22b - require ( - github.com/Pallinder/go-randomdata v0.0.0-20180329154440-dab270d296c6 - github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect - github.com/evanphx/json-patch v4.5.0+incompatible // indirect - github.com/gogo/protobuf v1.3.0 // indirect - github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect - github.com/googleapis/gnostic v0.3.1 // indirect - github.com/gophercloud/gophercloud v0.4.0 // indirect - github.com/hashicorp/golang-lru v0.5.3 // indirect - github.com/imdario/mergo v0.3.7 // indirect - github.com/opentracing/opentracing-go v1.1.0 + github.com/Pallinder/go-randomdata v1.2.0 github.com/prometheus/client_golang v1.1.0 - github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.4.0 - github.com/uber-go/atomic v1.4.0 // indirect - github.com/uber/jaeger-client-go v2.19.0+incompatible - github.com/uber/jaeger-lib v2.2.0+incompatible // indirect - go.uber.org/atomic v1.4.0 // indirect - golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 // indirect - golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 - golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - k8s.io/api v0.0.0 - k8s.io/apiextensions-apiserver v0.0.0 - k8s.io/apimachinery v0.0.0 - k8s.io/client-go v0.0.0 - k8s.io/klog v1.0.0 // indirect - k8s.io/kube-openapi v0.0.0-20190918143330-0270cf2f1c1d // indirect - k8s.io/utils v0.0.0-20191114200735-6ca3b61696b6 // indirect + k8s.io/api v0.15.10 + k8s.io/apimachinery v0.15.12-beta.0 + k8s.io/client-go v0.15.10 ) -go 1.13 +go 1.14 diff --git a/go.sum b/go.sum index cbb4ae08..4e56f75e 100644 --- a/go.sum +++ b/go.sum @@ -1,94 +1,36 @@ cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v11.1.2+incompatible h1:viZ3tV5l4gE2Sw0xrasFHytCGtzYCrT+um/rrSQ1BfA= github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/Pallinder/go-randomdata v0.0.0-20180329154440-dab270d296c6 h1:L1uKFY3oBXGjb5NAaD/3LUp2j9Ntuzp0WYKxUZhE7Bc= -github.com/Pallinder/go-randomdata v0.0.0-20180329154440-dab270d296c6/go.mod h1:yHmJgulpD2Nfrm0cR9tI/+oAgRqCQQixsA8HyRZfV9Y= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Pallinder/go-randomdata v1.2.0 h1:DZ41wBchNRb/0GfsePLiSwb0PHZmT67XY00lCDlaYPg= +github.com/Pallinder/go-randomdata v1.2.0/go.mod h1:yHmJgulpD2Nfrm0cR9tI/+oAgRqCQQixsA8HyRZfV9Y= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-oidc v0.0.0-20180117170138-065b426bd416/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.0.0-20180108230905-e214231b295a/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda h1:NyywMz59neOoVRFDz+ccfKWxn784fiHMDnZSy6T+JXY= github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= -github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= -github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.19.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.17.2/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= @@ -98,81 +40,50 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= -github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= +github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8 h1:L9JPKrtsHMQ4VCRQfHvbbHBfB2Urn8xf6QZeXZ+OrN4= github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= -github.com/gophercloud/gophercloud v0.4.0 h1:4iXQnHF7LKOl7ncQsRibnUmfx/unxT3rLAniYRB8kQQ= -github.com/gophercloud/gophercloud v0.4.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v0.0.0-20170330212424-2500245aa611/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= -github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= -github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jonboulle/clockwork v0.0.0-20141017032234-72f9bd7c4e0c/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c h1:Hww8mOyEKTeON4bZn7FrlLismspbPc1teNRUVH7wLQ8= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c h1:eSfnfIuwhxZyULg1NNuZycJcYkjYVGYe7FczwQReM6U= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3 h1:EooPXg51Tn+xmWPXJUGCnJhJSpeuMlBmfJVcqIRmmv8= github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= @@ -180,159 +91,88 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cobra v0.0.0-20180319062004-c439c4fa0937/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/uber-go/atomic v1.4.0 h1:yOuPqEq4ovnhEjpHmfFwsqBXDYbQeT6Nb0bwD6XnD5o= -github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= -github.com/uber/jaeger-client-go v2.19.0+incompatible h1:pbwbYfHUoaase0oPQOdZ1GcaUjImYGimUXSQ/+8+Z8Q= -github.com/uber/jaeger-client-go v2.19.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw= -github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68= golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20161028155119-f51c12702a4d h1:TnM+PKb3ylGmZvyPXmo9m/wktg7Jn/a/fNmr33HSj8g= golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w= -golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20170731182057-09f6ed296fc6/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o= gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/natefinch/lumberjack.v2 v2.0.0-20150622162204-20b71e5b60d7/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/square/go-jose.v2 v2.0.0-20180411045311-89060dee6a84/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -k8s.io/api v0.0.0-20191114100237-2cd11237263f h1:UfCVPkZrM+FAQ+c/cbThZTdXrOiuEYnVhzhIyrSqWQk= -k8s.io/api v0.0.0-20191114100237-2cd11237263f/go.mod h1:ceHJE/vDjU8jKnRV6Vqn/+vyZmC6NvOluInN+RhQkIs= -k8s.io/apiextensions-apiserver v0.0.0-20191114105316-e8706470940d h1:3h38p1tth7pLad/4U8pPTB7rAFIvlbzBM5Wbo8ne8is= -k8s.io/apiextensions-apiserver v0.0.0-20191114105316-e8706470940d/go.mod h1:z6StKBoP26Yie4MwJIQ6YDY70JC6KCeRYsKNU3DygEA= -k8s.io/apimachinery v0.0.0-20191004115701-31ade1b30762 h1:GYWOVyO+ZU+YK01nyPiAwB/fQrkxysXwkjbSpIIHdN4= -k8s.io/apimachinery v0.0.0-20191004115701-31ade1b30762/go.mod h1:Xc10RHc1U+F/e9GCloJ8QAeCGevSVP5xhOhqlE+e1kM= -k8s.io/apiserver v0.0.0-20191114102923-bf973bc1a46c/go.mod h1:2XZVh9YO4VVxJEw9xiCdAQ1thGTinWc6uZvQiNQC6VI= -k8s.io/client-go v0.0.0-20191114101336-8cba805ad12d h1:waFh/beHLU2QHqkvROghucKaVw1ZP9eCu6eGYL+R04U= -k8s.io/client-go v0.0.0-20191114101336-8cba805ad12d/go.mod h1:bfRZpiGteZXHxZtDHXTU6b4PBZyXuOc76l9DBv1ASKA= -k8s.io/code-generator v0.0.0-20190612205613-18da4a14b22b/go.mod h1:G8bQwmHm2eafm5bgtX67XDZQ8CWKSGu9DekI+yN4Y5I= -k8s.io/component-base v0.0.0-20191114102239-843ff05e8ff4/go.mod h1:zT8T6A3K4wLlbQkLUC62skjmWoiNJ9B8WUQj3KIvcrQ= -k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +k8s.io/api v0.15.10 h1:g6t2OLNjupSeoepE0zlIcvoT6Q+QDfdfEUwk5lwHXAo= +k8s.io/api v0.15.10/go.mod h1:PffiEKNf0aFiv2naEYSGTFHIGA9V8Qwt22DZIAokOzQ= +k8s.io/apimachinery v0.15.10/go.mod h1:ZRw+v83FjgEqlzqaBkxL3XB21MSLYdzjsY9Bgxclhdw= +k8s.io/apimachinery v0.15.12-beta.0 h1:aGvobE1kXnMyyAgzsYe6bfyyAcoIy2vqwPtSO/PgGBg= +k8s.io/apimachinery v0.15.12-beta.0/go.mod h1:ZRw+v83FjgEqlzqaBkxL3XB21MSLYdzjsY9Bgxclhdw= +k8s.io/client-go v0.15.10 h1:WyH6P2wgULDTFqqClm8cP1E2ELj2qbrxJGR76/SLBQw= +k8s.io/client-go v0.15.10/go.mod h1:UkLY6FcnDCJhI7sabgHI+WovVUTZKvmcOxFrSLOVr4g= +k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68= k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30 h1:TRb4wNWoBVrH9plmkp2q86FIDppkbrEXdXlxU3a3BMI= k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= -k8s.io/kube-openapi v0.0.0-20190918143330-0270cf2f1c1d h1:Xpe6sK+RY4ZgCTyZ3y273UmFmURhjtoJiwOMbQsXitY= -k8s.io/kube-openapi v0.0.0-20190918143330-0270cf2f1c1d/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/utils v0.0.0-20190221042446-c2654d5206da h1:ElyM7RPonbKnQqOcw7dG2IK5uvQQn3b/WPHqD5mBvP4= k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= -k8s.io/utils v0.0.0-20191114200735-6ca3b61696b6 h1:p0Ai3qVtkbCG/Af26dBmU0E1W58NID3hSSh7cMyylpM= -k8s.io/utils v0.0.0-20191114200735-6ca3b61696b6/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= -modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= -modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= -sigs.k8s.io/structured-merge-diff v0.0.0-20190302045857-e85c7b244fd2/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= -sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/hack/scripts/integration-test-kind.sh b/hack/scripts/integration-test-kind.sh index 80e0fc15..78fbd6b7 100755 --- a/hack/scripts/integration-test-kind.sh +++ b/hack/scripts/integration-test-kind.sh @@ -3,7 +3,7 @@ set -o errexit set -o nounset -KUBERNETES_VERSION=v${KUBERNETES_VERSION:-1.15.6} +KUBERNETES_VERSION=v${KUBERNETES_VERSION:-1.15.10} current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" PREVIOUS_KUBECTL_CONTEXT=$(kubectl config current-context) || PREVIOUS_KUBECTL_CONTEXT="" From 00f8861d1aee3d7e55dd1cd584ba4896b51bc1cb Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Thu, 19 Mar 2020 11:38:01 +0100 Subject: [PATCH 05/31] Decouple controller handler metrics into a decorator Signed-off-by: Xabier Larrakoetxea --- controller/generic.go | 38 +++++------------------------------- controller/handler.go | 45 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 33 deletions(-) diff --git a/controller/generic.go b/controller/generic.go index 2eaed9b4..25eab913 100644 --- a/controller/generic.go +++ b/controller/generic.go @@ -120,6 +120,8 @@ func New(cfg *Config) (Controller, error) { store := cache.Indexers{} informer := cache.NewSharedIndexInformer(cfg.Retriever.GetListerWatcher(), cfg.Retriever.GetObject(), cfg.ResyncInterval, store) + handler := newMetricsMeasuredHandler(cfg.Name, cfg.MetricRecorder, cfg.Handler) + // Set up our informer event handler. // Objects are already in our local store. Add only keys/jobs on the queue so they can bre processed // afterwards. @@ -153,7 +155,7 @@ func New(cfg *Config) (Controller, error) { informer: informer, logger: cfg.Logger, metrics: cfg.MetricRecorder, - handler: cfg.Handler, + handler: handler, leRunner: cfg.LeaderElector, cfg: *cfg, }, nil @@ -270,38 +272,8 @@ func (g *generic) processJob(ctx context.Context, key string) error { // handle the object. if !exists { // Deleted resource from the cache. - return g.handleDelete(ctx, key) + return g.handler.Delete(ctx, key) } - return g.handleAdd(ctx, key, obj.(runtime.Object)) -} - -func (g *generic) handleAdd(ctx context.Context, objKey string, obj runtime.Object) error { - start := time.Now() - g.metrics.IncResourceEventProcessed(g.cfg.Name, metrics.AddEvent) - defer func() { - g.metrics.ObserveDurationResourceEventProcessed(g.cfg.Name, metrics.AddEvent, start) - }() - - // Handle the job. - if err := g.handler.Add(ctx, obj); err != nil { - g.metrics.IncResourceEventProcessedError(g.cfg.Name, metrics.AddEvent) - return err - } - return nil -} - -func (g *generic) handleDelete(ctx context.Context, objKey string) error { - start := time.Now() - g.metrics.IncResourceEventProcessed(g.cfg.Name, metrics.DeleteEvent) - defer func() { - g.metrics.ObserveDurationResourceEventProcessed(g.cfg.Name, metrics.DeleteEvent, start) - }() - - // Handle the job. - if err := g.handler.Delete(ctx, objKey); err != nil { - g.metrics.IncResourceEventProcessedError(g.cfg.Name, metrics.DeleteEvent) - return err - } - return nil + return g.handler.Add(ctx, obj.(runtime.Object)) } diff --git a/controller/handler.go b/controller/handler.go index c6a53077..9d0d466a 100644 --- a/controller/handler.go +++ b/controller/handler.go @@ -3,8 +3,11 @@ package controller import ( "context" "fmt" + "time" "k8s.io/apimachinery/pkg/runtime" + + "github.com/spotahome/kooper/monitoring/metrics" ) // Handler knows how to handle the received resources from a kubernetes cluster. @@ -41,3 +44,45 @@ func (h *HandlerFunc) Delete(ctx context.Context, s string) error { } return h.DeleteFunc(ctx, s) } + +type metricsMeasuredHandler struct { + id string + mrec metrics.Recorder + next Handler +} + +func newMetricsMeasuredHandler(id string, mrec metrics.Recorder, next Handler) Handler { + return metricsMeasuredHandler{ + id: id, + mrec: mrec, + next: next, + } +} + +func (m metricsMeasuredHandler) Add(ctx context.Context, obj runtime.Object) (err error) { + defer func(start time.Time) { + m.mrec.ObserveDurationResourceEventProcessed(m.id, metrics.AddEvent, start) + + if err != nil { + m.mrec.IncResourceEventProcessedError(m.id, metrics.AddEvent) + } + }(time.Now()) + + m.mrec.IncResourceEventProcessed(m.id, metrics.AddEvent) + + return m.next.Add(ctx, obj) +} + +func (m metricsMeasuredHandler) Delete(ctx context.Context, objKey string) (err error) { + defer func(start time.Time) { + m.mrec.ObserveDurationResourceEventProcessed(m.id, metrics.DeleteEvent, start) + + if err != nil { + m.mrec.IncResourceEventProcessedError(m.id, metrics.DeleteEvent) + } + }(time.Now()) + + m.mrec.IncResourceEventProcessed(m.id, metrics.DeleteEvent) + + return m.next.Delete(ctx, objKey) +} From 9a71cc2e78f6e25b9d6c5b40db63effb09148f2f Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Thu, 19 Mar 2020 17:46:51 +0100 Subject: [PATCH 06/31] Fix Kind image version Signed-off-by: Xabier Larrakoetxea --- .travis.yml | 2 +- hack/scripts/integration-test-kind.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 92b824f3..756ea0c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,4 +18,4 @@ env: global: - GO111MODULE=on - KIND_VERSION=v0.7.0 - - KUBERNETES_VERSION=1.15.10 + - KUBERNETES_VERSION=1.15.7 diff --git a/hack/scripts/integration-test-kind.sh b/hack/scripts/integration-test-kind.sh index 78fbd6b7..a61841ab 100755 --- a/hack/scripts/integration-test-kind.sh +++ b/hack/scripts/integration-test-kind.sh @@ -3,7 +3,7 @@ set -o errexit set -o nounset -KUBERNETES_VERSION=v${KUBERNETES_VERSION:-1.15.10} +KUBERNETES_VERSION=v${KUBERNETES_VERSION:-1.15.7} current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" PREVIOUS_KUBECTL_CONTEXT=$(kubectl config current-context) || PREVIOUS_KUBECTL_CONTEXT="" From 7e210f7c9b897d46179d688f6a2d63cda932b931 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Fri, 20 Mar 2020 11:24:54 +0100 Subject: [PATCH 07/31] Split controller processor logic into an internal type Signed-off-by: Xabier Larrakoetxea --- CHANGELOG.md | 2 + controller/generic.go | 76 ++++++++++++++++------------------- controller/processor.go | 87 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 43 deletions(-) create mode 100644 controller/processor.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f0bfa2a..3ef4bb91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,9 @@ NOTE: Breaking release in controllers. controllers and let the CRD initialization outside Kooper (e.g CRD yaml). - Default resync time to 3 minutes. - Default workers to 3. +- Disable retry handling on controllers in case of error by default. - Remove tracing. +- Minimum Go version v1.13 (error wrapping required). ## [0.8.0] - 2019-12-11 diff --git a/controller/generic.go b/controller/generic.go index 25eab913..f6b133a4 100644 --- a/controller/generic.go +++ b/controller/generic.go @@ -7,7 +7,6 @@ import ( "sync" "time" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" @@ -26,7 +25,7 @@ var ( const ( defResyncInterval = 3 * time.Minute defConcurrentWorkers = 3 - defProcessingJobRetries = 3 + defProcessingJobRetries = 0 ) // Config is the controller configuration. @@ -84,7 +83,7 @@ func (c *Config) setDefaults() error { c.ResyncInterval = defResyncInterval } - if c.ProcessingJobRetries <= 0 { + if c.ProcessingJobRetries < 0 { c.ProcessingJobRetries = defProcessingJobRetries } @@ -95,7 +94,8 @@ func (c *Config) setDefaults() error { type generic struct { queue workqueue.RateLimitingInterface // queue will have the jobs that the controller will get and send to handlers. informer cache.SharedIndexInformer // informer will notify be inform us about resource changes. - handler Handler // handler is where the logic of resource processing. + processor processor // processor will call the user handler (logic). + running bool runningMu sync.Mutex cfg Config @@ -120,8 +120,6 @@ func New(cfg *Config) (Controller, error) { store := cache.Indexers{} informer := cache.NewSharedIndexInformer(cfg.Retriever.GetListerWatcher(), cfg.Retriever.GetObject(), cfg.ResyncInterval, store) - handler := newMetricsMeasuredHandler(cfg.Name, cfg.MetricRecorder, cfg.Handler) - // Set up our informer event handler. // Objects are already in our local store. Add only keys/jobs on the queue so they can bre processed // afterwards. @@ -149,15 +147,22 @@ func New(cfg *Config) (Controller, error) { }, }, cfg.ResyncInterval) + // Create processing chain processor(+middlewares) -> handler(+middlewares). + handler := newMetricsMeasuredHandler(cfg.Name, cfg.MetricRecorder, cfg.Handler) + processor := newIndexerProcessor(informer.GetIndexer(), handler) + if cfg.ProcessingJobRetries > 0 { + processor = newRetryProcessor(cfg.Name, cfg.ProcessingJobRetries, cfg.MetricRecorder, queue, processor) + } + // Create our generic controller object. return &generic{ - queue: queue, - informer: informer, - logger: cfg.Logger, - metrics: cfg.MetricRecorder, - handler: handler, - leRunner: cfg.LeaderElector, - cfg: *cfg, + queue: queue, + informer: informer, + metrics: cfg.MetricRecorder, + processor: processor, + leRunner: cfg.LeaderElector, + cfg: *cfg, + logger: cfg.Logger, }, nil } @@ -227,16 +232,18 @@ func (g *generic) run(stopC <-chan struct{}) error { // runWorker will start a processing loop on event queue. func (g *generic) runWorker() { for { - // Process newxt queue job, if needs to stop processing it will return true. - if g.getAndProcessNextJob() { + // Process next queue job, if needs to stop processing it will return true. + if g.processNextJob() { break } } } -// getAndProcessNextJob job will process the next job of the queue job and returns if +// processNextJob job will process the next job of the queue job and returns if // it needs to stop processing. -func (g *generic) getAndProcessNextJob() bool { +// +// If the queue has been closed then it will end the processing. +func (g *generic) processNextJob() bool { // Get next job. nextJob, exit := g.queue.Get() if exit { @@ -247,33 +254,16 @@ func (g *generic) getAndProcessNextJob() bool { // Process the job. If errors then enqueue again. ctx := context.Background() - if err := g.processJob(ctx, key); err == nil { - g.queue.Forget(key) - } else if g.queue.NumRequeues(key) < g.cfg.ProcessingJobRetries { - // Job processing failed, requeue. - g.logger.Warningf("error processing %s job (requeued): %v", key, err) - g.queue.AddRateLimited(key) - g.metrics.IncResourceEventQueued(g.cfg.Name, metrics.RequeueEvent) - } else { - g.logger.Errorf("Error processing %s: %v", key, err) - g.queue.Forget(key) + err := g.processor.Process(ctx, key) + + switch { + case err == nil: + g.logger.Infof("object with key %s processed", key) + case errors.Is(err, errRequeued): + g.logger.Warningf("error on object with key %s processing, retrying", key) + default: + g.logger.Errorf("error on object with key %s processing", key) } return false } - -// processJob is where the real processing logic of the item is. -func (g *generic) processJob(ctx context.Context, key string) error { - // Get the object - obj, exists, err := g.informer.GetIndexer().GetByKey(key) - if err != nil { - return err - } - - // handle the object. - if !exists { // Deleted resource from the cache. - return g.handler.Delete(ctx, key) - } - - return g.handler.Add(ctx, obj.(runtime.Object)) -} diff --git a/controller/processor.go b/controller/processor.go new file mode 100644 index 00000000..3c6a9233 --- /dev/null +++ b/controller/processor.go @@ -0,0 +1,87 @@ +package controller + +import ( + "context" + "fmt" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" + + "github.com/spotahome/kooper/monitoring/metrics" +) + +// processor knows how to process object keys. +type processor interface { + Process(ctx context.Context, key string) error +} + +func newIndexerProcessor(indexer cache.Indexer, handler Handler) processor { + return indexerProcessor{ + indexer: indexer, + handler: handler, + } +} + +// indexerProcessor processes a key that will get the kubernetes object from a cache +// called indexer were the kubernetes watch updates have been indexed and stored by the +// listerwatchers from the informers. +type indexerProcessor struct { + indexer cache.Indexer + handler Handler +} + +func (i indexerProcessor) Process(ctx context.Context, key string) error { + // Get the object + obj, exists, err := i.indexer.GetByKey(key) + if err != nil { + return err + } + + // handle the object. + if !exists { // Deleted resource from the cache. + return i.handler.Delete(ctx, key) + } + + return i.handler.Add(ctx, obj.(runtime.Object)) +} + +var errRequeued = fmt.Errorf("requeued after receiving error") + +// retryProcessor will delegate the processing of a key to the received processor, +// in case the processing/handling of this key fails it will add the key +// again to a queue if it has retrys pending. +// +// If the processing errored and has been retried, it will return a `errRequeued` +// error. +type retryProcessor struct { + name string + maxRetries int + mrec metrics.Recorder + queue workqueue.RateLimitingInterface + next processor +} + +func newRetryProcessor(name string, maxRetries int, mrec metrics.Recorder, queue workqueue.RateLimitingInterface, next processor) processor { + return retryProcessor{ + name: name, + maxRetries: maxRetries, + mrec: mrec, + queue: queue, + next: next, + } +} + +func (r retryProcessor) Process(ctx context.Context, key string) error { + err := r.next.Process(ctx, key) + + // If there was an error and we have retries pending then requeue. + if err != nil && r.queue.NumRequeues(key) < r.maxRetries { + r.queue.AddRateLimited(key) + r.mrec.IncResourceEventQueued(r.name, metrics.RequeueEvent) + return fmt.Errorf("%w: %s", errRequeued, err) + } + + r.queue.Forget(key) + return err +} From 8ae0ce56725eb5b2619a691ebb3515eb1ed9316f Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Sat, 21 Mar 2020 12:18:52 +0100 Subject: [PATCH 08/31] Refactor log package Signed-off-by: Xabier Larrakoetxea --- controller/generic.go | 13 +- controller/leaderelection/leaderelection.go | 5 +- docs/controller-tutorial.md | 188 ------------------ examples/config-custom-controller/main.go | 15 +- .../main.go | 30 +-- examples/echo-pod-controller/cmd/flags.go | 48 ----- examples/echo-pod-controller/cmd/main.go | 99 --------- .../echo-pod-controller/controller/config.go | 11 - .../echo-pod-controller/controller/echo.go | 48 ----- .../controller/retrieve.go | 42 ---- examples/echo-pod-controller/log/log.go | 11 - .../echo-pod-controller/service/service.go | 58 ------ .../service/service_test.go | 119 ----------- examples/leader-election-controller/main.go | 6 +- examples/metrics-controller/main.go | 18 +- examples/pod-terminator-operator/cmd/main.go | 7 +- examples/pod-terminator-operator/go.mod | 1 + examples/pod-terminator-operator/go.sum | 4 + go.mod | 1 + go.sum | 5 + log/log.go | 72 +++++-- log/logrus/logrus.go | 21 ++ 22 files changed, 150 insertions(+), 672 deletions(-) delete mode 100644 docs/controller-tutorial.md rename examples/{controller-concurrency-handling-example => controller-concurrency-handling}/main.go (84%) delete mode 100644 examples/echo-pod-controller/cmd/flags.go delete mode 100644 examples/echo-pod-controller/cmd/main.go delete mode 100644 examples/echo-pod-controller/controller/config.go delete mode 100644 examples/echo-pod-controller/controller/echo.go delete mode 100644 examples/echo-pod-controller/controller/retrieve.go delete mode 100644 examples/echo-pod-controller/log/log.go delete mode 100644 examples/echo-pod-controller/service/service.go delete mode 100644 examples/echo-pod-controller/service/service_test.go create mode 100644 log/logrus/logrus.go diff --git a/controller/generic.go b/controller/generic.go index f6b133a4..59054c27 100644 --- a/controller/generic.go +++ b/controller/generic.go @@ -66,9 +66,13 @@ func (c *Config) setDefaults() error { } if c.Logger == nil { - c.Logger = &log.Std{} + c.Logger = log.NewStd(false) c.Logger.Warningf("no logger specified, fallback to default logger, to disable logging use a explicit Noop logger") } + c.Logger = c.Logger.WithKV(log.KV{ + "source-service": "kooper/controller", + "controller-id": c.Name, + }) if c.MetricRecorder == nil { c.MetricRecorder = metrics.Dummy @@ -256,13 +260,14 @@ func (g *generic) processNextJob() bool { ctx := context.Background() err := g.processor.Process(ctx, key) + logger := g.logger.WithKV(log.KV{"object-key": key}) switch { case err == nil: - g.logger.Infof("object with key %s processed", key) + logger.Debugf("object with key processed") case errors.Is(err, errRequeued): - g.logger.Warningf("error on object with key %s processing, retrying", key) + logger.Warningf("error on object key processing, retrying") default: - g.logger.Errorf("error on object with key %s processing", key) + logger.Errorf("error on object key processing") } return false diff --git a/controller/leaderelection/leaderelection.go b/controller/leaderelection/leaderelection.go index 54199bd9..9dd0deba 100644 --- a/controller/leaderelection/leaderelection.go +++ b/controller/leaderelection/leaderelection.go @@ -74,7 +74,10 @@ func New(key, namespace string, lockCfg *LockConfig, k8scli kubernetes.Interface key: key, namespace: namespace, k8scli: k8scli, - logger: logger, + logger: logger.WithKV(log.KV{ + "source-service": "kooper/leader-election", + "leader-election-id": fmt.Sprintf("%s/%s", namespace, key), + }), } if err := r.validate(); err != nil { diff --git a/docs/controller-tutorial.md b/docs/controller-tutorial.md deleted file mode 100644 index f9858390..00000000 --- a/docs/controller-tutorial.md +++ /dev/null @@ -1,188 +0,0 @@ -Controller tutorial -=================== - -In this tutorial we will learn how to create a controller using kooper. Yes, I know what you are thinking, kooper is more an operator library... but an operator as we described in the [concepts](concepts.md) is a controller on steroids and controllers are also fully supported in Kooper. - -So... In this tutorial we will learn the pillars of the operator, the controller. The full controller is [here](https://github.com/spotahome/kooper/tree/master/examples/echo-pod-controller), we will go step by step but some of the code is *glue* or doesn't refer to kooper. - -Lets start! - -## 01 - Description. - -Our Controller will log all the add/delete events that occur to pods on a given namespace. Easy peasy... Lets call it `echo-pod-controller` (yes, very original). The full controller is in [examples/echo-pod-controller](https://github.com/spotahome/kooper/tree/master/examples/echo-pod-controller). - -### Structure. - -The structure of the controller is very simple. - -```bash -./examples/echo-pod-controller/ -├── cmd -│   ├── flags.go -│   └── main.go -├── controller -│   ├── config.go -│   ├── echo.go -│   └── retrieve.go -├── log -│   └── log.go -└── service - ├── service.go - └── service_test.go -``` - -From this structure the important paths are `controller` where all the controller stuff will be, this is, creation, initialization... - -And `service` our domain logic. - -The other ones are not so important for the tutorial, you should check the whole project to have in mind how is structured a full controller. `cmd` is the main program (flags, signal capturing, dependecy creation...). `log` is where our logger is. - -### Unit testing. - -Testing is important. As you see this project has very little unit tests. This is because of two things: - -One, the project is very simple. - -Two, you can trust Kubernetes and Kooper libraries, they are already tested, you don't need to test this, but you should test your domain logic (and if you want, main and glue code also). In this controller we just tested the service that has our domain logic (that is just a simple logging). - -## 02 - Echo service. - -First thigs first, we will implement our domain logic that doesn't know anything of our controller, in other words, our service will do the heavy stuff of the controller and what makes it special or different from other controllers. - -Our controller is a logger just that. - -```go -type Echo interface { - EchoObj(prefix string, obj runtime.Object) - EchoS(prefix string, s string) -} -``` - -We implemented that `Echo` service as `SimpleEcho` check it out on the [service file](https://github.com/spotahome/kooper/blob/master/examples/echo-pod-controller/service/service.go). - -Now is time to use Kooper and leverage all the controller stuff. - -## 03 - Controller configuration. - -We need to implement our controller configuration. The controller is simple, it will need a namespace to know what pods should log and also a (re)synchronization period where the controller will receive all the pods again (apart from real time events). - -[controller/config.go](https://github.com/spotahome/kooper/blob/master/examples/echo-pod-controller/controller/config.go) - -```go -type Config struct { - ResyncPeriod time.Duration - Namespace string -} -``` - -Simple. - -Note we don't have validation, but you could set a method on the `Config` object to validate the configuration. - -## 04 - Controller retriever. - -Like we state on the basics, the retriever is the way the controller knows how to listen to resource events, this is, **list** (initial get and resyncs) and **watch** (real time events). And also know what is the kind of the object is listening to. - -Our controller is for pods so our retriever will use the [Kubernetes client](https://github.com/kubernetes/client-go) to get the pods, for example: - - -```go -client.CoreV1().Pods(namespace).List(options) -``` - -Check the retriever [here](https://github.com/spotahome/kooper/blob/master/examples/echo-pod-controller/controller/retrieve.go) - - -## 05 - Controller handler. - -As the name says, the handler is the place where kooper controller/operator will call when it has an event regarding the resource is listening with the retriever, in our case pods. - -Handler will receive events on: - -* On the first iteration (when the controller starts) and on every resync (intervals) it will call as an `Add` so you get the full list of resources. -* On a resource deletion it will call `Delete`. -* On a resource update it will call `Add` - -At first can look odd that an update on a resource calls `Add`. But we are getting a desired and eventual state of a resource, so doesn't matter if is new or old and has been updated, the reality is that our resource is in this state at this moment and we need to take actions or check previously before taking actions (imagine if we send an email, if we don't do a check we could end up with thousand of emails...). - -In our case we don't bother to check if is new or old or if we have done something related with a previous event on the same resource. We just call our Echo Service. - - -[controller/echo.go](https://github.com/spotahome/kooper/blob/master/examples/echo-pod-controller/controller/echo.go) - -```go -type handler struct { - echoSrv service.Echo -} - -func (h *handler) Add(_ context.Context, obj runtime.Object) error { - h.echoSrv.EchoObj(addPrefix, obj) - return nil -} -func (h *handler) Delete(_ context.Context, s string) error { - h.echoSrv.EchoS(deletePrefix, s) - return nil -} -``` - -## 06 - Controller. - -We have all the pieces of the controller except the controller itself, but don't worry, Kooper gives you a controller implementation so you can glue all together and create a controller. - -This can be found in [controller/echo.go](https://github.com/spotahome/kooper/blob/master/examples/echo-pod-controller/controller/echo.go) (is the same file of the handler). - -We will go step by step: - -```go -type Controller struct { - controller.Controller - - config Config - logger log.Logger -} -``` - -Controller is our controller, it has a logger, a controller configuration(step 03), and a kooper controller that will have all the required stuff to run a controller. - -Our contructor starts by creating the dependencies to create `DefaultGeneric` kooper controller. - -```go -ret := NewPodRetrieve(config.Namespace, k8sCli) -``` - -This is our retriever (step 04), the kubernetes client that we pass to the retriever constructor is created on the main and passed to the controller constructor (where we are and create this retriever). - -```go -echoSrv := service.NewSimpleEcho(logger) -``` -We create our service (step 01), this is for the handler. - -```go -handler := &handler{echoSrv: echoSrv} -``` - -Then we create our simple handler that will have our service. - -And... finally we create the controller! - -```go -ctrl := controller.NewSequential(config.ResyncPeriod, handler, ret, nil, logger) -``` - -We are using a sequential controller constructor (`NewSequential`) from `"github.com/spotahome/kooper/operator/controller"` package. It receives a handler, a retriever, a logger and a resync period. - -Wow, that was easy :) - -## 07 - Finishing. - -After all these steps we have a controller, now just depends how the main is organized or where you start the controller. You can check how is initialized the kubernetes client on the exmaple's [main]((https://github.com/spotahome/kooper/blob/master/examples/echo-pod-controller/cmd/main.go)), call our controller constructor and run it. But mainly is this: - -```go -//... -ctrl, err := controller.New(cfg, k8sCli, logger) -//... -ctrl.Run(stopC) -``` - -The Run method receives a channel that when is closed all the controller stuff will be stopped. - diff --git a/examples/config-custom-controller/main.go b/examples/config-custom-controller/main.go index e02d1344..618ab127 100644 --- a/examples/config-custom-controller/main.go +++ b/examples/config-custom-controller/main.go @@ -7,8 +7,10 @@ import ( "path/filepath" "time" + "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/kubernetes" @@ -20,11 +22,13 @@ import ( "github.com/spotahome/kooper/controller" "github.com/spotahome/kooper/log" + kooperlogrus "github.com/spotahome/kooper/log/logrus" ) func run() error { // Initialize logger. - log := &log.Std{} + logger := kooperlogrus.New(logrus.NewEntry(logrus.New())). + WithKV(log.KV{"example": "config-custom-controller"}) // Get k8s client. k8scfg, err := rest.InClusterConfig() @@ -43,7 +47,7 @@ func run() error { // Create our retriever so the controller knows how to get/listen for pod events. retr := &controller.Resource{ - Object: &corev1.Pod{}, + Object: &unstructured.Unstructured{}, ListerWatcher: &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { return k8scli.CoreV1().Pods("").List(options) @@ -58,20 +62,21 @@ func run() error { hand := &controller.HandlerFunc{ AddFunc: func(_ context.Context, obj runtime.Object) error { pod := obj.(*corev1.Pod) - log.Infof("Pod added: %s/%s", pod.Namespace, pod.Name) + logger.Infof("Pod added: %s/%s", pod.Namespace, pod.Name) return nil }, DeleteFunc: func(_ context.Context, s string) error { - log.Infof("Pod deleted: %s", s) + logger.Infof("Pod deleted: %s", s) return nil }, } // Create the controller with custom configuration. cfg := &controller.Config{ + Name: "config-custom-controller", Handler: hand, Retriever: retr, - Logger: log, + Logger: logger, ProcessingJobRetries: 5, ResyncInterval: 45 * time.Second, diff --git a/examples/controller-concurrency-handling-example/main.go b/examples/controller-concurrency-handling/main.go similarity index 84% rename from examples/controller-concurrency-handling-example/main.go rename to examples/controller-concurrency-handling/main.go index 16812847..893b6010 100644 --- a/examples/controller-concurrency-handling-example/main.go +++ b/examples/controller-concurrency-handling/main.go @@ -8,6 +8,7 @@ import ( "path/filepath" "time" + "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -19,24 +20,25 @@ import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" - "github.com/spotahome/kooper/log" "github.com/spotahome/kooper/controller" + "github.com/spotahome/kooper/log" + kooperlogrus "github.com/spotahome/kooper/log/logrus" ) var ( concurrentWorkers int sleepMS int - intervalS int - retries int + intervalS int + retries int ) func initFlags() error { fg := flag.NewFlagSet(os.Args[0], flag.ExitOnError) fg.IntVar(&concurrentWorkers, "concurrency", 3, "The number of concurrent event handling") fg.IntVar(&sleepMS, "sleep-ms", 25, "The number of milliseconds to sleep on each event handling") - fg.IntVar(&intervalS, "interval-s", 300, "The number of seconds to for reconciliation loop intervals") + fg.IntVar(&intervalS, "interval-s", 45, "The number of seconds to for reconciliation loop intervals") fg.IntVar(&retries, "retries", 3, "The number of retries in case of error") - + err := fg.Parse(os.Args[1:]) if err != nil { return err @@ -66,7 +68,8 @@ func sleep() { func run() error { // Initialize logger. - log := &log.Std{} + logger := kooperlogrus.New(logrus.NewEntry(logrus.New())). + WithKV(log.KV{"example": "controller-concurrency-handling"}) // Init flags. if err := initFlags(); err != nil { @@ -101,26 +104,27 @@ func run() error { }, } - // Our domain logic that will print every add/sync/update and delete event we . + // Our domain logic that will print every add/sync/update and delete event we. hand := &controller.HandlerFunc{ AddFunc: func(_ context.Context, obj runtime.Object) error { pod := obj.(*corev1.Pod) sleep() - log.Infof("Pod added: %s/%s", pod.Namespace, pod.Name) + logger.Infof("Pod added: %s/%s", pod.Namespace, pod.Name) return nil }, DeleteFunc: func(_ context.Context, s string) error { sleep() - log.Infof("Pod deleted: %s", s) + logger.Infof("Pod deleted: %s", s) return nil }, } - // Create the controller that will refresh every 30 seconds. + // Create the controller. cfg := &controller.Config{ - Handler: hand, + Name: "controller-concurrency-handling", + Handler: hand, Retriever: retr, - Logger: log, + Logger: logger, ProcessingJobRetries: retries, ResyncInterval: time.Duration(intervalS) * time.Second, @@ -148,4 +152,4 @@ func main() { } os.Exit(0) -} \ No newline at end of file +} diff --git a/examples/echo-pod-controller/cmd/flags.go b/examples/echo-pod-controller/cmd/flags.go deleted file mode 100644 index a7aea3c9..00000000 --- a/examples/echo-pod-controller/cmd/flags.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import ( - "flag" - "os" - "path/filepath" - "time" - - "github.com/spotahome/kooper/examples/echo-pod-controller/controller" - "k8s.io/client-go/util/homedir" -) - -// Flags are the controller flags. -type Flags struct { - flagSet *flag.FlagSet - - Namespace string - ResyncSec int - KubeConfig string - Development bool -} - -// ControllerConfig converts the command line flag arguments to controller configuration. -func (f *Flags) ControllerConfig() controller.Config { - return controller.Config{ - Namespace: f.Namespace, - ResyncPeriod: time.Duration(f.ResyncSec) * time.Second, - } -} - -// NewFlags returns a new Flags. -func NewFlags() *Flags { - f := &Flags{ - flagSet: flag.NewFlagSet(os.Args[0], flag.ExitOnError), - } - // Get the user kubernetes configuration in it's home directory. - kubehome := filepath.Join(homedir.HomeDir(), ".kube", "config") - - // Init flags. - f.flagSet.StringVar(&f.Namespace, "namespace", "", "kubernetes namespace where this app is running") - f.flagSet.IntVar(&f.ResyncSec, "resync-seconds", 30, "The number of seconds the controller will resync the resources") - f.flagSet.StringVar(&f.KubeConfig, "kubeconfig", kubehome, "kubernetes configuration path, only used when development mode enabled") - f.flagSet.BoolVar(&f.Development, "development", false, "development flag will allow to run the operator outside a kubernetes cluster") - - f.flagSet.Parse(os.Args[1:]) - - return f -} diff --git a/examples/echo-pod-controller/cmd/main.go b/examples/echo-pod-controller/cmd/main.go deleted file mode 100644 index b876cf5f..00000000 --- a/examples/echo-pod-controller/cmd/main.go +++ /dev/null @@ -1,99 +0,0 @@ -package main - -import ( - "fmt" - "os" - "os/signal" - "syscall" - - applogger "github.com/spotahome/kooper/log" - "k8s.io/client-go/kubernetes" - _ "k8s.io/client-go/plugin/pkg/client/auth" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - - "github.com/spotahome/kooper/examples/echo-pod-controller/controller" - "github.com/spotahome/kooper/examples/echo-pod-controller/log" -) - -// Main is the main program. -type Main struct { - flags *Flags - config controller.Config - logger log.Logger -} - -// New returns the main application. -func New(logger log.Logger) *Main { - f := NewFlags() - return &Main{ - flags: f, - config: f.ControllerConfig(), - logger: logger, - } -} - -// Run runs the app. -func (m *Main) Run(stopC <-chan struct{}) error { - m.logger.Infof("initializing echo controller") - - // Get kubernetes rest client. - k8sCli, err := m.getKubernetesClient() - if err != nil { - return err - } - - // Create the controller and run - ctrl, err := controller.New(m.config, k8sCli, m.logger) - if err != nil { - return err - } - - return ctrl.Run(stopC) -} - -func (m *Main) getKubernetesClient() (kubernetes.Interface, error) { - var err error - var cfg *rest.Config - - // If devel mode then use configuration flag path. - if m.flags.Development { - cfg, err = clientcmd.BuildConfigFromFlags("", m.flags.KubeConfig) - if err != nil { - return nil, fmt.Errorf("could not load configuration: %s", err) - } - } else { - cfg, err = rest.InClusterConfig() - if err != nil { - return nil, fmt.Errorf("error loading kubernetes configuration inside cluster, check app is running outside kubernetes cluster or run in development mode: %s", err) - } - } - - return kubernetes.NewForConfig(cfg) -} - -func main() { - logger := &applogger.Std{} - - stopC := make(chan struct{}) - finishC := make(chan error) - signalC := make(chan os.Signal, 1) - signal.Notify(signalC, syscall.SIGTERM, syscall.SIGINT) - m := New(logger) - - // Run in background the controller. - go func() { - finishC <- m.Run(stopC) - }() - - select { - case err := <-finishC: - if err != nil { - fmt.Fprintf(os.Stderr, "error running controller: %s", err) - os.Exit(1) - } - case <-signalC: - logger.Infof("Signal captured, exiting...") - } - -} diff --git a/examples/echo-pod-controller/controller/config.go b/examples/echo-pod-controller/controller/config.go deleted file mode 100644 index 717dc267..00000000 --- a/examples/echo-pod-controller/controller/config.go +++ /dev/null @@ -1,11 +0,0 @@ -package controller - -import ( - "time" -) - -// Config is the controller configuration. -type Config struct { - ResyncPeriod time.Duration - Namespace string -} diff --git a/examples/echo-pod-controller/controller/echo.go b/examples/echo-pod-controller/controller/echo.go deleted file mode 100644 index d6038fa0..00000000 --- a/examples/echo-pod-controller/controller/echo.go +++ /dev/null @@ -1,48 +0,0 @@ -package controller - -import ( - "context" - - "github.com/spotahome/kooper/controller" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes" - - "github.com/spotahome/kooper/examples/echo-pod-controller/log" - "github.com/spotahome/kooper/examples/echo-pod-controller/service" -) - -// New returns a new Echo controller. -func New(config Config, k8sCli kubernetes.Interface, logger log.Logger) (controller.Controller, error) { - - ret := NewPodRetrieve(config.Namespace, k8sCli) - echoSrv := service.NewSimpleEcho(logger) - handler := &handler{echoSrv: echoSrv} - - cfg := &controller.Config{ - Handler: handler, - Retriever: ret, - Logger: logger, - - ResyncInterval: config.ResyncPeriod, - } - - return controller.New(cfg) -} - -const ( - addPrefix = "ADD" - deletePrefix = "DELETE" -) - -type handler struct { - echoSrv service.Echo -} - -func (h *handler) Add(_ context.Context, obj runtime.Object) error { - h.echoSrv.EchoObj(addPrefix, obj) - return nil -} -func (h *handler) Delete(_ context.Context, s string) error { - h.echoSrv.EchoS(deletePrefix, s) - return nil -} diff --git a/examples/echo-pod-controller/controller/retrieve.go b/examples/echo-pod-controller/controller/retrieve.go deleted file mode 100644 index eaf43f0c..00000000 --- a/examples/echo-pod-controller/controller/retrieve.go +++ /dev/null @@ -1,42 +0,0 @@ -package controller - -import ( - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/cache" -) - -// PodRetrieve knows how to retrieve pods. -type PodRetrieve struct { - namespace string - client kubernetes.Interface -} - -// NewPodRetrieve returns a new pod retriever. -func NewPodRetrieve(namespace string, client kubernetes.Interface) *PodRetrieve { - return &PodRetrieve{ - namespace: namespace, - client: client, - } -} - -// GetListerWatcher knows how to return a listerWatcher of a pod. -func (p *PodRetrieve) GetListerWatcher() cache.ListerWatcher { - - return &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - return p.client.CoreV1().Pods(p.namespace).List(options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - return p.client.CoreV1().Pods(p.namespace).Watch(options) - }, - } -} - -// GetObject returns the empty pod. -func (p *PodRetrieve) GetObject() runtime.Object { - return &corev1.Pod{} -} diff --git a/examples/echo-pod-controller/log/log.go b/examples/echo-pod-controller/log/log.go deleted file mode 100644 index 36f1fb5d..00000000 --- a/examples/echo-pod-controller/log/log.go +++ /dev/null @@ -1,11 +0,0 @@ -package log - -import ( - "github.com/spotahome/kooper/log" -) - -// Logger is the interface of the controller logger. This is an example -// so our Loggger will be the same as the kooper one. -type Logger interface { - log.Logger -} diff --git a/examples/echo-pod-controller/service/service.go b/examples/echo-pod-controller/service/service.go deleted file mode 100644 index 398c705b..00000000 --- a/examples/echo-pod-controller/service/service.go +++ /dev/null @@ -1,58 +0,0 @@ -package service - -import ( - "fmt" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - - "github.com/spotahome/kooper/examples/echo-pod-controller/log" -) - -// Echo is simple echo service. -type Echo interface { - // EchoObj echoes the received object. - EchoObj(prefix string, obj runtime.Object) - // EchoS echoes the received string. - EchoS(prefix string, s string) -} - -// SimpleEcho echoes the received object name. -type SimpleEcho struct { - logger log.Logger -} - -// NewSimpleEcho returns a new SimpleEcho. -func NewSimpleEcho(logger log.Logger) *SimpleEcho { - return &SimpleEcho{ - logger: logger, - } -} - -func (s *SimpleEcho) getObjInfo(obj runtime.Object) (string, error) { - objMeta, ok := obj.(metav1.Object) - if !ok { - return "", fmt.Errorf("could not print object information") - } - return fmt.Sprintf("%s", objMeta.GetName()), nil -} - -func (s *SimpleEcho) echo(prefix string, str string) { - s.logger.Infof("[%s] %s", prefix, str) -} - -// EchoObj satisfies service.Echo interface. -func (s *SimpleEcho) EchoObj(prefix string, obj runtime.Object) { - // Get object string with all the information. - objInfo, err := s.getObjInfo(obj) - if err != nil { - s.logger.Errorf("error on echo: %s", err) - } - - s.echo(prefix, objInfo) -} - -// EchoS satisfies service.Echo interface. -func (s *SimpleEcho) EchoS(prefix string, str string) { - s.echo(prefix, str) -} diff --git a/examples/echo-pod-controller/service/service_test.go b/examples/echo-pod-controller/service/service_test.go deleted file mode 100644 index bfc591ab..00000000 --- a/examples/echo-pod-controller/service/service_test.go +++ /dev/null @@ -1,119 +0,0 @@ -package service_test - -import ( - "fmt" - "sync" - "testing" - - "github.com/stretchr/testify/assert" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - - "github.com/spotahome/kooper/examples/echo-pod-controller/service" -) - -type logKind int - -const ( - infoKind logKind = iota - warnignKind - errorKind -) - -type logEvent struct { - kind logKind - line string -} - -type testLogger struct { - events []logEvent - sync.Mutex -} - -func (t *testLogger) logLine(kind logKind, format string, args ...interface{}) { - str := fmt.Sprintf(format, args...) - t.events = append(t.events, logEvent{kind: kind, line: str}) -} - -func (t *testLogger) Infof(format string, args ...interface{}) { - t.logLine(infoKind, format, args...) -} -func (t *testLogger) Warningf(format string, args ...interface{}) { - t.logLine(warnignKind, format, args...) -} -func (t *testLogger) Errorf(format string, args ...interface{}) { - t.logLine(errorKind, format, args...) -} - -func TestEchoServiceEchoString(t *testing.T) { - tests := []struct { - name string - prefix string - msg string - expResults []logEvent - }{ - { - name: "Logging a prefix and a string should log.", - prefix: "test", - msg: "this is a test", - expResults: []logEvent{ - logEvent{kind: infoKind, line: "[test] this is a test"}, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - assert := assert.New(t) - - // Mocks. - ml := &testLogger{events: []logEvent{}} - - // Create aservice and run. - srv := service.NewSimpleEcho(ml) - srv.EchoS(test.prefix, test.msg) - - // Check. - assert.Equal(test.expResults, ml.events) - }) - } -} - -func TestEchoServiceEchoObj(t *testing.T) { - tests := []struct { - name string - prefix string - obj runtime.Object - expResults []logEvent - }{ - { - name: "Logging a pod should print pod name.", - prefix: "test", - obj: &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "mypod", - }, - }, - expResults: []logEvent{ - logEvent{kind: infoKind, line: "[test] mypod"}, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - assert := assert.New(t) - - // Mocks. - ml := &testLogger{events: []logEvent{}} - - // Create aservice and run. - srv := service.NewSimpleEcho(ml) - srv.EchoObj(test.prefix, test.obj) - - // Check. - assert.Equal(test.expResults, ml.events) - }) - } -} diff --git a/examples/leader-election-controller/main.go b/examples/leader-election-controller/main.go index 7e1dbe6e..85a7afa1 100644 --- a/examples/leader-election-controller/main.go +++ b/examples/leader-election-controller/main.go @@ -10,6 +10,7 @@ import ( "syscall" "time" + "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -24,6 +25,7 @@ import ( "github.com/spotahome/kooper/controller" "github.com/spotahome/kooper/controller/leaderelection" "github.com/spotahome/kooper/log" + kooperlogrus "github.com/spotahome/kooper/log/logrus" ) const ( @@ -56,7 +58,8 @@ func run() error { fl := NewFlags() // Initialize logger. - logger := &log.Std{} + logger := kooperlogrus.New(logrus.NewEntry(logrus.New())). + WithKV(log.KV{"example": "leader-election-controller"}) // Get k8s client. k8scfg, err := rest.InClusterConfig() @@ -107,6 +110,7 @@ func run() error { // Create the controller and run. cfg := &controller.Config{ + Name: "leader-election-controller", Handler: hand, Retriever: retr, LeaderElector: lesvc, diff --git a/examples/metrics-controller/main.go b/examples/metrics-controller/main.go index 98ded27e..72a1d322 100644 --- a/examples/metrics-controller/main.go +++ b/examples/metrics-controller/main.go @@ -12,6 +12,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -25,6 +26,7 @@ import ( "github.com/spotahome/kooper/controller" "github.com/spotahome/kooper/log" + kooperlogrus "github.com/spotahome/kooper/log/logrus" "github.com/spotahome/kooper/monitoring/metrics" ) @@ -89,7 +91,8 @@ func getMetricRecorder(backend string, logger log.Logger) (metrics.Recorder, err func run() error { // Initialize logger. - log := &log.Std{} + logger := kooperlogrus.New(logrus.NewEntry(logrus.New())). + WithKV(log.KV{"example": "metrics-controller"}) // Init flags. if err := initFlags(); err != nil { @@ -137,16 +140,17 @@ func run() error { } // Create the controller that will refresh every 30 seconds. - m, err := getMetricRecorder(metricsBackend, log) + m, err := getMetricRecorder(metricsBackend, logger) if err != nil { return fmt.Errorf("errors getting metrics backend: %w", err) } cfg := &controller.Config{ - Name: "metricsControllerTest", - Handler: hand, - Retriever: retr, - MetricRecorder: m, - Logger: log, + Name: "metricsControllerTest", + Handler: hand, + Retriever: retr, + MetricRecorder: m, + Logger: logger, + ProcessingJobRetries: 3, } ctrl, err := controller.New(cfg) if err != nil { diff --git a/examples/pod-terminator-operator/cmd/main.go b/examples/pod-terminator-operator/cmd/main.go index e67044ef..a98a3eb8 100644 --- a/examples/pod-terminator-operator/cmd/main.go +++ b/examples/pod-terminator-operator/cmd/main.go @@ -7,7 +7,7 @@ import ( "syscall" "time" - applogger "github.com/spotahome/kooper/log" + "github.com/sirupsen/logrus" "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/client-go/rest" @@ -16,6 +16,8 @@ import ( podtermk8scli "github.com/spotahome/kooper/examples/pod-terminator-operator/client/k8s/clientset/versioned" "github.com/spotahome/kooper/examples/pod-terminator-operator/log" "github.com/spotahome/kooper/examples/pod-terminator-operator/operator" + kooperlog "github.com/spotahome/kooper/log" + kooperlogrus "github.com/spotahome/kooper/log/logrus" ) // Main is the main program. @@ -89,7 +91,8 @@ func (m *Main) getKubernetesClients() (podtermk8scli.Interface, kubernetes.Inter } func main() { - logger := &applogger.Std{} + logger := kooperlogrus.New(logrus.NewEntry(logrus.New())). + WithKV(kooperlog.KV{"example": "pod-terminator-operator"}) stopC := make(chan struct{}) finishC := make(chan error) diff --git a/examples/pod-terminator-operator/go.mod b/examples/pod-terminator-operator/go.mod index a52773e0..b6421bb8 100644 --- a/examples/pod-terminator-operator/go.mod +++ b/examples/pod-terminator-operator/go.mod @@ -5,6 +5,7 @@ go 1.14 replace github.com/spotahome/kooper => ../../ require ( + github.com/sirupsen/logrus v1.4.2 github.com/spotahome/kooper v0.0.0 github.com/stretchr/testify v1.4.0 k8s.io/api v0.15.10 diff --git a/examples/pod-terminator-operator/go.sum b/examples/pod-terminator-operator/go.sum index d6bd010b..a3186b33 100644 --- a/examples/pod-terminator-operator/go.sum +++ b/examples/pod-terminator-operator/go.sum @@ -6,6 +6,7 @@ github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/Pallinder/go-randomdata v1.2.0/go.mod h1:yHmJgulpD2Nfrm0cR9tI/+oAgRqCQQixsA8HyRZfV9Y= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -148,6 +149,8 @@ github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURm github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spf13/cobra v0.0.0-20180319062004-c439c4fa0937/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= @@ -192,6 +195,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/go.mod b/go.mod index 3c29dc82..2b45ad2b 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/spotahome/kooper require ( github.com/Pallinder/go-randomdata v1.2.0 github.com/prometheus/client_golang v1.1.0 + github.com/sirupsen/logrus v1.4.2 github.com/stretchr/testify v1.4.0 k8s.io/api v0.15.10 k8s.io/apimachinery v0.15.12-beta.0 diff --git a/go.sum b/go.sum index 4e56f75e..f7ae9b7f 100644 --- a/go.sum +++ b/go.sum @@ -61,6 +61,7 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -98,7 +99,10 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -133,6 +137,7 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= diff --git a/log/log.go b/log/log.go index 10bc6e4f..e5d8ab3b 100644 --- a/log/log.go +++ b/log/log.go @@ -5,36 +5,78 @@ import ( "log" ) +// KV is a helper type for structured logging fields usage. +type KV map[string]interface{} + // Logger is the interface that the loggers used by the library will use. type Logger interface { Infof(format string, args ...interface{}) Warningf(format string, args ...interface{}) Errorf(format string, args ...interface{}) + Debugf(format string, args ...interface{}) + WithKV(KV) Logger } // Dummy logger doesn't log anything -var Dummy = &dummy{} +const Dummy = dummy(0) -type dummy struct{} +type dummy int -func (d *dummy) Infof(format string, args ...interface{}) {} -func (d *dummy) Warningf(format string, args ...interface{}) {} -func (d *dummy) Errorf(format string, args ...interface{}) {} +func (d dummy) Infof(format string, args ...interface{}) {} +func (d dummy) Warningf(format string, args ...interface{}) {} +func (d dummy) Errorf(format string, args ...interface{}) {} +func (d dummy) Debugf(format string, args ...interface{}) {} +func (d dummy) WithKV(KV) Logger { return d } // Std is a wrapper for go standard library logger. -type Std struct{} +type std struct { + debug bool + fields map[string]interface{} +} + +// NewStd returns a Logger implementation with the standard logger. +func NewStd(debug bool) Logger { + return std{ + debug: debug, + fields: map[string]interface{}{}, + } +} + +func (s std) logWithPrefix(prefix, format string, kv map[string]interface{}, args ...interface{}) { -func (s *Std) logWithPrefix(prefix, format string, args ...interface{}) { - format = fmt.Sprintf("%s %s", prefix, format) - log.Printf(format, args...) + msgFmt := "" + if len(kv) == 0 { + msgFmt = fmt.Sprintf("%s\t%s", prefix, format) + } else { + msgFmt = fmt.Sprintf("%s\t%s\t\t%v", prefix, format, kv) + } + + log.Printf(msgFmt, args...) } -func (s *Std) Infof(format string, args ...interface{}) { - s.logWithPrefix("[INFO]", format, args...) +func (s std) Infof(format string, args ...interface{}) { + s.logWithPrefix("[INFO]", format, s.fields, args...) +} +func (s std) Warningf(format string, args ...interface{}) { + s.logWithPrefix("[WARN]", format, s.fields, args...) +} +func (s std) Errorf(format string, args ...interface{}) { + s.logWithPrefix("[ERROR]", format, s.fields, args...) } -func (s *Std) Warningf(format string, args ...interface{}) { - s.logWithPrefix("[WARN]", format, args...) +func (s std) Debugf(format string, args ...interface{}) { + if s.debug { + s.logWithPrefix("[DEBUG]", format, s.fields, args...) + } } -func (s *Std) Errorf(format string, args ...interface{}) { - s.logWithPrefix("[ERROR]", format, args...) + +func (s std) WithKV(kv KV) Logger { + kvs := map[string]interface{}{} + for k, v := range s.fields { + kvs[k] = v + } + for k, v := range kv { + kvs[k] = v + } + + return std{debug: s.debug, fields: kvs} } diff --git a/log/logrus/logrus.go b/log/logrus/logrus.go new file mode 100644 index 00000000..ec746c05 --- /dev/null +++ b/log/logrus/logrus.go @@ -0,0 +1,21 @@ +package logrus + +import ( + "github.com/sirupsen/logrus" + + "github.com/spotahome/kooper/log" +) + +type logger struct { + *logrus.Entry +} + +// New returns a new log.Logger for a logrus implementation. +func New(l *logrus.Entry) log.Logger { + return logger{Entry: l} +} + +func (l logger) WithKV(kv log.KV) log.Logger { + newLogger := l.Entry.WithFields(logrus.Fields(kv)) + return New(newLogger) +} From 26cc598211f31fd5b17d799646f1f466b5485c6a Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Sat, 21 Mar 2020 12:34:42 +0100 Subject: [PATCH 09/31] Minor changes Signed-off-by: Xabier Larrakoetxea --- CHANGELOG.md | 2 ++ controller/generic.go | 6 +++--- examples/config-custom-controller/main.go | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ef4bb91..88a44830 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ NOTE: Breaking release in controllers. - Disable retry handling on controllers in case of error by default. - Remove tracing. - Minimum Go version v1.13 (error wrapping required). +- Refactor Logger with structured logging. +- Add Logrus helper wrapper. ## [0.8.0] - 2019-12-11 diff --git a/controller/generic.go b/controller/generic.go index 59054c27..0e45fd22 100644 --- a/controller/generic.go +++ b/controller/generic.go @@ -263,11 +263,11 @@ func (g *generic) processNextJob() bool { logger := g.logger.WithKV(log.KV{"object-key": key}) switch { case err == nil: - logger.Debugf("object with key processed") + logger.Debugf("object processed") case errors.Is(err, errRequeued): - logger.Warningf("error on object key processing, retrying") + logger.Warningf("error on object processing, retrying: %v", err) default: - logger.Errorf("error on object key processing") + logger.Errorf("error on object processing: %v", err) } return false diff --git a/examples/config-custom-controller/main.go b/examples/config-custom-controller/main.go index 618ab127..3d4f9f1f 100644 --- a/examples/config-custom-controller/main.go +++ b/examples/config-custom-controller/main.go @@ -10,7 +10,6 @@ import ( "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/kubernetes" @@ -47,7 +46,7 @@ func run() error { // Create our retriever so the controller knows how to get/listen for pod events. retr := &controller.Resource{ - Object: &unstructured.Unstructured{}, + Object: &corev1.Pod{}, ListerWatcher: &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { return k8scli.CoreV1().Pods("").List(options) From 2908896fe5f12a25710d9af61d66f9bbf64bae39 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Fri, 27 Mar 2020 10:59:02 +0100 Subject: [PATCH 10/31] Refactor metrics Signed-off-by: Xabier Larrakoetxea --- CHANGELOG.md | 2 + controller/generic.go | 21 ++-- controller/handler.go | 18 ++- controller/metrics.go | 40 ++++++ controller/processor.go | 8 +- examples/metrics-controller/main.go | 24 +--- metrics/prometheus/prometheus.go | 114 ++++++++++++++++++ .../prometheus}/prometheus_test.go | 80 ++++++------ monitoring/metrics/dummy.go | 17 --- monitoring/metrics/metrics.go | 27 ----- monitoring/metrics/prometheus.go | 100 --------------- 11 files changed, 224 insertions(+), 227 deletions(-) create mode 100644 controller/metrics.go create mode 100644 metrics/prometheus/prometheus.go rename {monitoring/metrics => metrics/prometheus}/prometheus_test.go (58%) delete mode 100644 monitoring/metrics/dummy.go delete mode 100644 monitoring/metrics/metrics.go delete mode 100644 monitoring/metrics/prometheus.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 2421f552..d673e298 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ NOTE: Breaking release in controllers. - Add Logrus helper wrapper. - Refactor to simplify the retrievers. - Add multiretriever to retriever different resource types on the same controller. +- Refactor metrics recorder implementation including the prometheus backend, the + output prometheus metrics have not been changed. ## [0.8.0] - 2019-12-11 diff --git a/controller/generic.go b/controller/generic.go index 140d75b7..79a8d9d3 100644 --- a/controller/generic.go +++ b/controller/generic.go @@ -16,7 +16,6 @@ import ( "github.com/spotahome/kooper/controller/leaderelection" "github.com/spotahome/kooper/log" - "github.com/spotahome/kooper/monitoring/metrics" ) var ( @@ -41,7 +40,7 @@ type Config struct { // leader election will be ignored LeaderElector leaderelection.Runner // MetricsRecorder will record the controller metrics. - MetricRecorder metrics.Recorder + MetricsRecorder MetricsRecorder // Logger will log messages of the controller. Logger log.Logger @@ -77,8 +76,8 @@ func (c *Config) setDefaults() error { "controller-id": c.Name, }) - if c.MetricRecorder == nil { - c.MetricRecorder = metrics.Dummy + if c.MetricsRecorder == nil { + c.MetricsRecorder = DummyMetricsRecorder c.Logger.Warningf("no metrics recorder specified, disabling metrics") } @@ -106,7 +105,7 @@ type generic struct { running bool runningMu sync.Mutex cfg Config - metrics metrics.Recorder + metrics MetricsRecorder leRunner leaderelection.Runner logger log.Logger } @@ -148,37 +147,37 @@ func New(cfg *Config) (Controller, error) { key, err := cache.MetaNamespaceKeyFunc(obj) if err == nil { queue.Add(key) - cfg.MetricRecorder.IncResourceEventQueued(cfg.Name, metrics.AddEvent) + cfg.MetricsRecorder.IncResourceEventQueued(context.TODO(), cfg.Name, AddEvent) } }, UpdateFunc: func(old interface{}, new interface{}) { key, err := cache.MetaNamespaceKeyFunc(new) if err == nil { queue.Add(key) - cfg.MetricRecorder.IncResourceEventQueued(cfg.Name, metrics.AddEvent) + cfg.MetricsRecorder.IncResourceEventQueued(context.TODO(), cfg.Name, AddEvent) } }, DeleteFunc: func(obj interface{}) { key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) if err == nil { queue.Add(key) - cfg.MetricRecorder.IncResourceEventQueued(cfg.Name, metrics.DeleteEvent) + cfg.MetricsRecorder.IncResourceEventQueued(context.TODO(), cfg.Name, DeleteEvent) } }, }, cfg.ResyncInterval) // Create processing chain processor(+middlewares) -> handler(+middlewares). - handler := newMetricsMeasuredHandler(cfg.Name, cfg.MetricRecorder, cfg.Handler) + handler := newMetricsMeasuredHandler(cfg.Name, cfg.MetricsRecorder, cfg.Handler) processor := newIndexerProcessor(informer.GetIndexer(), handler) if cfg.ProcessingJobRetries > 0 { - processor = newRetryProcessor(cfg.Name, cfg.ProcessingJobRetries, cfg.MetricRecorder, queue, processor) + processor = newRetryProcessor(cfg.Name, cfg.ProcessingJobRetries, cfg.MetricsRecorder, queue, processor) } // Create our generic controller object. return &generic{ queue: queue, informer: informer, - metrics: cfg.MetricRecorder, + metrics: cfg.MetricsRecorder, processor: processor, leRunner: cfg.LeaderElector, cfg: *cfg, diff --git a/controller/handler.go b/controller/handler.go index 9d0d466a..3e9c38eb 100644 --- a/controller/handler.go +++ b/controller/handler.go @@ -6,8 +6,6 @@ import ( "time" "k8s.io/apimachinery/pkg/runtime" - - "github.com/spotahome/kooper/monitoring/metrics" ) // Handler knows how to handle the received resources from a kubernetes cluster. @@ -47,11 +45,11 @@ func (h *HandlerFunc) Delete(ctx context.Context, s string) error { type metricsMeasuredHandler struct { id string - mrec metrics.Recorder + mrec MetricsRecorder next Handler } -func newMetricsMeasuredHandler(id string, mrec metrics.Recorder, next Handler) Handler { +func newMetricsMeasuredHandler(id string, mrec MetricsRecorder, next Handler) Handler { return metricsMeasuredHandler{ id: id, mrec: mrec, @@ -61,28 +59,28 @@ func newMetricsMeasuredHandler(id string, mrec metrics.Recorder, next Handler) H func (m metricsMeasuredHandler) Add(ctx context.Context, obj runtime.Object) (err error) { defer func(start time.Time) { - m.mrec.ObserveDurationResourceEventProcessed(m.id, metrics.AddEvent, start) + m.mrec.ObserveDurationResourceEventProcessed(ctx, m.id, AddEvent, start) if err != nil { - m.mrec.IncResourceEventProcessedError(m.id, metrics.AddEvent) + m.mrec.IncResourceEventProcessedError(ctx, m.id, AddEvent) } }(time.Now()) - m.mrec.IncResourceEventProcessed(m.id, metrics.AddEvent) + m.mrec.IncResourceEventProcessed(ctx, m.id, AddEvent) return m.next.Add(ctx, obj) } func (m metricsMeasuredHandler) Delete(ctx context.Context, objKey string) (err error) { defer func(start time.Time) { - m.mrec.ObserveDurationResourceEventProcessed(m.id, metrics.DeleteEvent, start) + m.mrec.ObserveDurationResourceEventProcessed(ctx, m.id, DeleteEvent, start) if err != nil { - m.mrec.IncResourceEventProcessedError(m.id, metrics.DeleteEvent) + m.mrec.IncResourceEventProcessedError(ctx, m.id, DeleteEvent) } }(time.Now()) - m.mrec.IncResourceEventProcessed(m.id, metrics.DeleteEvent) + m.mrec.IncResourceEventProcessed(ctx, m.id, DeleteEvent) return m.next.Delete(ctx, objKey) } diff --git a/controller/metrics.go b/controller/metrics.go new file mode 100644 index 00000000..a8736efb --- /dev/null +++ b/controller/metrics.go @@ -0,0 +1,40 @@ +package controller + +import ( + "context" + "time" +) + +// EventType is the event type of a controller enqueued object. +type EventType string + +const ( + //AddEvent is the add event. + AddEvent EventType = "add" + // DeleteEvent is the delete event. + DeleteEvent EventType = "delete" + // RequeueEvent is a requeued event (unknown state when handling again). + RequeueEvent EventType = "requeue" +) + +// MetricsRecorder knows how to record metrics of a controller. +type MetricsRecorder interface { + // IncResourceEvent increments in one the metric records of a queued event. + IncResourceEventQueued(ctx context.Context, controller string, eventType EventType) + // IncResourceEventProcessed increments in one the metric records processed event. + IncResourceEventProcessed(ctx context.Context, controller string, eventType EventType) + // IncResourceEventProcessedError increments in one the metric records of a processed event in error. + IncResourceEventProcessedError(ctx context.Context, controller string, eventType EventType) + // ObserveDurationResourceEventProcessed measures the duration it took to process a event. + ObserveDurationResourceEventProcessed(ctx context.Context, controller string, eventType EventType, start time.Time) +} + +// DummyMetricsRecorder is a dummy metrics recorder. +var DummyMetricsRecorder = dummy(0) + +type dummy int + +func (dummy) IncResourceEventQueued(_ context.Context, _ string, _ EventType) {} +func (dummy) IncResourceEventProcessed(_ context.Context, _ string, _ EventType) {} +func (dummy) IncResourceEventProcessedError(_ context.Context, _ string, _ EventType) {} +func (dummy) ObserveDurationResourceEventProcessed(_ context.Context, _ string, _ EventType, _ time.Time) {} diff --git a/controller/processor.go b/controller/processor.go index 3c6a9233..4cac7283 100644 --- a/controller/processor.go +++ b/controller/processor.go @@ -7,8 +7,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" - - "github.com/spotahome/kooper/monitoring/metrics" ) // processor knows how to process object keys. @@ -57,12 +55,12 @@ var errRequeued = fmt.Errorf("requeued after receiving error") type retryProcessor struct { name string maxRetries int - mrec metrics.Recorder + mrec MetricsRecorder queue workqueue.RateLimitingInterface next processor } -func newRetryProcessor(name string, maxRetries int, mrec metrics.Recorder, queue workqueue.RateLimitingInterface, next processor) processor { +func newRetryProcessor(name string, maxRetries int, mrec MetricsRecorder, queue workqueue.RateLimitingInterface, next processor) processor { return retryProcessor{ name: name, maxRetries: maxRetries, @@ -78,7 +76,7 @@ func (r retryProcessor) Process(ctx context.Context, key string) error { // If there was an error and we have retries pending then requeue. if err != nil && r.queue.NumRequeues(key) < r.maxRetries { r.queue.AddRateLimited(key) - r.mrec.IncResourceEventQueued(r.name, metrics.RequeueEvent) + r.mrec.IncResourceEventQueued(ctx, r.name, RequeueEvent) return fmt.Errorf("%w: %s", errRequeued, err) } diff --git a/examples/metrics-controller/main.go b/examples/metrics-controller/main.go index f22808af..256315ad 100644 --- a/examples/metrics-controller/main.go +++ b/examples/metrics-controller/main.go @@ -26,7 +26,7 @@ import ( "github.com/spotahome/kooper/controller" "github.com/spotahome/kooper/log" kooperlogrus "github.com/spotahome/kooper/log/logrus" - "github.com/spotahome/kooper/monitoring/metrics" + kooperprometheus "github.com/spotahome/kooper/metrics/prometheus" ) const ( @@ -62,11 +62,11 @@ func errRandomly() error { } // creates prometheus recorder and starts serving metrics in background. -func createPrometheusRecorder(logger log.Logger) metrics.Recorder { +func createPrometheusRecorder(logger log.Logger) *kooperprometheus.Recorder { // We could use also prometheus global registry (the default one) // prometheus.DefaultRegisterer instead of creating a new one reg := prometheus.NewRegistry() - m := metrics.NewPrometheus(reg) + rec := kooperprometheus.New(kooperprometheus.Config{Registerer: reg}) // Start serving metrics in background. h := promhttp.HandlerFor(reg, promhttp.HandlerOpts{}) @@ -75,17 +75,7 @@ func createPrometheusRecorder(logger log.Logger) metrics.Recorder { http.ListenAndServe(metricsAddr, h) }() - return m -} - -func getMetricRecorder(backend string, logger log.Logger) (metrics.Recorder, error) { - switch backend { - case prometheusBackend: - logger.Infof("using Prometheus metrics recorder") - return createPrometheusRecorder(logger), nil - } - - return nil, fmt.Errorf("wrong metrics backend") + return rec } func run() error { @@ -136,15 +126,11 @@ func run() error { } // Create the controller that will refresh every 30 seconds. - m, err := getMetricRecorder(metricsBackend, logger) - if err != nil { - return fmt.Errorf("errors getting metrics backend: %w", err) - } cfg := &controller.Config{ Name: "metricsControllerTest", Handler: hand, Retriever: retr, - MetricRecorder: m, + MetricsRecorder: createPrometheusRecorder(logger), Logger: logger, ProcessingJobRetries: 3, } diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go new file mode 100644 index 00000000..916e1213 --- /dev/null +++ b/metrics/prometheus/prometheus.go @@ -0,0 +1,114 @@ +package prometheus + +import ( + "context" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/spotahome/kooper/controller" +) + +const ( + promNamespace = "kooper" + promControllerSubsystem = "controller" +) + +// Config is the Recorder Config. +type Config struct { + // Registerer is a prometheus registerer, e.g: prometheus.Registry. + // By default will use Prometheus default registry. + Registerer prometheus.Registerer + // Buckets sets custom buckets for the duration/latency metrics. This should be used when + // the default buckets don't work. This could happen when the time to process an event is not on the + // range of 5ms-10s duration. + // Check https://godoc.org/github.com/prometheus/client_golang/prometheus#pkg-variables + Buckets []float64 +} + +func (c *Config) defaults() { + if c.Registerer == nil { + c.Registerer = prometheus.DefaultRegisterer + } + + if c.Buckets == nil || len(c.Buckets) == 0 { + c.Buckets = prometheus.DefBuckets + } +} + +// Recorder implements the metrics recording in a prometheus registry. +type Recorder struct { + // Metrics + queuedEvents *prometheus.CounterVec + processedEvents *prometheus.CounterVec + processedEventErrors *prometheus.CounterVec + processedEventDuration *prometheus.HistogramVec +} + +// New returns a new Prometheus implementaiton for a metrics recorder. +func New(cfg Config) *Recorder { + cfg.defaults() + + r := &Recorder{ + queuedEvents: prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: promNamespace, + Subsystem: promControllerSubsystem, + Name: "queued_events_total", + Help: "Total number of events queued.", + }, []string{"controller", "type"}), + + processedEvents: prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: promNamespace, + Subsystem: promControllerSubsystem, + Name: "processed_events_total", + Help: "Total number of successfuly processed events.", + }, []string{"controller", "type"}), + + processedEventErrors: prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: promNamespace, + Subsystem: promControllerSubsystem, + Name: "processed_event_errors_total", + Help: "Total number of errors processing events.", + }, []string{"controller", "type"}), + + processedEventDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: promNamespace, + Subsystem: promControllerSubsystem, + Name: "processed_event_duration_seconds", + Help: "The duration for a successful event to be processed.", + Buckets: cfg.Buckets, + }, []string{"controller", "type"}), + } + + // Register metrics. + cfg.Registerer.MustRegister( + r.queuedEvents, + r.processedEvents, + r.processedEventErrors, + r.processedEventDuration) + + return r +} + +// IncResourceEventQueued satisfies metrics.Recorder interface. +func (r Recorder) IncResourceEventQueued(_ context.Context, controller string, eventType controller.EventType) { + r.queuedEvents.WithLabelValues(controller, string(eventType)).Inc() +} + +// IncResourceEventProcessed satisfies metrics.Recorder interface. +func (r Recorder) IncResourceEventProcessed(_ context.Context, controller string, eventType controller.EventType) { + r.processedEvents.WithLabelValues(controller, string(eventType)).Inc() +} + +// IncResourceEventProcessedError satisfies metrics.Recorder interface. +func (r Recorder) IncResourceEventProcessedError(_ context.Context, controller string, eventType controller.EventType) { + r.processedEventErrors.WithLabelValues(controller, string(eventType)).Inc() +} + +// ObserveDurationResourceEventProcessed satisfies metrics.Recorder interface. +func (r Recorder) ObserveDurationResourceEventProcessed(_ context.Context, controller string, eventType controller.EventType, start time.Time) { + secs := time.Now().Sub(start).Seconds() + r.processedEventDuration.WithLabelValues(controller, string(eventType)).Observe(secs) +} + +// Check we implement all the required metrics recorder interfaces. +var _ controller.MetricsRecorder = &Recorder{} diff --git a/monitoring/metrics/prometheus_test.go b/metrics/prometheus/prometheus_test.go similarity index 58% rename from monitoring/metrics/prometheus_test.go rename to metrics/prometheus/prometheus_test.go index 251bd534..64ec3569 100644 --- a/monitoring/metrics/prometheus_test.go +++ b/metrics/prometheus/prometheus_test.go @@ -1,6 +1,7 @@ -package metrics_test +package prometheus_test import ( + "context" "io/ioutil" "net/http/httptest" "testing" @@ -10,28 +11,29 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/stretchr/testify/assert" - "github.com/spotahome/kooper/monitoring/metrics" + "github.com/spotahome/kooper/controller" + kooperprometheus "github.com/spotahome/kooper/metrics/prometheus" ) -func TestPrometheusMetrics(t *testing.T) { - controller := "test" +func TestPrometheusRecorder(t *testing.T) { + controllerID := "test" tests := []struct { name string - addMetrics func(*metrics.Prometheus) + addMetrics func(*kooperprometheus.Recorder) expMetrics []string expCode int }{ { name: "Incrementing different kind of queued events should measure the queued events counter", - addMetrics: func(p *metrics.Prometheus) { - p.IncResourceEventQueued(controller, metrics.AddEvent) - p.IncResourceEventQueued(controller, metrics.AddEvent) - p.IncResourceEventQueued(controller, metrics.AddEvent) - p.IncResourceEventQueued(controller, metrics.AddEvent) - p.IncResourceEventQueued(controller, metrics.DeleteEvent) - p.IncResourceEventQueued(controller, metrics.DeleteEvent) - p.IncResourceEventQueued(controller, metrics.DeleteEvent) + addMetrics: func(r *kooperprometheus.Recorder) { + r.IncResourceEventQueued(context.TODO(), controllerID, controller.AddEvent) + r.IncResourceEventQueued(context.TODO(), controllerID, controller.AddEvent) + r.IncResourceEventQueued(context.TODO(), controllerID, controller.AddEvent) + r.IncResourceEventQueued(context.TODO(), controllerID, controller.AddEvent) + r.IncResourceEventQueued(context.TODO(), controllerID, controller.DeleteEvent) + r.IncResourceEventQueued(context.TODO(), controllerID, controller.DeleteEvent) + r.IncResourceEventQueued(context.TODO(), controllerID, controller.DeleteEvent) }, expMetrics: []string{ `kooper_controller_queued_events_total{controller="test",type="add"} 4`, @@ -41,17 +43,17 @@ func TestPrometheusMetrics(t *testing.T) { }, { name: "Incrementing different kind of processed events should measure the processed events counter", - addMetrics: func(p *metrics.Prometheus) { - p.IncResourceEventProcessed(controller, metrics.AddEvent) - p.IncResourceEventProcessedError(controller, metrics.AddEvent) - p.IncResourceEventProcessedError(controller, metrics.AddEvent) - p.IncResourceEventProcessed(controller, metrics.DeleteEvent) - p.IncResourceEventProcessed(controller, metrics.DeleteEvent) - p.IncResourceEventProcessed(controller, metrics.DeleteEvent) - p.IncResourceEventProcessedError(controller, metrics.DeleteEvent) - p.IncResourceEventProcessedError(controller, metrics.DeleteEvent) - p.IncResourceEventProcessedError(controller, metrics.DeleteEvent) - p.IncResourceEventProcessedError(controller, metrics.DeleteEvent) + addMetrics: func(r *kooperprometheus.Recorder) { + r.IncResourceEventProcessed(context.TODO(), controllerID, controller.AddEvent) + r.IncResourceEventProcessedError(context.TODO(), controllerID, controller.AddEvent) + r.IncResourceEventProcessedError(context.TODO(), controllerID, controller.AddEvent) + r.IncResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent) + r.IncResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent) + r.IncResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent) + r.IncResourceEventProcessedError(context.TODO(), controllerID, controller.DeleteEvent) + r.IncResourceEventProcessedError(context.TODO(), controllerID, controller.DeleteEvent) + r.IncResourceEventProcessedError(context.TODO(), controllerID, controller.DeleteEvent) + r.IncResourceEventProcessedError(context.TODO(), controllerID, controller.DeleteEvent) }, expMetrics: []string{ @@ -64,20 +66,20 @@ func TestPrometheusMetrics(t *testing.T) { }, { name: "Measuring the duration of processed events return the correct buckets.", - addMetrics: func(p *metrics.Prometheus) { + addMetrics: func(r *kooperprometheus.Recorder) { now := time.Now() - p.ObserveDurationResourceEventProcessed(controller, metrics.AddEvent, now.Add(-2*time.Millisecond)) - p.ObserveDurationResourceEventProcessed(controller, metrics.AddEvent, now.Add(-3*time.Millisecond)) - p.ObserveDurationResourceEventProcessed(controller, metrics.AddEvent, now.Add(-11*time.Millisecond)) - p.ObserveDurationResourceEventProcessed(controller, metrics.AddEvent, now.Add(-280*time.Millisecond)) - p.ObserveDurationResourceEventProcessed(controller, metrics.AddEvent, now.Add(-1*time.Second)) - p.ObserveDurationResourceEventProcessed(controller, metrics.AddEvent, now.Add(-5*time.Second)) - p.ObserveDurationResourceEventProcessed(controller, metrics.DeleteEvent, now.Add(-110*time.Millisecond)) - p.ObserveDurationResourceEventProcessed(controller, metrics.DeleteEvent, now.Add(-560*time.Millisecond)) - p.ObserveDurationResourceEventProcessed(controller, metrics.DeleteEvent, now.Add(-4*time.Second)) - p.ObserveDurationResourceEventProcessed(controller, metrics.DeleteEvent, now.Add(-7*time.Second)) - p.ObserveDurationResourceEventProcessed(controller, metrics.DeleteEvent, now.Add(-12*time.Second)) - p.ObserveDurationResourceEventProcessed(controller, metrics.DeleteEvent, now.Add(-30*time.Second)) + r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.AddEvent, now.Add(-2*time.Millisecond)) + r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.AddEvent, now.Add(-3*time.Millisecond)) + r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.AddEvent, now.Add(-11*time.Millisecond)) + r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.AddEvent, now.Add(-280*time.Millisecond)) + r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.AddEvent, now.Add(-1*time.Second)) + r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.AddEvent, now.Add(-5*time.Second)) + r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent, now.Add(-110*time.Millisecond)) + r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent, now.Add(-560*time.Millisecond)) + r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent, now.Add(-4*time.Second)) + r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent, now.Add(-7*time.Second)) + r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent, now.Add(-12*time.Second)) + r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent, now.Add(-30*time.Second)) }, expMetrics: []string{ `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="add",le="0.005"} 2`, @@ -118,7 +120,9 @@ func TestPrometheusMetrics(t *testing.T) { // Create a new prometheus empty registry and a kooper prometheus recorder. reg := prometheus.NewRegistry() - m := metrics.NewPrometheus(reg) + m := kooperprometheus.New(kooperprometheus.Config{ + Registerer: reg, + }) // Add desired metrics test.addMetrics(m) diff --git a/monitoring/metrics/dummy.go b/monitoring/metrics/dummy.go deleted file mode 100644 index 11cdbe39..00000000 --- a/monitoring/metrics/dummy.go +++ /dev/null @@ -1,17 +0,0 @@ -package metrics - -import "time" - -// Dummy is a dummy stats recorder. -var Dummy = &dummy{} - -type dummy struct{} - -func (*dummy) IncResourceEventQueued(_ string, _ EventType) { -} -func (*dummy) IncResourceEventProcessed(_ string, _ EventType) { -} -func (*dummy) IncResourceEventProcessedError(_ string, _ EventType) { -} -func (*dummy) ObserveDurationResourceEventProcessed(_ string, _ EventType, _ time.Time) { -} diff --git a/monitoring/metrics/metrics.go b/monitoring/metrics/metrics.go deleted file mode 100644 index f4726eef..00000000 --- a/monitoring/metrics/metrics.go +++ /dev/null @@ -1,27 +0,0 @@ -package metrics - -import "time" - -// EventType is the event type handled by the controller. -type EventType string - -const ( - //AddEvent is the add event. - AddEvent EventType = "add" - // DeleteEvent is the delete event. - DeleteEvent EventType = "delete" - // RequeueEvent is a requeued event (unknown state when handling again). - RequeueEvent EventType = "requeue" -) - -// Recorder knows how to record metrics all over the application. -type Recorder interface { - // IncResourceEvent increments in one the metric records of a queued event. - IncResourceEventQueued(controller string, eventType EventType) - // IncResourceEventProcessed increments in one the metric records processed event. - IncResourceEventProcessed(controller string, eventType EventType) - // IncResourceEventProcessedError increments in one the metric records of a processed event in error. - IncResourceEventProcessedError(controller string, eventType EventType) - // ObserveDurationResourceEventProcessed measures the duration it took to process a event. - ObserveDurationResourceEventProcessed(controller string, eventType EventType, start time.Time) -} diff --git a/monitoring/metrics/prometheus.go b/monitoring/metrics/prometheus.go deleted file mode 100644 index f241176b..00000000 --- a/monitoring/metrics/prometheus.go +++ /dev/null @@ -1,100 +0,0 @@ -package metrics - -import ( - "time" - - "github.com/prometheus/client_golang/prometheus" -) - -const ( - promNamespace = "kooper" - promControllerSubsystem = "controller" -) - -// Prometheus implements the metrics recording in a prometheus registry. -type Prometheus struct { - // Metrics - queuedEvents *prometheus.CounterVec - processedEvents *prometheus.CounterVec - processedEventErrors *prometheus.CounterVec - processedEventDuration *prometheus.HistogramVec - - reg prometheus.Registerer -} - -// NewPrometheus returns a new Prometheus metrics backend with metrics prefixed by the namespace. -func NewPrometheus(registry prometheus.Registerer) *Prometheus { - return NewPrometheusWithBuckets(prometheus.DefBuckets, registry) -} - -// NewPrometheusWithBuckets returns a new Prometheus metrics backend with metrics prefixed by the -// namespace and with custom buckets for the duration/latency metrics. This kind should be used when -// the default buckets don't work. This could happen when the time to process an event is not on the -// range of 5ms-10s duration. -// Check https://godoc.org/github.com/prometheus/client_golang/prometheus#pkg-variables -func NewPrometheusWithBuckets(buckets []float64, registry prometheus.Registerer) *Prometheus { - p := &Prometheus{ - queuedEvents: prometheus.NewCounterVec(prometheus.CounterOpts{ - Namespace: promNamespace, - Subsystem: promControllerSubsystem, - Name: "queued_events_total", - Help: "Total number of events queued.", - }, []string{"controller", "type"}), - - processedEvents: prometheus.NewCounterVec(prometheus.CounterOpts{ - Namespace: promNamespace, - Subsystem: promControllerSubsystem, - Name: "processed_events_total", - Help: "Total number of successfuly processed events.", - }, []string{"controller", "type"}), - - processedEventErrors: prometheus.NewCounterVec(prometheus.CounterOpts{ - Namespace: promNamespace, - Subsystem: promControllerSubsystem, - Name: "processed_event_errors_total", - Help: "Total number of errors processing events.", - }, []string{"controller", "type"}), - - processedEventDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{ - Namespace: promNamespace, - Subsystem: promControllerSubsystem, - Name: "processed_event_duration_seconds", - Help: "The duration for a successful event to be processed.", - Buckets: buckets, - }, []string{"controller", "type"}), - reg: registry, - } - - p.registerMetrics() - return p -} - -func (p *Prometheus) registerMetrics() { - p.reg.MustRegister( - p.queuedEvents, - p.processedEvents, - p.processedEventErrors, - p.processedEventDuration) - -} - -// IncResourceEventQueued satisfies metrics.Recorder interface. -func (p *Prometheus) IncResourceEventQueued(controller string, eventType EventType) { - p.queuedEvents.WithLabelValues(controller, string(eventType)).Inc() -} - -// IncResourceEventProcessed satisfies metrics.Recorder interface. -func (p *Prometheus) IncResourceEventProcessed(controller string, eventType EventType) { - p.processedEvents.WithLabelValues(controller, string(eventType)).Inc() -} - -// IncResourceEventProcessedError satisfies metrics.Recorder interface. -func (p *Prometheus) IncResourceEventProcessedError(controller string, eventType EventType) { - p.processedEventErrors.WithLabelValues(controller, string(eventType)).Inc() -} - -// ObserveDurationResourceEventProcessed satisfies metrics.Recorder interface. -func (p *Prometheus) ObserveDurationResourceEventProcessed(controller string, eventType EventType, start time.Time) { - secs := time.Now().Sub(start).Seconds() - p.processedEventDuration.WithLabelValues(controller, string(eventType)).Observe(secs) -} From 1d5f64851af395f47daa93aae86ca76e37a7a07e Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Wed, 1 Apr 2020 11:53:37 +0200 Subject: [PATCH 11/31] Refactor metrics and the internal queue Signed-off-by: Xabier Larrakoetxea --- controller/generic.go | 61 ++++--- controller/handler.go | 43 ----- controller/metrics.go | 32 +--- controller/processor.go | 102 +++++------ controller/queue.go | 141 +++++++++++++++ metrics/prometheus/prometheus.go | 83 ++++----- metrics/prometheus/prometheus_test.go | 243 ++++++++++++++++---------- 7 files changed, 427 insertions(+), 278 deletions(-) create mode 100644 controller/queue.go diff --git a/controller/generic.go b/controller/generic.go index 79a8d9d3..3ceb8e60 100644 --- a/controller/generic.go +++ b/controller/generic.go @@ -98,9 +98,9 @@ func (c *Config) setDefaults() error { // generic controller is a controller that can be used to create different kind of controllers. type generic struct { - queue workqueue.RateLimitingInterface // queue will have the jobs that the controller will get and send to handlers. - informer cache.SharedIndexInformer // informer will notify be inform us about resource changes. - processor processor // processor will call the user handler (logic). + queue blockingQueue // queue will have the jobs that the controller will get and send to handlers. + informer cache.SharedIndexInformer // informer will notify be inform us about resource changes. + processor processor // processor will call the user handler (logic). running bool runningMu sync.Mutex @@ -130,9 +130,17 @@ func New(cfg *Config) (Controller, error) { return nil, fmt.Errorf("could no create controller: %w: %v", ErrControllerNotValid, err) } - // Create the queue that will have our received job changes. It's rate limited so we don't have problems when - // a job processing errors every time is processed in a loop. - queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()) + // Create the queue that will have our received job changes. + queue := newRateLimitingBlockingQueue( + cfg.ProcessingJobRetries, + workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()), + ) + queue = newMetricsBlockingQueue( + cfg.Name, + cfg.MetricsRecorder, + queue, + cfg.Logger, + ) // store is the internal cache where objects will be store. store := cache.Indexers{} @@ -145,33 +153,36 @@ func New(cfg *Config) (Controller, error) { informer.AddEventHandlerWithResyncPeriod(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { key, err := cache.MetaNamespaceKeyFunc(obj) - if err == nil { - queue.Add(key) - cfg.MetricsRecorder.IncResourceEventQueued(context.TODO(), cfg.Name, AddEvent) + if err != nil { + cfg.Logger.Warningf("could not add item from 'add' event to queue: %s", err) + return } + queue.Add(context.TODO(), key) }, UpdateFunc: func(old interface{}, new interface{}) { key, err := cache.MetaNamespaceKeyFunc(new) - if err == nil { - queue.Add(key) - cfg.MetricsRecorder.IncResourceEventQueued(context.TODO(), cfg.Name, AddEvent) + if err != nil { + cfg.Logger.Warningf("could not add item from 'update' event to queue: %s", err) + return } + queue.Add(context.TODO(), key) }, DeleteFunc: func(obj interface{}) { key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) - if err == nil { - queue.Add(key) - cfg.MetricsRecorder.IncResourceEventQueued(context.TODO(), cfg.Name, DeleteEvent) + if err != nil { + cfg.Logger.Warningf("could not add item from 'delete' event to queue: %s", err) + return } + queue.Add(context.TODO(), key) }, }, cfg.ResyncInterval) - // Create processing chain processor(+middlewares) -> handler(+middlewares). - handler := newMetricsMeasuredHandler(cfg.Name, cfg.MetricsRecorder, cfg.Handler) - processor := newIndexerProcessor(informer.GetIndexer(), handler) + // Create processing chain: processor(+middlewares) -> handler(+middlewares). + processor := newIndexerProcessor(informer.GetIndexer(), cfg.Handler) if cfg.ProcessingJobRetries > 0 { - processor = newRetryProcessor(cfg.Name, cfg.ProcessingJobRetries, cfg.MetricsRecorder, queue, processor) + processor = newRetryProcessor(cfg.Name, cfg.ProcessingJobRetries, queue, cfg.Logger, processor) } + processor = newMetricsProcessor(cfg.Name, cfg.MetricsRecorder, processor) // Create our generic controller object. return &generic{ @@ -222,7 +233,7 @@ func (g *generic) run(stopC <-chan struct{}) error { // Shutdown when Run is stopped so we can process the last items and the queue doesn't // accept more jobs. - defer g.queue.ShutDown() + defer g.queue.ShutDown(context.TODO()) // Run the informer so it starts listening to resource events. go g.informer.Run(stopC) @@ -263,16 +274,18 @@ func (g *generic) runWorker() { // // If the queue has been closed then it will end the processing. func (g *generic) processNextJob() bool { + ctx := context.Background() + // Get next job. - nextJob, exit := g.queue.Get() + nextJob, exit := g.queue.Get(ctx) if exit { return true } - defer g.queue.Done(nextJob) + + defer g.queue.Done(ctx, nextJob) key := nextJob.(string) - // Process the job. If errors then enqueue again. - ctx := context.Background() + // Process the job. err := g.processor.Process(ctx, key) logger := g.logger.WithKV(log.KV{"object-key": key}) diff --git a/controller/handler.go b/controller/handler.go index 3e9c38eb..c6a53077 100644 --- a/controller/handler.go +++ b/controller/handler.go @@ -3,7 +3,6 @@ package controller import ( "context" "fmt" - "time" "k8s.io/apimachinery/pkg/runtime" ) @@ -42,45 +41,3 @@ func (h *HandlerFunc) Delete(ctx context.Context, s string) error { } return h.DeleteFunc(ctx, s) } - -type metricsMeasuredHandler struct { - id string - mrec MetricsRecorder - next Handler -} - -func newMetricsMeasuredHandler(id string, mrec MetricsRecorder, next Handler) Handler { - return metricsMeasuredHandler{ - id: id, - mrec: mrec, - next: next, - } -} - -func (m metricsMeasuredHandler) Add(ctx context.Context, obj runtime.Object) (err error) { - defer func(start time.Time) { - m.mrec.ObserveDurationResourceEventProcessed(ctx, m.id, AddEvent, start) - - if err != nil { - m.mrec.IncResourceEventProcessedError(ctx, m.id, AddEvent) - } - }(time.Now()) - - m.mrec.IncResourceEventProcessed(ctx, m.id, AddEvent) - - return m.next.Add(ctx, obj) -} - -func (m metricsMeasuredHandler) Delete(ctx context.Context, objKey string) (err error) { - defer func(start time.Time) { - m.mrec.ObserveDurationResourceEventProcessed(ctx, m.id, DeleteEvent, start) - - if err != nil { - m.mrec.IncResourceEventProcessedError(ctx, m.id, DeleteEvent) - } - }(time.Now()) - - m.mrec.IncResourceEventProcessed(ctx, m.id, DeleteEvent) - - return m.next.Delete(ctx, objKey) -} diff --git a/controller/metrics.go b/controller/metrics.go index a8736efb..8b2dc341 100644 --- a/controller/metrics.go +++ b/controller/metrics.go @@ -5,28 +5,15 @@ import ( "time" ) -// EventType is the event type of a controller enqueued object. -type EventType string - -const ( - //AddEvent is the add event. - AddEvent EventType = "add" - // DeleteEvent is the delete event. - DeleteEvent EventType = "delete" - // RequeueEvent is a requeued event (unknown state when handling again). - RequeueEvent EventType = "requeue" -) - // MetricsRecorder knows how to record metrics of a controller. type MetricsRecorder interface { // IncResourceEvent increments in one the metric records of a queued event. - IncResourceEventQueued(ctx context.Context, controller string, eventType EventType) - // IncResourceEventProcessed increments in one the metric records processed event. - IncResourceEventProcessed(ctx context.Context, controller string, eventType EventType) - // IncResourceEventProcessedError increments in one the metric records of a processed event in error. - IncResourceEventProcessedError(ctx context.Context, controller string, eventType EventType) - // ObserveDurationResourceEventProcessed measures the duration it took to process a event. - ObserveDurationResourceEventProcessed(ctx context.Context, controller string, eventType EventType, start time.Time) + IncResourceEventQueued(ctx context.Context, controller string, isRequeue bool) + // ObserveResourceInQueueDuration measures how long takes to dequeue a queued object. If the object is already in queue + // it will be measured once, since the first time it was added to the queue. + ObserveResourceInQueueDuration(ctx context.Context, controller string, queuedAt time.Time) + // ObserveResourceProcessingDuration measures how long it takes to process a resources (handling). + ObserveResourceProcessingDuration(ctx context.Context, controller string, success bool, startProcessingAt time.Time) } // DummyMetricsRecorder is a dummy metrics recorder. @@ -34,7 +21,6 @@ var DummyMetricsRecorder = dummy(0) type dummy int -func (dummy) IncResourceEventQueued(_ context.Context, _ string, _ EventType) {} -func (dummy) IncResourceEventProcessed(_ context.Context, _ string, _ EventType) {} -func (dummy) IncResourceEventProcessedError(_ context.Context, _ string, _ EventType) {} -func (dummy) ObserveDurationResourceEventProcessed(_ context.Context, _ string, _ EventType, _ time.Time) {} +func (dummy) IncResourceEventQueued(context.Context, string, bool) {} +func (dummy) ObserveResourceInQueueDuration(context.Context, string, time.Time) {} +func (dummy) ObserveResourceProcessingDuration(context.Context, string, bool, time.Time) {} diff --git a/controller/processor.go b/controller/processor.go index 4cac7283..615a1925 100644 --- a/controller/processor.go +++ b/controller/processor.go @@ -3,10 +3,12 @@ package controller import ( "context" "fmt" + "time" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/cache" - "k8s.io/client-go/util/workqueue" + + "github.com/spotahome/kooper/log" ) // processor knows how to process object keys. @@ -14,72 +16,62 @@ type processor interface { Process(ctx context.Context, key string) error } -func newIndexerProcessor(indexer cache.Indexer, handler Handler) processor { - return indexerProcessor{ - indexer: indexer, - handler: handler, - } -} +// processorFunc a helper to create processors. +type processorFunc func(ctx context.Context, key string) error -// indexerProcessor processes a key that will get the kubernetes object from a cache -// called indexer were the kubernetes watch updates have been indexed and stored by the -// listerwatchers from the informers. -type indexerProcessor struct { - indexer cache.Indexer - handler Handler -} +func (p processorFunc) Process(ctx context.Context, key string) error { return p(ctx, key) } -func (i indexerProcessor) Process(ctx context.Context, key string) error { - // Get the object - obj, exists, err := i.indexer.GetByKey(key) - if err != nil { - return err - } +// newIndexerProcessor returns a processor that processes a key that will get the kubernetes object +// from a cache called indexer were the kubernetes watch updates have been indexed and stored +// by the listerwatchers from the informers. +func newIndexerProcessor(indexer cache.Indexer, handler Handler) processor { + return processorFunc(func(ctx context.Context, key string) error { + // Get the object + obj, exists, err := indexer.GetByKey(key) + if err != nil { + return err + } - // handle the object. - if !exists { // Deleted resource from the cache. - return i.handler.Delete(ctx, key) - } + // Handle the object. + if !exists { + return handler.Delete(ctx, key) + } - return i.handler.Add(ctx, obj.(runtime.Object)) + return handler.Add(ctx, obj.(runtime.Object)) + }) } var errRequeued = fmt.Errorf("requeued after receiving error") -// retryProcessor will delegate the processing of a key to the received processor, -// in case the processing/handling of this key fails it will add the key +// newRetryProcessor returns a processor that will delegate the processing of a key to the +// received processor, in case the processing/handling of this key fails it will add the key // again to a queue if it has retrys pending. // -// If the processing errored and has been retried, it will return a `errRequeued` -// error. -type retryProcessor struct { - name string - maxRetries int - mrec MetricsRecorder - queue workqueue.RateLimitingInterface - next processor -} +// If the processing errored and has been retried, it will return a `errRequeued` error. +func newRetryProcessor(name string, maxRetries int, queue blockingQueue, logger log.Logger, next processor) processor { + return processorFunc(func(ctx context.Context, key string) error { + err := next.Process(ctx, key) + if err != nil { + // Retry if possible. + requeueErr := queue.Requeue(ctx, key) + if requeueErr != nil { + return fmt.Errorf("could not retry: %s: %w", requeueErr, err) + } + logger.WithKV(log.KV{"object-key": key}).Warningf("item requeued due to processing error: %s", err) + return nil + } -func newRetryProcessor(name string, maxRetries int, mrec MetricsRecorder, queue workqueue.RateLimitingInterface, next processor) processor { - return retryProcessor{ - name: name, - maxRetries: maxRetries, - mrec: mrec, - queue: queue, - next: next, - } + return nil + }) } -func (r retryProcessor) Process(ctx context.Context, key string) error { - err := r.next.Process(ctx, key) - - // If there was an error and we have retries pending then requeue. - if err != nil && r.queue.NumRequeues(key) < r.maxRetries { - r.queue.AddRateLimited(key) - r.mrec.IncResourceEventQueued(ctx, r.name, RequeueEvent) - return fmt.Errorf("%w: %s", errRequeued, err) - } +// newMetricsProcessor returns a processor that measures everything related with the processing logic. +func newMetricsProcessor(name string, mrec MetricsRecorder, next processor) processor { + return processorFunc(func(ctx context.Context, key string) (err error) { + defer func(t0 time.Time) { + mrec.ObserveResourceProcessingDuration(ctx, name, err == nil, t0) + }(time.Now()) - r.queue.Forget(key) - return err + return next.Process(ctx, key) + }) } diff --git a/controller/queue.go b/controller/queue.go new file mode 100644 index 00000000..995246c4 --- /dev/null +++ b/controller/queue.go @@ -0,0 +1,141 @@ +package controller + +import ( + "context" + "fmt" + "sync" + "time" + + "k8s.io/client-go/util/workqueue" + + "github.com/spotahome/kooper/log" +) + +// blockingQueue is a queue that any of its implementations should +// implement a blocking mechanism when the +type blockingQueue interface { + // Add will add an item to the queue. + Add(ctx context.Context, item interface{}) + // Requeue will add an item to the queue in a requeue mode. + // If doesn't accept requeueing or max requeue have been reached + // it will return an error. + Requeue(ctx context.Context, item interface{}) error + // Get is a blocking operation, if the last object usage has not been finished (`done`) + // being used it will block until this has been done. + Get(ctx context.Context) (item interface{}, shutdown bool) + // Done marks the item being used as done. + Done(ctx context.Context, item interface{}) + // ShutDown stops the queue from accepting new jobs + ShutDown(ctx context.Context) +} + +var ( + errMaxRetriesReached = fmt.Errorf("max retries reached") +) + +type rateLimitingBlockingQueue struct { + maxRetries int + queue workqueue.RateLimitingInterface +} + +func newRateLimitingBlockingQueue(maxRetries int, queue workqueue.RateLimitingInterface) blockingQueue { + return rateLimitingBlockingQueue{ + maxRetries: maxRetries, + queue: queue, + } +} + +func (r rateLimitingBlockingQueue) Add(_ context.Context, item interface{}) { + r.queue.Add(item) +} + +func (r rateLimitingBlockingQueue) Requeue(_ context.Context, item interface{}) error { + // If there was an error and we have retries pending then requeue. + if r.queue.NumRequeues(item) < r.maxRetries { + r.queue.AddRateLimited(item) + return nil + } + + r.queue.Forget(item) + return errMaxRetriesReached +} + +func (r rateLimitingBlockingQueue) Get(_ context.Context) (item interface{}, shutdown bool) { + return r.queue.Get() +} + +func (r rateLimitingBlockingQueue) Done(_ context.Context, item interface{}) { + r.queue.Done(item) +} + +func (r rateLimitingBlockingQueue) ShutDown(_ context.Context) { + r.queue.ShutDown() +} + +// metricsQueue is a wrapper for a metrics measured queue. +type metricsBlockingQueue struct { + mu sync.Mutex + name string + mrec MetricsRecorder + itemsQueuedAt map[interface{}]time.Time + logger log.Logger + queue blockingQueue +} + +func newMetricsBlockingQueue(name string, mrec MetricsRecorder, queue blockingQueue, logger log.Logger) blockingQueue { + return &metricsBlockingQueue{ + name: name, + mrec: mrec, + itemsQueuedAt: map[interface{}]time.Time{}, + logger: logger, + queue: queue, + } +} + +func (m *metricsBlockingQueue) Add(ctx context.Context, item interface{}) { + m.mu.Lock() + if _, ok := m.itemsQueuedAt[item]; !ok { + m.itemsQueuedAt[item] = time.Now() + } + m.mu.Unlock() + + m.mrec.IncResourceEventQueued(ctx, m.name, false) + m.queue.Add(ctx, item) +} + +func (m *metricsBlockingQueue) Requeue(ctx context.Context, item interface{}) error { + m.mu.Lock() + if _, ok := m.itemsQueuedAt[item]; !ok { + m.itemsQueuedAt[item] = time.Now() + } + m.mu.Unlock() + + m.mrec.IncResourceEventQueued(ctx, m.name, true) + return m.queue.Requeue(ctx, item) +} + +func (m *metricsBlockingQueue) Get(ctx context.Context) (interface{}, bool) { + // Here should get blocked, warning with the mutexes. + item, shutdown := m.queue.Get(ctx) + + m.mu.Lock() + queuedAt, ok := m.itemsQueuedAt[item] + if ok { + m.mrec.ObserveResourceInQueueDuration(ctx, m.name, queuedAt) + delete(m.itemsQueuedAt, item) + } else { + m.logger.WithKV(log.KV{"object-key": item}). + Infof("could not measure item because item is not present on metricsMeasuredQueue.itemsQueuedAt map") + } + m.mu.Unlock() + + return item, shutdown +} + +func (m *metricsBlockingQueue) Done(ctx context.Context, item interface{}) { + m.queue.Done(ctx, item) +} + +func (m *metricsBlockingQueue) ShutDown(ctx context.Context) { + m.queue.ShutDown(ctx) +} diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index 916e1213..b0b1f667 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -2,6 +2,7 @@ package prometheus import ( "context" + "strconv" "time" "github.com/prometheus/client_golang/prometheus" @@ -18,11 +19,12 @@ type Config struct { // Registerer is a prometheus registerer, e.g: prometheus.Registry. // By default will use Prometheus default registry. Registerer prometheus.Registerer - // Buckets sets custom buckets for the duration/latency metrics. This should be used when - // the default buckets don't work. This could happen when the time to process an event is not on the - // range of 5ms-10s duration. + // InQueueBuckets sets custom buckets for the duration/latency items in queue metrics. // Check https://godoc.org/github.com/prometheus/client_golang/prometheus#pkg-variables - Buckets []float64 + InQueueBuckets []float64 + // ProcessingBuckets sets custom buckets for the duration/latency processing metrics. + // Check https://godoc.org/github.com/prometheus/client_golang/prometheus#pkg-variables + ProcessingBuckets []float64 } func (c *Config) defaults() { @@ -30,17 +32,19 @@ func (c *Config) defaults() { c.Registerer = prometheus.DefaultRegisterer } - if c.Buckets == nil || len(c.Buckets) == 0 { - c.Buckets = prometheus.DefBuckets + if c.InQueueBuckets == nil || len(c.InQueueBuckets) == 0 { + c.InQueueBuckets = prometheus.DefBuckets + } + + if c.ProcessingBuckets == nil || len(c.ProcessingBuckets) == 0 { + c.ProcessingBuckets = prometheus.DefBuckets } } // Recorder implements the metrics recording in a prometheus registry. type Recorder struct { - // Metrics - queuedEvents *prometheus.CounterVec - processedEvents *prometheus.CounterVec - processedEventErrors *prometheus.CounterVec + queuedEventsTotal *prometheus.CounterVec + inQueueEventDuration *prometheus.HistogramVec processedEventDuration *prometheus.HistogramVec } @@ -49,66 +53,55 @@ func New(cfg Config) *Recorder { cfg.defaults() r := &Recorder{ - queuedEvents: prometheus.NewCounterVec(prometheus.CounterOpts{ + queuedEventsTotal: prometheus.NewCounterVec(prometheus.CounterOpts{ Namespace: promNamespace, Subsystem: promControllerSubsystem, Name: "queued_events_total", Help: "Total number of events queued.", - }, []string{"controller", "type"}), - - processedEvents: prometheus.NewCounterVec(prometheus.CounterOpts{ - Namespace: promNamespace, - Subsystem: promControllerSubsystem, - Name: "processed_events_total", - Help: "Total number of successfuly processed events.", - }, []string{"controller", "type"}), + }, []string{"controller", "requeue"}), - processedEventErrors: prometheus.NewCounterVec(prometheus.CounterOpts{ + inQueueEventDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: promNamespace, Subsystem: promControllerSubsystem, - Name: "processed_event_errors_total", - Help: "Total number of errors processing events.", - }, []string{"controller", "type"}), + Name: "event_in_queue_duration_total", + Help: "The duration of an event in the queue.", + Buckets: cfg.InQueueBuckets, + }, []string{"controller"}), processedEventDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: promNamespace, Subsystem: promControllerSubsystem, Name: "processed_event_duration_seconds", - Help: "The duration for a successful event to be processed.", - Buckets: cfg.Buckets, - }, []string{"controller", "type"}), + Help: "The duration for an event to be processed.", + Buckets: cfg.ProcessingBuckets, + }, []string{"controller", "success"}), } // Register metrics. cfg.Registerer.MustRegister( - r.queuedEvents, - r.processedEvents, - r.processedEventErrors, + r.queuedEventsTotal, + r.inQueueEventDuration, r.processedEventDuration) return r } -// IncResourceEventQueued satisfies metrics.Recorder interface. -func (r Recorder) IncResourceEventQueued(_ context.Context, controller string, eventType controller.EventType) { - r.queuedEvents.WithLabelValues(controller, string(eventType)).Inc() -} - -// IncResourceEventProcessed satisfies metrics.Recorder interface. -func (r Recorder) IncResourceEventProcessed(_ context.Context, controller string, eventType controller.EventType) { - r.processedEvents.WithLabelValues(controller, string(eventType)).Inc() +// IncResourceEventQueued satisfies controller.MetricsRecorder interface. +func (r Recorder) IncResourceEventQueued(ctx context.Context, controller string, isRequeue bool) { + r.queuedEventsTotal.WithLabelValues(controller, strconv.FormatBool(isRequeue)).Inc() } -// IncResourceEventProcessedError satisfies metrics.Recorder interface. -func (r Recorder) IncResourceEventProcessedError(_ context.Context, controller string, eventType controller.EventType) { - r.processedEventErrors.WithLabelValues(controller, string(eventType)).Inc() +// ObserveResourceInQueueDuration satisfies controller.MetricsRecorder interface. +func (r Recorder) ObserveResourceInQueueDuration(ctx context.Context, controller string, queuedAt time.Time) { + r.inQueueEventDuration.WithLabelValues(controller). + Observe(time.Since(queuedAt).Seconds()) } -// ObserveDurationResourceEventProcessed satisfies metrics.Recorder interface. -func (r Recorder) ObserveDurationResourceEventProcessed(_ context.Context, controller string, eventType controller.EventType, start time.Time) { - secs := time.Now().Sub(start).Seconds() - r.processedEventDuration.WithLabelValues(controller, string(eventType)).Observe(secs) +// ObserveResourceProcessingDuration satisfies controller.MetricsRecorder interface. +func (r Recorder) ObserveResourceProcessingDuration(ctx context.Context, controller string, success bool, startProcessingAt time.Time) { + r.processedEventDuration.WithLabelValues(controller, strconv.FormatBool(success)). + Observe(time.Since(startProcessingAt).Seconds()) } -// Check we implement all the required metrics recorder interfaces. +// Check interfaces implementation. var _ controller.MetricsRecorder = &Recorder{} diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go index 64ec3569..07289e43 100644 --- a/metrics/prometheus/prometheus_test.go +++ b/metrics/prometheus/prometheus_test.go @@ -11,118 +11,187 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/stretchr/testify/assert" - "github.com/spotahome/kooper/controller" kooperprometheus "github.com/spotahome/kooper/metrics/prometheus" ) func TestPrometheusRecorder(t *testing.T) { - controllerID := "test" - - tests := []struct { - name string + tests := map[string]struct { + cfg kooperprometheus.Config addMetrics func(*kooperprometheus.Recorder) expMetrics []string - expCode int }{ - { - name: "Incrementing different kind of queued events should measure the queued events counter", + "Incremeneting the total queued resource events should record the metrics.": { + addMetrics: func(r *kooperprometheus.Recorder) { + ctx := context.TODO() + r.IncResourceEventQueued(ctx, "ctrl1", false) + r.IncResourceEventQueued(ctx, "ctrl1", false) + r.IncResourceEventQueued(ctx, "ctrl2", false) + r.IncResourceEventQueued(ctx, "ctrl3", true) + r.IncResourceEventQueued(ctx, "ctrl3", true) + r.IncResourceEventQueued(ctx, "ctrl3", false) + }, + expMetrics: []string{ + `kooper_controller_queued_events_total{controller="ctrl1",requeue="false"} 2`, + `kooper_controller_queued_events_total{controller="ctrl2",requeue="false"} 1`, + `kooper_controller_queued_events_total{controller="ctrl3",requeue="false"} 1`, + `kooper_controller_queued_events_total{controller="ctrl3",requeue="true"} 2`, + }, + }, + + "Observing the duration in queue of events should record the metrics.": { addMetrics: func(r *kooperprometheus.Recorder) { - r.IncResourceEventQueued(context.TODO(), controllerID, controller.AddEvent) - r.IncResourceEventQueued(context.TODO(), controllerID, controller.AddEvent) - r.IncResourceEventQueued(context.TODO(), controllerID, controller.AddEvent) - r.IncResourceEventQueued(context.TODO(), controllerID, controller.AddEvent) - r.IncResourceEventQueued(context.TODO(), controllerID, controller.DeleteEvent) - r.IncResourceEventQueued(context.TODO(), controllerID, controller.DeleteEvent) - r.IncResourceEventQueued(context.TODO(), controllerID, controller.DeleteEvent) + ctx := context.TODO() + t0 := time.Now() + r.ObserveResourceInQueueDuration(ctx, "ctrl1", t0.Add(-3*time.Second)) + r.ObserveResourceInQueueDuration(ctx, "ctrl1", t0.Add(-280*time.Millisecond)) + r.ObserveResourceInQueueDuration(ctx, "ctrl2", t0.Add(-7*time.Second)) + r.ObserveResourceInQueueDuration(ctx, "ctrl2", t0.Add(-35*time.Millisecond)) + r.ObserveResourceInQueueDuration(ctx, "ctrl2", t0.Add(-770*time.Millisecond)) + r.ObserveResourceInQueueDuration(ctx, "ctrl2", t0.Add(-17*time.Millisecond)) }, expMetrics: []string{ - `kooper_controller_queued_events_total{controller="test",type="add"} 4`, - `kooper_controller_queued_events_total{controller="test",type="delete"} 3`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="0.005"} 0`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="0.01"} 0`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="0.025"} 0`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="0.05"} 0`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="0.1"} 0`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="0.25"} 0`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="0.5"} 1`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="1"} 1`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="2.5"} 1`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="5"} 2`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="10"} 2`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="+Inf"} 2`, + `kooper_controller_event_in_queue_duration_total_count{controller="ctrl1"} 2`, + + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="0.005"} 0`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="0.01"} 0`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="0.025"} 1`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="0.05"} 2`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="0.1"} 2`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="0.25"} 2`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="0.5"} 2`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="1"} 3`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="2.5"} 3`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="5"} 3`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="10"} 4`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="+Inf"} 4`, + `kooper_controller_event_in_queue_duration_total_count{controller="ctrl2"} 4`, }, - expCode: 200, }, - { - name: "Incrementing different kind of processed events should measure the processed events counter", + + "Observing the duration in queue of events should record the metrics (Custom buckets).": { + cfg: kooperprometheus.Config{ + InQueueBuckets: []float64{10, 20, 30, 50}, + }, addMetrics: func(r *kooperprometheus.Recorder) { - r.IncResourceEventProcessed(context.TODO(), controllerID, controller.AddEvent) - r.IncResourceEventProcessedError(context.TODO(), controllerID, controller.AddEvent) - r.IncResourceEventProcessedError(context.TODO(), controllerID, controller.AddEvent) - r.IncResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent) - r.IncResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent) - r.IncResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent) - r.IncResourceEventProcessedError(context.TODO(), controllerID, controller.DeleteEvent) - r.IncResourceEventProcessedError(context.TODO(), controllerID, controller.DeleteEvent) - r.IncResourceEventProcessedError(context.TODO(), controllerID, controller.DeleteEvent) - r.IncResourceEventProcessedError(context.TODO(), controllerID, controller.DeleteEvent) + ctx := context.TODO() + t0 := time.Now() + r.ObserveResourceInQueueDuration(ctx, "ctrl1", t0.Add(-6*time.Second)) + r.ObserveResourceInQueueDuration(ctx, "ctrl1", t0.Add(-12*time.Second)) + r.ObserveResourceInQueueDuration(ctx, "ctrl1", t0.Add(-25*time.Second)) + r.ObserveResourceInQueueDuration(ctx, "ctrl1", t0.Add(-60*time.Second)) + r.ObserveResourceInQueueDuration(ctx, "ctrl1", t0.Add(-70*time.Second)) }, expMetrics: []string{ - `kooper_controller_processed_events_total{controller="test",type="add"} 1`, - `kooper_controller_processed_event_errors_total{controller="test",type="add"} 2`, - `kooper_controller_processed_events_total{controller="test",type="delete"} 3`, - `kooper_controller_processed_event_errors_total{controller="test",type="delete"} 4`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="10"} 1`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="20"} 2`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="30"} 3`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="50"} 3`, + `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="+Inf"} 5`, + `kooper_controller_event_in_queue_duration_total_count{controller="ctrl1"} 5`, }, - expCode: 200, }, - { - name: "Measuring the duration of processed events return the correct buckets.", + + "Observing the duration of processing events should record the metrics.": { + addMetrics: func(r *kooperprometheus.Recorder) { + ctx := context.TODO() + t0 := time.Now() + r.ObserveResourceProcessingDuration(ctx, "ctrl1", true, t0.Add(-3*time.Second)) + r.ObserveResourceProcessingDuration(ctx, "ctrl1", true, t0.Add(-280*time.Millisecond)) + r.ObserveResourceProcessingDuration(ctx, "ctrl2", true, t0.Add(-7*time.Second)) + r.ObserveResourceProcessingDuration(ctx, "ctrl2", false, t0.Add(-35*time.Millisecond)) + r.ObserveResourceProcessingDuration(ctx, "ctrl2", true, t0.Add(-770*time.Millisecond)) + r.ObserveResourceProcessingDuration(ctx, "ctrl2", false, t0.Add(-17*time.Millisecond)) + }, + expMetrics: []string{ + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="0.005"} 0`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="0.01"} 0`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="0.025"} 0`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="0.05"} 0`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="0.1"} 0`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="0.25"} 0`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="0.5"} 1`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="1"} 1`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="2.5"} 1`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="5"} 2`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="10"} 2`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="+Inf"} 2`, + `kooper_controller_processed_event_duration_seconds_count{controller="ctrl1",success="true"} 2`, + + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="false",le="0.005"} 0`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="false",le="0.01"} 0`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="false",le="0.025"} 1`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="false",le="0.05"} 2`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="false",le="0.1"} 2`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="false",le="0.25"} 2`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="false",le="0.5"} 2`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="false",le="1"} 2`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="false",le="2.5"} 2`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="false",le="5"} 2`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="false",le="10"} 2`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="false",le="+Inf"} 2`, + `kooper_controller_processed_event_duration_seconds_count{controller="ctrl2",success="false"} 2`, + + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="true",le="0.005"} 0`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="true",le="0.01"} 0`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="true",le="0.025"} 0`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="true",le="0.05"} 0`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="true",le="0.1"} 0`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="true",le="0.25"} 0`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="true",le="0.5"} 0`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="true",le="1"} 1`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="true",le="2.5"} 1`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="true",le="5"} 1`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="true",le="10"} 2`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl2",success="true",le="+Inf"} 2`, + `kooper_controller_processed_event_duration_seconds_count{controller="ctrl2",success="true"} 2`, + }, + }, + + "Observing the duration of processing events should record the metrics (Custom buckets).": { + cfg: kooperprometheus.Config{ + ProcessingBuckets: []float64{10, 20, 30, 50}, + }, addMetrics: func(r *kooperprometheus.Recorder) { - now := time.Now() - r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.AddEvent, now.Add(-2*time.Millisecond)) - r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.AddEvent, now.Add(-3*time.Millisecond)) - r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.AddEvent, now.Add(-11*time.Millisecond)) - r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.AddEvent, now.Add(-280*time.Millisecond)) - r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.AddEvent, now.Add(-1*time.Second)) - r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.AddEvent, now.Add(-5*time.Second)) - r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent, now.Add(-110*time.Millisecond)) - r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent, now.Add(-560*time.Millisecond)) - r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent, now.Add(-4*time.Second)) - r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent, now.Add(-7*time.Second)) - r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent, now.Add(-12*time.Second)) - r.ObserveDurationResourceEventProcessed(context.TODO(), controllerID, controller.DeleteEvent, now.Add(-30*time.Second)) + ctx := context.TODO() + t0 := time.Now() + r.ObserveResourceProcessingDuration(ctx, "ctrl1", true, t0.Add(-6*time.Second)) + r.ObserveResourceProcessingDuration(ctx, "ctrl1", true, t0.Add(-12*time.Second)) + r.ObserveResourceProcessingDuration(ctx, "ctrl1", true, t0.Add(-25*time.Second)) + r.ObserveResourceProcessingDuration(ctx, "ctrl1", true, t0.Add(-60*time.Second)) + r.ObserveResourceProcessingDuration(ctx, "ctrl1", true, t0.Add(-70*time.Second)) }, expMetrics: []string{ - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="add",le="0.005"} 2`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="add",le="0.01"} 2`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="add",le="0.025"} 3`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="add",le="0.05"} 3`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="add",le="0.1"} 3`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="add",le="0.25"} 3`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="add",le="0.5"} 4`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="add",le="1"} 4`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="add",le="2.5"} 5`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="add",le="5"} 5`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="add",le="10"} 6`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="add",le="+Inf"} 6`, - `kooper_controller_processed_event_duration_seconds_count{controller="test",type="add"} 6`, - - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="delete",le="0.005"} 0`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="delete",le="0.01"} 0`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="delete",le="0.025"} 0`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="delete",le="0.05"} 0`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="delete",le="0.1"} 0`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="delete",le="0.25"} 1`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="delete",le="0.5"} 1`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="delete",le="1"} 2`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="delete",le="2.5"} 2`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="delete",le="5"} 3`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="delete",le="10"} 4`, - `kooper_controller_processed_event_duration_seconds_bucket{controller="test",type="delete",le="+Inf"} 6`, - `kooper_controller_processed_event_duration_seconds_count{controller="test",type="delete"} 6`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="10"} 1`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="20"} 2`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="30"} 3`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="50"} 3`, + `kooper_controller_processed_event_duration_seconds_bucket{controller="ctrl1",success="true",le="+Inf"} 5`, + `kooper_controller_processed_event_duration_seconds_count{controller="ctrl1",success="true"} 5`, }, - expCode: 200, }, } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { + for name, test := range tests { + t.Run(name, func(t *testing.T) { assert := assert.New(t) // Create a new prometheus empty registry and a kooper prometheus recorder. reg := prometheus.NewRegistry() - m := kooperprometheus.New(kooperprometheus.Config{ - Registerer: reg, - }) + test.cfg.Registerer = reg + m := kooperprometheus.New(test.cfg) // Add desired metrics test.addMetrics(m) @@ -135,11 +204,9 @@ func TestPrometheusRecorder(t *testing.T) { resp := w.Result() // Check all metrics are present. - if assert.Equal(test.expCode, resp.StatusCode) { - body, _ := ioutil.ReadAll(resp.Body) - for _, expMetric := range test.expMetrics { - assert.Contains(string(body), expMetric, "metric not present on the result of metrics service") - } + body, _ := ioutil.ReadAll(resp.Body) + for _, expMetric := range test.expMetrics { + assert.Contains(string(body), expMetric, "metric not present on the result of metrics service") } }) } From 7e0ce7fc2198df70ec02f4fe4240a34b35e10f06 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Thu, 2 Apr 2020 10:42:35 +0200 Subject: [PATCH 12/31] Update changelog Signed-off-by: Xabier Larrakoetxea --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d673e298..701d6717 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,8 +16,8 @@ NOTE: Breaking release in controllers. - Add Logrus helper wrapper. - Refactor to simplify the retrievers. - Add multiretriever to retriever different resource types on the same controller. -- Refactor metrics recorder implementation including the prometheus backend, the - output prometheus metrics have not been changed. +- Refactor metrics recorder implementation including the prometheus backend. +- Refactor internal controller queue into a decorator implementation approach. ## [0.8.0] - 2019-12-11 From 47d77a3f757368c9096614ac56bb421fb8c83b8a Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Tue, 7 Apr 2020 09:50:34 +0200 Subject: [PATCH 13/31] Remove Delete from handler Signed-off-by: Xabier Larrakoetxea --- CHANGELOG.md | 1 + controller/generic_test.go | 84 ++----------------- controller/handler.go | 35 ++------ controller/processor.go | 5 +- examples/config-custom-controller/main.go | 16 ++-- .../controller-concurrency-handling/main.go | 19 ++--- examples/leader-election-controller/main.go | 16 ++-- examples/metrics-controller/main.go | 14 +--- examples/multi-resource-controller/main.go | 31 +++---- .../apis/chaos/v1alpha1/types.go | 5 +- .../manifest-examples/pause.yaml | 3 +- .../chaos.spotahome.com_podterminators.yaml | 8 +- .../operator/operator.go | 76 +++++++++++++---- .../service/chaos/chaos.go | 1 - mocks/controller/Handler.go | 18 +--- .../integration/controller/controller_test.go | 67 +++++---------- test/integration/controller/generic_test.go | 16 ++-- 17 files changed, 145 insertions(+), 270 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 701d6717..403c126a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ NOTE: Breaking release in controllers. - Add multiretriever to retriever different resource types on the same controller. - Refactor metrics recorder implementation including the prometheus backend. - Refactor internal controller queue into a decorator implementation approach. +- Remove `Delete` method from `controller.Handler` and simplify to only `Handle` method ## [0.8.0] - 2019-12-11 diff --git a/controller/generic_test.go b/controller/generic_test.go index 1ec4700e..fa1b7123 100644 --- a/controller/generic_test.go +++ b/controller/generic_test.go @@ -104,7 +104,7 @@ func createNamespaceList(prefix string, q int) (*corev1.NamespaceList, []*corev1 return nsl, nss } -func TestGenericControllerHandleAdds(t *testing.T) { +func TestGenericControllerHandle(t *testing.T) { nsList, expNSAdds := createNamespaceList("testing", 10) tests := []struct { @@ -113,7 +113,7 @@ func TestGenericControllerHandleAdds(t *testing.T) { expNSAdds []*corev1.Namespace }{ { - name: "Listing multiple namespaces should call as add handlers for every namespace on list.", + name: "Listing multiple namespaces should execute the handling for every namespace on list.", nsList: nsList, expNSAdds: expNSAdds, }, @@ -134,7 +134,7 @@ func TestGenericControllerHandleAdds(t *testing.T) { callHandling := 0 // used to track the number of calls. mh := &mcontroller.Handler{} for _, ns := range test.expNSAdds { - mh.On("Add", mock.Anything, ns).Once().Return(nil).Run(func(args mock.Arguments) { + mh.On("Handle", mock.Anything, ns).Once().Return(nil).Run(func(args mock.Arguments) { callHandling++ // Check last call, if is the last call expected then stop the controller so // we can assert the expectations of the calls and finish the test. @@ -172,80 +172,6 @@ func TestGenericControllerHandleAdds(t *testing.T) { } } -func TestGenericControllerHandleDeletes(t *testing.T) { - - startNSList, expNSAdds := createNamespaceList("testing", 10) - nsDels := []*corev1.Namespace{expNSAdds[0], expNSAdds[4], expNSAdds[1]} - - tests := []struct { - name string - startNSList *corev1.NamespaceList - deleteNs []*corev1.Namespace - expDeleteNs []*corev1.Namespace - }{ - { - name: "Deleting multiple namespaces should call as delete handlers for every namespace on deleted.", - startNSList: startNSList, - deleteNs: nsDels, - expDeleteNs: nsDels, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - assert := assert.New(t) - require := require.New(t) - controllerStopperC := make(chan struct{}) - resultC := make(chan error) - - // Mocks kubernetes client. - mc := &fake.Clientset{} - // Populate cache so we ensure deletes are correctly delivered. - onKubeClientListNamespaceReturn(mc, test.startNSList) - onKubeClientWatchNamespaceReturn(mc, nil, nil, test.deleteNs) - - // Mock our handler and set expects. - callHandling := 0 // used to track the number of calls. - mh := &mcontroller.Handler{} - mh.On("Add", mock.Anything, mock.Anything).Return(nil) - for _, ns := range test.expDeleteNs { - mh.On("Delete", mock.Anything, ns.ObjectMeta.Name).Once().Return(nil).Run(func(args mock.Arguments) { - // Check last call, if is the last call expected then stop the controller so - // we can assert the expectations of the calls and finish the test. - callHandling++ - if callHandling == len(test.expDeleteNs) { - close(controllerStopperC) - } - }) - } - - c, err := controller.New(&controller.Config{ - Name: "test", - Handler: mh, - Retriever: newNamespaceRetriever(mc), - Logger: log.Dummy, - }) - require.NoError(err) - - // Run Controller in background. - go func() { - resultC <- c.Run(controllerStopperC) - }() - - // Wait for different results. If no result means error failure. - select { - case err := <-resultC: - if assert.NoError(err) { - // Check handles from the controller. - mh.AssertExpectations(t) - } - case <-time.After(1 * time.Second): - assert.Fail("timeout waiting for controller handling, this could mean the controller is not receiving resources") - } - }) - } -} - func TestGenericControllerErrorRetries(t *testing.T) { nsList, _ := createNamespaceList("testing", 11) @@ -281,7 +207,7 @@ func TestGenericControllerErrorRetries(t *testing.T) { // Expect all the retries for range test.nsList.Items { callsPerNS := test.retryNumber + 1 // initial call + retries. - mh.On("Add", mock.Anything, mock.Anything).Return(err).Times(callsPerNS).Run(func(args mock.Arguments) { + mh.On("Handle", mock.Anything, mock.Anything).Return(err).Times(callsPerNS).Run(func(args mock.Arguments) { totalCalls-- // Check last call, if is the last call expected then stop the controller so // we can assert the expectations of the calls and finish the test. @@ -350,7 +276,7 @@ func TestGenericControllerWithLeaderElection(t *testing.T) { // Expect the calls on the lead (mh1) and no calls on the other ones. totalCalls := len(test.nsList.Items) - mh1.On("Add", mock.Anything, mock.Anything).Return(nil).Times(totalCalls).Run(func(args mock.Arguments) { + mh1.On("Handle", mock.Anything, mock.Anything).Return(nil).Times(totalCalls).Run(func(args mock.Arguments) { totalCalls-- // Check last call, if is the last call expected then stop the controller so // we can assert the expectations of the calls and finish the test. diff --git a/controller/handler.go b/controller/handler.go index c6a53077..5cf7abe7 100644 --- a/controller/handler.go +++ b/controller/handler.go @@ -9,35 +9,16 @@ import ( // Handler knows how to handle the received resources from a kubernetes cluster. type Handler interface { - Add(context.Context, runtime.Object) error - Delete(context.Context, string) error + Handle(context.Context, runtime.Object) error } -// AddFunc knows how to handle resource adds. -type AddFunc func(context.Context, runtime.Object) error +// HandlerFunc knows how to handle resource adds. +type HandlerFunc func(context.Context, runtime.Object) error -// DeleteFunc knows how to handle resource deletes. -type DeleteFunc func(context.Context, string) error - -// HandlerFunc is a handler that is created from functions that the -// Handler interface requires. -type HandlerFunc struct { - AddFunc AddFunc - DeleteFunc DeleteFunc -} - -// Add satisfies Handler interface. -func (h *HandlerFunc) Add(ctx context.Context, obj runtime.Object) error { - if h.AddFunc == nil { - return fmt.Errorf("function can't be nil") - } - return h.AddFunc(ctx, obj) -} - -// Delete satisfies Handler interface. -func (h *HandlerFunc) Delete(ctx context.Context, s string) error { - if h.DeleteFunc == nil { - return fmt.Errorf("function can't be nil") +// Handle satisfies controller.Handler interface. +func (h HandlerFunc) Handle(ctx context.Context, obj runtime.Object) error { + if h == nil { + return fmt.Errorf("handle func is required") } - return h.DeleteFunc(ctx, s) + return h(ctx, obj) } diff --git a/controller/processor.go b/controller/processor.go index 615a1925..9910bf7f 100644 --- a/controller/processor.go +++ b/controller/processor.go @@ -32,12 +32,11 @@ func newIndexerProcessor(indexer cache.Indexer, handler Handler) processor { return err } - // Handle the object. if !exists { - return handler.Delete(ctx, key) + return nil } - return handler.Add(ctx, obj.(runtime.Object)) + return handler.Handle(ctx, obj.(runtime.Object)) }) } diff --git a/examples/config-custom-controller/main.go b/examples/config-custom-controller/main.go index 1e84353f..3d7b7723 100644 --- a/examples/config-custom-controller/main.go +++ b/examples/config-custom-controller/main.go @@ -55,17 +55,11 @@ func run() error { }) // Our domain logic that will print every add/sync/update and delete event we . - hand := &controller.HandlerFunc{ - AddFunc: func(_ context.Context, obj runtime.Object) error { - pod := obj.(*corev1.Pod) - logger.Infof("Pod added: %s/%s", pod.Namespace, pod.Name) - return nil - }, - DeleteFunc: func(_ context.Context, s string) error { - logger.Infof("Pod deleted: %s", s) - return nil - }, - } + hand := controller.HandlerFunc(func(_ context.Context, obj runtime.Object) error { + pod := obj.(*corev1.Pod) + logger.Infof("Pod added: %s/%s", pod.Namespace, pod.Name) + return nil + }) // Create the controller with custom configuration. cfg := &controller.Config{ diff --git a/examples/controller-concurrency-handling/main.go b/examples/controller-concurrency-handling/main.go index 27e88ec4..4abbdaeb 100644 --- a/examples/controller-concurrency-handling/main.go +++ b/examples/controller-concurrency-handling/main.go @@ -102,19 +102,12 @@ func run() error { }) // Our domain logic that will print every add/sync/update and delete event we. - hand := &controller.HandlerFunc{ - AddFunc: func(_ context.Context, obj runtime.Object) error { - pod := obj.(*corev1.Pod) - sleep() - logger.Infof("Pod added: %s/%s", pod.Namespace, pod.Name) - return nil - }, - DeleteFunc: func(_ context.Context, s string) error { - sleep() - logger.Infof("Pod deleted: %s", s) - return nil - }, - } + hand := controller.HandlerFunc(func(_ context.Context, obj runtime.Object) error { + pod := obj.(*corev1.Pod) + sleep() + logger.Infof("Pod added: %s/%s", pod.Namespace, pod.Name) + return nil + }) // Create the controller. cfg := &controller.Config{ diff --git a/examples/leader-election-controller/main.go b/examples/leader-election-controller/main.go index 01c9873f..71a207e3 100644 --- a/examples/leader-election-controller/main.go +++ b/examples/leader-election-controller/main.go @@ -87,17 +87,11 @@ func run() error { }) // Our domain logic that will print every add/sync/update and delete event we . - hand := &controller.HandlerFunc{ - AddFunc: func(_ context.Context, obj runtime.Object) error { - pod := obj.(*corev1.Pod) - logger.Infof("Pod added: %s/%s", pod.Namespace, pod.Name) - return nil - }, - DeleteFunc: func(_ context.Context, s string) error { - logger.Infof("Pod deleted: %s", s) - return nil - }, - } + hand := controller.HandlerFunc(func(_ context.Context, obj runtime.Object) error { + pod := obj.(*corev1.Pod) + logger.Infof("Pod added: %s/%s", pod.Namespace, pod.Name) + return nil + }) // Leader election service. lesvc, err := leaderelection.NewDefault(leaderElectionKey, fl.Namespace, k8scli, logger) diff --git a/examples/metrics-controller/main.go b/examples/metrics-controller/main.go index 256315ad..68b391ee 100644 --- a/examples/metrics-controller/main.go +++ b/examples/metrics-controller/main.go @@ -114,16 +114,10 @@ func run() error { }) // Our domain logic that will print every add/sync/update and delete event we . - hand := &controller.HandlerFunc{ - AddFunc: func(_ context.Context, obj runtime.Object) error { - sleepRandomly() - return errRandomly() - }, - DeleteFunc: func(_ context.Context, s string) error { - sleepRandomly() - return errRandomly() - }, - } + hand := controller.HandlerFunc(func(_ context.Context, obj runtime.Object) error { + sleepRandomly() + return errRandomly() + }) // Create the controller that will refresh every 30 seconds. cfg := &controller.Config{ diff --git a/examples/multi-resource-controller/main.go b/examples/multi-resource-controller/main.go index 28d1dde3..ff9d72c8 100644 --- a/examples/multi-resource-controller/main.go +++ b/examples/multi-resource-controller/main.go @@ -73,26 +73,21 @@ func run() error { } // Our domain logic that will print every add/sync/update and delete event we . - hand := &controller.HandlerFunc{ - AddFunc: func(_ context.Context, obj runtime.Object) error { - dep, ok := obj.(*appsv1.Deployment) - if ok { - logger.Infof("Deployment added: %s/%s", dep.Namespace, dep.Name) - return nil - } - - st, ok := obj.(*appsv1.StatefulSet) - if ok { - logger.Infof("Statefulset added: %s/%s", st.Namespace, st.Name) - return nil - } - + hand := controller.HandlerFunc(func(_ context.Context, obj runtime.Object) error { + dep, ok := obj.(*appsv1.Deployment) + if ok { + logger.Infof("Deployment added: %s/%s", dep.Namespace, dep.Name) return nil - }, - DeleteFunc: func(_ context.Context, s string) error { + } + + st, ok := obj.(*appsv1.StatefulSet) + if ok { + logger.Infof("Statefulset added: %s/%s", st.Namespace, st.Name) return nil - }, - } + } + + return nil + }) // Create the controller with custom configuration. cfg := &controller.Config{ diff --git a/examples/pod-terminator-operator/apis/chaos/v1alpha1/types.go b/examples/pod-terminator-operator/apis/chaos/v1alpha1/types.go index 54fba233..5c3fed5f 100644 --- a/examples/pod-terminator-operator/apis/chaos/v1alpha1/types.go +++ b/examples/pod-terminator-operator/apis/chaos/v1alpha1/types.go @@ -4,11 +4,12 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +// PodTerminator represents a pod terminator. +// // +genclient // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// PodTerminator represents a pod terminator. +// +kubebuilder:resource:singular=podterminator,path=podterminators,shortName=pt,scope=Cluster,categories=terminators;killers;gc type PodTerminator struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. diff --git a/examples/pod-terminator-operator/manifest-examples/pause.yaml b/examples/pod-terminator-operator/manifest-examples/pause.yaml index 63e5d8f5..3ca0001a 100644 --- a/examples/pod-terminator-operator/manifest-examples/pause.yaml +++ b/examples/pod-terminator-operator/manifest-examples/pause.yaml @@ -1,7 +1,8 @@ -apiVersion: apps/v1beta2 +apiVersion: apps/v1 kind: Deployment metadata: name: pause-pods + namespace: kooper-test labels: application: pause spec: diff --git a/examples/pod-terminator-operator/manifests/chaos.spotahome.com_podterminators.yaml b/examples/pod-terminator-operator/manifests/chaos.spotahome.com_podterminators.yaml index b7db9ddd..bf4f7308 100644 --- a/examples/pod-terminator-operator/manifests/chaos.spotahome.com_podterminators.yaml +++ b/examples/pod-terminator-operator/manifests/chaos.spotahome.com_podterminators.yaml @@ -10,11 +10,17 @@ metadata: spec: group: chaos.spotahome.com names: + categories: + - terminators + - killers + - gc kind: PodTerminator listKind: PodTerminatorList plural: podterminators + shortNames: + - pt singular: podterminator - scope: Namespaced + scope: Cluster validation: openAPIV3Schema: description: PodTerminator represents a pod terminator. diff --git a/examples/pod-terminator-operator/operator/operator.go b/examples/pod-terminator-operator/operator/operator.go index 3d591517..88e53e92 100644 --- a/examples/pod-terminator-operator/operator/operator.go +++ b/examples/pod-terminator-operator/operator/operator.go @@ -27,7 +27,8 @@ type Config struct { // New returns pod terminator operator. func New(cfg Config, podTermCli podtermk8scli.Interface, kubeCli kubernetes.Interface, logger log.Logger) (controller.Controller, error) { return controller.New(&controller.Config{ - Handler: newHandler(kubeCli, logger), + Name: "pod-terminator", + Handler: newHandler(kubeCli, podTermCli, logger), Retriever: newRetriever(podTermCli), Logger: logger, @@ -46,27 +47,66 @@ func newRetriever(cli podtermk8scli.Interface) controller.Retriever { }) } -type handler struct { - chaosService chaos.Syncer - logger log.Logger -} +func newHandler(k8sCli kubernetes.Interface, ptCli podtermk8scli.Interface, logger log.Logger) controller.Handler { + const finalizer = "finalizer.chaos.spotahome.com/podKiller" + chaossvc := chaos.NewChaos(k8sCli, logger) -func newHandler(k8sCli kubernetes.Interface, logger log.Logger) *handler { - return &handler{ - chaosService: chaos.NewChaos(k8sCli, logger), - logger: logger, - } + return controller.HandlerFunc(func(_ context.Context, obj runtime.Object) error { + pt, ok := obj.(*chaosv1alpha1.PodTerminator) + if !ok { + return fmt.Errorf("%v is not a pod terminator object", obj.GetObjectKind()) + } + + switch { + // Handle deletion and remove finalizer. + case !pt.DeletionTimestamp.IsZero() && stringPresentInSlice(pt.Finalizers, finalizer): + logger.Infof("handling pod termination deletion...") + err := chaossvc.DeletePodTerminator(pt.ObjectMeta.Name) + if err != nil { + return fmt.Errorf("could not handle PodTerminator deletion: %w", err) + } + + pt.Finalizers = removeStringFromSlice(pt.Finalizers, finalizer) + _, err = ptCli.ChaosV1alpha1().PodTerminators().Update(pt) + if err != nil { + return fmt.Errorf("could not update pod terminator: %w", err) + } + + return nil + + // Deletion already handled, don't do anything. + case !pt.DeletionTimestamp.IsZero() && !stringPresentInSlice(pt.Finalizers, finalizer): + logger.Infof("handling pod termination deletion already handled, skipping...") + return nil + + // Add finalizer to the object. + case pt.DeletionTimestamp.IsZero() && !stringPresentInSlice(pt.Finalizers, finalizer): + pt.Finalizers = append(pt.Finalizers, finalizer) + _, err := ptCli.ChaosV1alpha1().PodTerminators().Update(pt) + if err != nil { + return fmt.Errorf("could not update pod termiantor: %w", err) + } + } + + // Handle. + return chaossvc.EnsurePodTerminator(pt) + }) } -func (h handler) Add(_ context.Context, obj runtime.Object) error { - pt, ok := obj.(*chaosv1alpha1.PodTerminator) - if !ok { - return fmt.Errorf("%v is not a pod terminator object", obj.GetObjectKind()) +func stringPresentInSlice(ss []string, s string) bool { + for _, f := range ss { + if f == s { + return true + } } - - return h.chaosService.EnsurePodTerminator(pt) + return false } -func (h handler) Delete(_ context.Context, name string) error { - return h.chaosService.DeletePodTerminator(name) +func removeStringFromSlice(ss []string, s string) []string { + for i, f := range ss { + if f == s { + return append(ss[:i], ss[i+1:]...) + } + } + return ss } diff --git a/examples/pod-terminator-operator/service/chaos/chaos.go b/examples/pod-terminator-operator/service/chaos/chaos.go index e6f32465..02971c8f 100644 --- a/examples/pod-terminator-operator/service/chaos/chaos.go +++ b/examples/pod-terminator-operator/service/chaos/chaos.go @@ -59,7 +59,6 @@ func (c *Chaos) EnsurePodTerminator(pt *chaosv1alpha1.PodTerminator) error { pk = NewPodKiller(ptCopy, c.k8sCli, c.logger) c.reg.Store(pt.Name, pk) return pk.Start() - // TODO: garbage collection. } // DeletePodTerminator satisfies ChaosSyncer interface. diff --git a/mocks/controller/Handler.go b/mocks/controller/Handler.go index ae12ac23..ebe15dfc 100644 --- a/mocks/controller/Handler.go +++ b/mocks/controller/Handler.go @@ -15,8 +15,8 @@ type Handler struct { mock.Mock } -// Add provides a mock function with given fields: _a0, _a1 -func (_m *Handler) Add(_a0 context.Context, _a1 runtime.Object) error { +// Handle provides a mock function with given fields: _a0, _a1 +func (_m *Handler) Handle(_a0 context.Context, _a1 runtime.Object) error { ret := _m.Called(_a0, _a1) var r0 error @@ -28,17 +28,3 @@ func (_m *Handler) Add(_a0 context.Context, _a1 runtime.Object) error { return r0 } - -// Delete provides a mock function with given fields: _a0, _a1 -func (_m *Handler) Delete(_a0 context.Context, _a1 string) error { - ret := _m.Called(_a0, _a1) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(_a0, _a1) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/test/integration/controller/controller_test.go b/test/integration/controller/controller_test.go index b7bc9766..8164875e 100644 --- a/test/integration/controller/controller_test.go +++ b/test/integration/controller/controller_test.go @@ -4,7 +4,6 @@ package controller_test import ( "context" - "strings" "sync" "testing" "time" @@ -27,12 +26,10 @@ import ( // events are received and handled correctly. func TestControllerHandleEvents(t *testing.T) { tests := []struct { - name string - addServices []*corev1.Service - updateServices []string - delServices []string - expAddedServices []string - expDeletedServices []string + name string + addServices []*corev1.Service + updateServices []string + expAddedServices []string }{ { name: "If a controller is watching services it should react to the service change events.", @@ -56,10 +53,8 @@ func TestControllerHandleEvents(t *testing.T) { }, }, }, - updateServices: []string{"svc1"}, - delServices: []string{"svc1", "svc2"}, - expAddedServices: []string{"svc1", "svc2", "svc1"}, - expDeletedServices: []string{"svc1", "svc2"}, + updateServices: []string{"svc1"}, + expAddedServices: []string{"svc1", "svc2", "svc1"}, }, } @@ -70,7 +65,6 @@ func TestControllerHandleEvents(t *testing.T) { resync := 30 * time.Second stopC := make(chan struct{}) var gotAddedServices []string - var gotDeletedServices []string // Create the kubernetes client. k8scli, err := cli.GetK8sClient("") @@ -86,38 +80,23 @@ func TestControllerHandleEvents(t *testing.T) { rt := controller.MustRetrieverFromListerWatcher(cache.NewListWatchFromClient(k8scli.CoreV1().RESTClient(), "services", prep.Namespace().Name, fields.Everything())) // Call times are the number of times the handler should be called before sending the termination signal. - stopCallTimes := len(test.addServices) + len(test.updateServices) + len(test.delServices) + stopCallTimes := len(test.addServices) + len(test.updateServices) calledTimes := 0 var mx sync.Mutex // Create the handler. - hl := &controller.HandlerFunc{ - AddFunc: func(_ context.Context, obj runtime.Object) error { - mx.Lock() - calledTimes++ - mx.Unlock() - - svc := obj.(*corev1.Service) - gotAddedServices = append(gotAddedServices, svc.Name) - if calledTimes >= stopCallTimes { - close(stopC) - } - return nil - }, - DeleteFunc: func(_ context.Context, id string) error { - mx.Lock() - calledTimes++ - mx.Unlock() - - // Ignore namespace. - id = strings.Split(id, "/")[1] - gotDeletedServices = append(gotDeletedServices, id) - if calledTimes >= stopCallTimes { - close(stopC) - } - return nil - }, - } + hl := controller.HandlerFunc(func(_ context.Context, obj runtime.Object) error { + mx.Lock() + calledTimes++ + mx.Unlock() + + svc := obj.(*corev1.Service) + gotAddedServices = append(gotAddedServices, svc.Name) + if calledTimes >= stopCallTimes { + close(stopC) + } + return nil + }) // Create a Pod controller. cfg := &controller.Config{ @@ -149,13 +128,6 @@ func TestControllerHandleEvents(t *testing.T) { } } - // Delete the required services. - for _, svc := range test.delServices { - err := k8scli.CoreV1().Services(prep.Namespace().Name).Delete(svc, &metav1.DeleteOptions{}) - assert.NoError(err) - time.Sleep(1 * time.Second) - } - // Wait until we have finished. select { // Timeout. @@ -166,7 +138,6 @@ func TestControllerHandleEvents(t *testing.T) { // Check. assert.Equal(test.expAddedServices, gotAddedServices) - assert.Equal(test.expDeletedServices, gotDeletedServices) }) } } diff --git a/test/integration/controller/generic_test.go b/test/integration/controller/generic_test.go index bf84858f..0f9c1be7 100644 --- a/test/integration/controller/generic_test.go +++ b/test/integration/controller/generic_test.go @@ -64,17 +64,11 @@ func runTimedController(sleepDuration time.Duration, concurrencyLevel int, numbe var wg sync.WaitGroup wg.Add(numberOfEvents) - h := &controller.HandlerFunc{ - AddFunc: func(_ context.Context, _ runtime.Object) error { - time.Sleep(sleepDuration) - wg.Done() - return nil - }, - DeleteFunc: func(_ context.Context, _ string) error { - assert.Fail("delete events should not be used on this test") - return nil - }, - } + h := controller.HandlerFunc(func(_ context.Context, _ runtime.Object) error { + time.Sleep(sleepDuration) + wg.Done() + return nil + }) // Create the controller type depending on the concurrency level. cfg := &controller.Config{ From 0317100489468751afaf5f241bd19bd412cba136 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Wed, 8 Apr 2020 10:03:05 +0200 Subject: [PATCH 14/31] Don't measure queue events on queue shutdown Signed-off-by: Xabier Larrakoetxea --- controller/controller.go | 294 +++++++++++++++++ .../{generic_test.go => controller_test.go} | 0 controller/generic.go | 302 ------------------ controller/queue.go | 3 + 4 files changed, 297 insertions(+), 302 deletions(-) rename controller/{generic_test.go => controller_test.go} (100%) delete mode 100644 controller/generic.go diff --git a/controller/controller.go b/controller/controller.go index df0e5156..a53dee24 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -1,5 +1,28 @@ package controller +import ( + "context" + "errors" + "fmt" + "sync" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" + + "github.com/spotahome/kooper/controller/leaderelection" + "github.com/spotahome/kooper/log" +) + +var ( + // ErrControllerNotValid will be used when the controller has invalid configuration. + ErrControllerNotValid = errors.New("controller not valid") +) + // Controller is the object that will implement the different kinds of controllers that will be running // on the application. type Controller interface { @@ -7,3 +30,274 @@ type Controller interface { // Run will block until it's stopped. Run(stopper <-chan struct{}) error } + +// Config is the controller configuration. +type Config struct { + // Handler is the controller handler. + Handler Handler + // Retriever is the controller retriever. + Retriever Retriever + // Leader elector will be used to use only one instance, if no set it will be + // leader election will be ignored + LeaderElector leaderelection.Runner + // MetricsRecorder will record the controller metrics. + MetricsRecorder MetricsRecorder + // Logger will log messages of the controller. + Logger log.Logger + + // name of the controller. + Name string + // ConcurrentWorkers is the number of concurrent workers the controller will have running processing events. + ConcurrentWorkers int + // ResyncInterval is the interval the controller will process all the selected resources. + ResyncInterval time.Duration + // ProcessingJobRetries is the number of times the job will try to reprocess the event before returning a real error. + ProcessingJobRetries int +} + +func (c *Config) setDefaults() error { + if c.Name == "" { + return fmt.Errorf("a controller name is required") + } + + if c.Handler == nil { + return fmt.Errorf("a handler is required") + } + + if c.Retriever == nil { + return fmt.Errorf("a retriever is required") + } + + if c.Logger == nil { + c.Logger = log.NewStd(false) + c.Logger.Warningf("no logger specified, fallback to default logger, to disable logging use a explicit Noop logger") + } + c.Logger = c.Logger.WithKV(log.KV{ + "service": "kooper.controller", + "controller-id": c.Name, + }) + + if c.MetricsRecorder == nil { + c.MetricsRecorder = DummyMetricsRecorder + c.Logger.Warningf("no metrics recorder specified, disabling metrics") + } + + if c.ConcurrentWorkers <= 0 { + c.ConcurrentWorkers = 3 + } + + if c.ResyncInterval <= 0 { + c.ResyncInterval = 3 * time.Minute + } + + if c.ProcessingJobRetries < 0 { + c.ProcessingJobRetries = 0 + } + + return nil +} + +// generic controller is a controller that can be used to create different kind of controllers. +type generic struct { + queue blockingQueue // queue will have the jobs that the controller will get and send to handlers. + informer cache.SharedIndexInformer // informer will notify be inform us about resource changes. + processor processor // processor will call the user handler (logic). + + running bool + runningMu sync.Mutex + cfg Config + metrics MetricsRecorder + leRunner leaderelection.Runner + logger log.Logger +} + +func listerWatcherFromRetriever(ret Retriever) cache.ListerWatcher { + // TODO(slok): pass context when Kubernetes updates its ListerWatchers ¯\_(ツ)_/¯. + return &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + return ret.List(context.TODO(), options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + return ret.Watch(context.TODO(), options) + }, + } +} + +// New creates a new controller that can be configured using the cfg parameter. +func New(cfg *Config) (Controller, error) { + // Sets the required default configuration. + err := cfg.setDefaults() + if err != nil { + return nil, fmt.Errorf("could no create controller: %w: %v", ErrControllerNotValid, err) + } + + // Create the queue that will have our received job changes. + queue := newRateLimitingBlockingQueue( + cfg.ProcessingJobRetries, + workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()), + ) + queue = newMetricsBlockingQueue( + cfg.Name, + cfg.MetricsRecorder, + queue, + cfg.Logger, + ) + + // store is the internal cache where objects will be store. + store := cache.Indexers{} + lw := listerWatcherFromRetriever(cfg.Retriever) + informer := cache.NewSharedIndexInformer(lw, nil, cfg.ResyncInterval, store) + + // Set up our informer event handler. + // Objects are already in our local store. Add only keys/jobs on the queue so they can bre processed + // afterwards. + informer.AddEventHandlerWithResyncPeriod(cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + key, err := cache.MetaNamespaceKeyFunc(obj) + if err != nil { + cfg.Logger.Warningf("could not add item from 'add' event to queue: %s", err) + return + } + queue.Add(context.TODO(), key) + }, + UpdateFunc: func(old interface{}, new interface{}) { + key, err := cache.MetaNamespaceKeyFunc(new) + if err != nil { + cfg.Logger.Warningf("could not add item from 'update' event to queue: %s", err) + return + } + queue.Add(context.TODO(), key) + }, + DeleteFunc: func(obj interface{}) { + key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) + if err != nil { + cfg.Logger.Warningf("could not add item from 'delete' event to queue: %s", err) + return + } + queue.Add(context.TODO(), key) + }, + }, cfg.ResyncInterval) + + // Create processing chain: processor(+middlewares) -> handler(+middlewares). + processor := newIndexerProcessor(informer.GetIndexer(), cfg.Handler) + if cfg.ProcessingJobRetries > 0 { + processor = newRetryProcessor(cfg.Name, cfg.ProcessingJobRetries, queue, cfg.Logger, processor) + } + processor = newMetricsProcessor(cfg.Name, cfg.MetricsRecorder, processor) + + // Create our generic controller object. + return &generic{ + queue: queue, + informer: informer, + metrics: cfg.MetricsRecorder, + processor: processor, + leRunner: cfg.LeaderElector, + cfg: *cfg, + logger: cfg.Logger, + }, nil +} + +func (g *generic) isRunning() bool { + g.runningMu.Lock() + defer g.runningMu.Unlock() + return g.running +} + +func (g *generic) setRunning(running bool) { + g.runningMu.Lock() + defer g.runningMu.Unlock() + g.running = running +} + +// Run will run the controller. +func (g *generic) Run(stopC <-chan struct{}) error { + // Check if leader election is required. + if g.leRunner != nil { + return g.leRunner.Run(func() error { + return g.run(stopC) + }) + } + + return g.run(stopC) +} + +// run is the real run of the controller. +func (g *generic) run(stopC <-chan struct{}) error { + if g.isRunning() { + return fmt.Errorf("controller already running") + } + + g.logger.Infof("starting controller") + // Set state of controller. + g.setRunning(true) + defer g.setRunning(false) + + // Shutdown when Run is stopped so we can process the last items and the queue doesn't + // accept more jobs. + defer g.queue.ShutDown(context.TODO()) + + // Run the informer so it starts listening to resource events. + go g.informer.Run(stopC) + + // Wait until our store, jobs... stuff is synced (first list on resource, resources on store and jobs on queue). + if !cache.WaitForCacheSync(stopC, g.informer.HasSynced) { + return fmt.Errorf("timed out waiting for caches to sync") + } + + // Start our resource processing worker, if finishes then restart the worker. The workers should + // not end. + for i := 0; i < g.cfg.ConcurrentWorkers; i++ { + go func() { + wait.Until(g.runWorker, time.Second, stopC) + }() + } + + // Until will be running our workers in a continuous way (and re run if they fail). But + // when stop signal is received we must stop. + <-stopC + g.logger.Infof("stopping controller") + + return nil +} + +// runWorker will start a processing loop on event queue. +func (g *generic) runWorker() { + for { + // Process next queue job, if needs to stop processing it will return true. + if g.processNextJob() { + break + } + } +} + +// processNextJob job will process the next job of the queue job and returns if +// it needs to stop processing. +// +// If the queue has been closed then it will end the processing. +func (g *generic) processNextJob() bool { + ctx := context.Background() + + // Get next job. + nextJob, exit := g.queue.Get(ctx) + if exit { + return true + } + + defer g.queue.Done(ctx, nextJob) + key := nextJob.(string) + + // Process the job. + err := g.processor.Process(ctx, key) + + logger := g.logger.WithKV(log.KV{"object-key": key}) + switch { + case err == nil: + logger.Debugf("object processed") + case errors.Is(err, errRequeued): + logger.Warningf("error on object processing, retrying: %v", err) + default: + logger.Errorf("error on object processing: %v", err) + } + + return false +} diff --git a/controller/generic_test.go b/controller/controller_test.go similarity index 100% rename from controller/generic_test.go rename to controller/controller_test.go diff --git a/controller/generic.go b/controller/generic.go deleted file mode 100644 index 3ceb8e60..00000000 --- a/controller/generic.go +++ /dev/null @@ -1,302 +0,0 @@ -package controller - -import ( - "context" - "errors" - "fmt" - "sync" - "time" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/util/workqueue" - - "github.com/spotahome/kooper/controller/leaderelection" - "github.com/spotahome/kooper/log" -) - -var ( - // ErrControllerNotValid will be used when the controller has invalid configuration. - ErrControllerNotValid = errors.New("controller not valid") -) - -// Defaults. -const ( - defResyncInterval = 3 * time.Minute - defConcurrentWorkers = 3 - defProcessingJobRetries = 0 -) - -// Config is the controller configuration. -type Config struct { - // Handler is the controller handler. - Handler Handler - // Retriever is the controller retriever. - Retriever Retriever - // Leader elector will be used to use only one instance, if no set it will be - // leader election will be ignored - LeaderElector leaderelection.Runner - // MetricsRecorder will record the controller metrics. - MetricsRecorder MetricsRecorder - // Logger will log messages of the controller. - Logger log.Logger - - // name of the controller. - Name string - // ConcurrentWorkers is the number of concurrent workers the controller will have running processing events. - ConcurrentWorkers int - // ResyncInterval is the interval the controller will process all the selected resources. - ResyncInterval time.Duration - // ProcessingJobRetries is the number of times the job will try to reprocess the event before returning a real error. - ProcessingJobRetries int -} - -func (c *Config) setDefaults() error { - if c.Name == "" { - return fmt.Errorf("a controller name is required") - } - - if c.Handler == nil { - return fmt.Errorf("a handler is required") - } - - if c.Retriever == nil { - return fmt.Errorf("a retriever is required") - } - - if c.Logger == nil { - c.Logger = log.NewStd(false) - c.Logger.Warningf("no logger specified, fallback to default logger, to disable logging use a explicit Noop logger") - } - c.Logger = c.Logger.WithKV(log.KV{ - "source-service": "kooper/controller", - "controller-id": c.Name, - }) - - if c.MetricsRecorder == nil { - c.MetricsRecorder = DummyMetricsRecorder - c.Logger.Warningf("no metrics recorder specified, disabling metrics") - } - - if c.ConcurrentWorkers <= 0 { - c.ConcurrentWorkers = defConcurrentWorkers - } - - if c.ResyncInterval <= 0 { - c.ResyncInterval = defResyncInterval - } - - if c.ProcessingJobRetries < 0 { - c.ProcessingJobRetries = defProcessingJobRetries - } - - return nil -} - -// generic controller is a controller that can be used to create different kind of controllers. -type generic struct { - queue blockingQueue // queue will have the jobs that the controller will get and send to handlers. - informer cache.SharedIndexInformer // informer will notify be inform us about resource changes. - processor processor // processor will call the user handler (logic). - - running bool - runningMu sync.Mutex - cfg Config - metrics MetricsRecorder - leRunner leaderelection.Runner - logger log.Logger -} - -func listerWatcherFromRetriever(ret Retriever) cache.ListerWatcher { - // TODO(slok): pass context when Kubernetes updates its ListerWatchers ¯\_(ツ)_/¯. - return &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - return ret.List(context.TODO(), options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - return ret.Watch(context.TODO(), options) - }, - } -} - -// New creates a new controller that can be configured using the cfg parameter. -func New(cfg *Config) (Controller, error) { - // Sets the required default configuration. - err := cfg.setDefaults() - if err != nil { - return nil, fmt.Errorf("could no create controller: %w: %v", ErrControllerNotValid, err) - } - - // Create the queue that will have our received job changes. - queue := newRateLimitingBlockingQueue( - cfg.ProcessingJobRetries, - workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()), - ) - queue = newMetricsBlockingQueue( - cfg.Name, - cfg.MetricsRecorder, - queue, - cfg.Logger, - ) - - // store is the internal cache where objects will be store. - store := cache.Indexers{} - lw := listerWatcherFromRetriever(cfg.Retriever) - informer := cache.NewSharedIndexInformer(lw, nil, cfg.ResyncInterval, store) - - // Set up our informer event handler. - // Objects are already in our local store. Add only keys/jobs on the queue so they can bre processed - // afterwards. - informer.AddEventHandlerWithResyncPeriod(cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - key, err := cache.MetaNamespaceKeyFunc(obj) - if err != nil { - cfg.Logger.Warningf("could not add item from 'add' event to queue: %s", err) - return - } - queue.Add(context.TODO(), key) - }, - UpdateFunc: func(old interface{}, new interface{}) { - key, err := cache.MetaNamespaceKeyFunc(new) - if err != nil { - cfg.Logger.Warningf("could not add item from 'update' event to queue: %s", err) - return - } - queue.Add(context.TODO(), key) - }, - DeleteFunc: func(obj interface{}) { - key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) - if err != nil { - cfg.Logger.Warningf("could not add item from 'delete' event to queue: %s", err) - return - } - queue.Add(context.TODO(), key) - }, - }, cfg.ResyncInterval) - - // Create processing chain: processor(+middlewares) -> handler(+middlewares). - processor := newIndexerProcessor(informer.GetIndexer(), cfg.Handler) - if cfg.ProcessingJobRetries > 0 { - processor = newRetryProcessor(cfg.Name, cfg.ProcessingJobRetries, queue, cfg.Logger, processor) - } - processor = newMetricsProcessor(cfg.Name, cfg.MetricsRecorder, processor) - - // Create our generic controller object. - return &generic{ - queue: queue, - informer: informer, - metrics: cfg.MetricsRecorder, - processor: processor, - leRunner: cfg.LeaderElector, - cfg: *cfg, - logger: cfg.Logger, - }, nil -} - -func (g *generic) isRunning() bool { - g.runningMu.Lock() - defer g.runningMu.Unlock() - return g.running -} - -func (g *generic) setRunning(running bool) { - g.runningMu.Lock() - defer g.runningMu.Unlock() - g.running = running -} - -// Run will run the controller. -func (g *generic) Run(stopC <-chan struct{}) error { - // Check if leader election is required. - if g.leRunner != nil { - return g.leRunner.Run(func() error { - return g.run(stopC) - }) - } - - return g.run(stopC) -} - -// run is the real run of the controller. -func (g *generic) run(stopC <-chan struct{}) error { - if g.isRunning() { - return fmt.Errorf("controller already running") - } - - g.logger.Infof("starting controller") - // Set state of controller. - g.setRunning(true) - defer g.setRunning(false) - - // Shutdown when Run is stopped so we can process the last items and the queue doesn't - // accept more jobs. - defer g.queue.ShutDown(context.TODO()) - - // Run the informer so it starts listening to resource events. - go g.informer.Run(stopC) - - // Wait until our store, jobs... stuff is synced (first list on resource, resources on store and jobs on queue). - if !cache.WaitForCacheSync(stopC, g.informer.HasSynced) { - return fmt.Errorf("timed out waiting for caches to sync") - } - - // Start our resource processing worker, if finishes then restart the worker. The workers should - // not end. - for i := 0; i < g.cfg.ConcurrentWorkers; i++ { - go func() { - wait.Until(g.runWorker, time.Second, stopC) - }() - } - - // Until will be running our workers in a continuous way (and re run if they fail). But - // when stop signal is received we must stop. - <-stopC - g.logger.Infof("stopping controller") - - return nil -} - -// runWorker will start a processing loop on event queue. -func (g *generic) runWorker() { - for { - // Process next queue job, if needs to stop processing it will return true. - if g.processNextJob() { - break - } - } -} - -// processNextJob job will process the next job of the queue job and returns if -// it needs to stop processing. -// -// If the queue has been closed then it will end the processing. -func (g *generic) processNextJob() bool { - ctx := context.Background() - - // Get next job. - nextJob, exit := g.queue.Get(ctx) - if exit { - return true - } - - defer g.queue.Done(ctx, nextJob) - key := nextJob.(string) - - // Process the job. - err := g.processor.Process(ctx, key) - - logger := g.logger.WithKV(log.KV{"object-key": key}) - switch { - case err == nil: - logger.Debugf("object processed") - case errors.Is(err, errRequeued): - logger.Warningf("error on object processing, retrying: %v", err) - default: - logger.Errorf("error on object processing: %v", err) - } - - return false -} diff --git a/controller/queue.go b/controller/queue.go index 995246c4..a095839b 100644 --- a/controller/queue.go +++ b/controller/queue.go @@ -117,6 +117,9 @@ func (m *metricsBlockingQueue) Requeue(ctx context.Context, item interface{}) er func (m *metricsBlockingQueue) Get(ctx context.Context) (interface{}, bool) { // Here should get blocked, warning with the mutexes. item, shutdown := m.queue.Get(ctx) + if shutdown { + return item, shutdown + } m.mu.Lock() queuedAt, ok := m.itemsQueuedAt[item] From 3abdd6bad75efeb4e6d9f7be88790e6ff7a394e0 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Sun, 12 Apr 2020 11:39:08 +0200 Subject: [PATCH 15/31] Refactor docs Signed-off-by: Xabier Larrakoetxea --- README.md | 235 +++++++++++++++++++++-------------------------- docs/concepts.md | 114 ----------------------- docs/logger.md | 42 --------- docs/metrics.md | 79 ---------------- docs/tracing.md | 134 --------------------------- 5 files changed, 105 insertions(+), 499 deletions(-) delete mode 100644 docs/concepts.md delete mode 100644 docs/logger.md delete mode 100644 docs/metrics.md delete mode 100644 docs/tracing.md diff --git a/README.md b/README.md index 8bcb883e..45c71bdb 100644 --- a/README.md +++ b/README.md @@ -1,180 +1,148 @@ # Kooper [![Build Status][travis-image]][travis-url] [![Go Report Card][goreport-image]][goreport-url] [![GoDoc][godoc-image]][godoc-url] -Kooper is a simple Go library to create Kubernetes [operators](https://coreos.com/operators/) and [controllers](https://github.com/kubernetes/community/blob/master/contributors/devel/controllers.md). +Kooper is a Go library to create simple and flexible [controllers]/operators, in a fast, decoupled and easy way. -## What is Kooper? +In other words, is a small alternative to big frameworks like [Kubebuilder] or [operator-framework]. -Kooper is a set of utilities packed as a library or framework to easily create Kubernetes controllers and operators. +## Getting started -There is a little of discussion of what a controller and what an operator is, see [here](https://stackoverflow.com/questions/47848258/kubernetes-controller-vs-kubernetes-operator) for more information. - -In Kooper the concepts of controller an operator are very simple, a controller controls the state of a resource in Kubernetes, and an operator is a controller that initializes custom resources (CRD) and controls the state of this custom resource. - -## Features - -- Easy and decoupled library. -- Well structured and a clear API. -- Remove all duplicated code from every controller and operator. -- Uses the tooling already created by Kubernetes. -- Remove complexity from operators and controllers so the focus is on domain logic. -- Easy to mock and extend functionality (Go interfaces!). -- Only support CRD, no TPR support (Kubernetes >=1.7). -- Controller metrics (with a [Grafana dashboard][grafana-dashboard] based on Prometheus metrics backend). -- Leader election. -- Tracing with [Opentracing][opentracing-url]. - -## Example - -It can be seen how easy is to develop a controller or an operator in kooper looking at the [documentation](docs). - -This is a simple pod log controller example ([full running example here](https://github.com/spotahome/kooper/blob/master/examples/onefile-echo-pod-controller/main.go)): +The simplest example that prints pods would be this: ```go -// Initialize resources like logger and kubernetes client -//... - -// Create our retriever so the controller knows how to get/listen for pod events. -retr := &retrieve.Resource{ - Object: &corev1.Pod{}, - ListerWatcher: &cache.ListWatch{ + // Create our retriever so the controller knows how to get/listen for pod events. + retr := controller.MustRetrieverFromListerWatcher(&cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { return k8scli.CoreV1().Pods("").List(options) }, WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { return k8scli.CoreV1().Pods("").Watch(options) }, - }, -} + }) -// Our domain logic that will print every add/sync/update and delete event. -hand := &handler.HandlerFunc{ - AddFunc: func(_ context.Context, obj runtime.Object) error { + // Our domain logic that will print all pod events. + hand := controller.HandlerFunc(func(_ context.Context, obj runtime.Object) error { pod := obj.(*corev1.Pod) - log.Infof("Pod added: %s/%s", pod.Namespace, pod.Name) - return nil - }, - DeleteFunc: func(_ context.Context, s string) error { - log.Infof("Pod deleted: %s", s) + logger.Infof("Pod event: %s/%s", pod.Namespace, pod.Name) return nil - }, -} - -// Create the controller that will refresh every 30 seconds. -ctrl := controller.NewSequential(30*time.Second, hand, retr, nil, log) -stopC := make(chan struct{}) -if err := ctrl.Run(stopC); err != nil { - log.Errorf("error running controller: %s", err) - os.Exit(1) -} -os.Exit(0) + }) + + // Create the controller with custom configuration. + cfg := &controller.Config{ + Name: "example-controller", + Handler: hand, + Retriever: retr, + Logger: logger, + } + ctrl, err := controller.New(cfg) + if err != nil { + return fmt.Errorf("could not create controller: %w", err) + } + + // Start our controller. + ctrl.Run(make(chan struct{})) ``` -The above shows that it is very easy to get a controller working in less than 100 lines of code. How it works can be demonstrated by running the controller from this repository. +## When should I use Kooper? -```bash -go run ./examples/onefile-echo-pod-controller/main.go -``` +### Alternatives -or directly using docker (Note you are binding kubernetes configuration on the container so kooper can connect to kubernetes cluster). +What is the difference between kooper and alternatives like [Kubebuilder] or [operator-framework]? -```bash -docker run \ - --rm -it \ - -v ${HOME}/.kube:/root/.kube:ro \ - golang:1.10 \ - /bin/bash -c "go get github.com/spotahome/kooper/... && cd /go/src/github.com/spotahome/kooper && go run ./examples/onefile-echo-pod-controller/main.go" -``` +Kooper embraces the Go philosophy of having small simple components/libs and use them as you wish in combination of others, instead of trying to solve every use case and imposing everything by sacriying flexbility and adding complexity. -## Motivation +As an example using the web applications world as reference: We could say that Kooper is more like Go HTTP router/libs, on the other side, Kubebuilder and operator-framework are like Ruby on rails/Django style frameworks. -The state of art in the operators/controllers moves fast, a lot of new operators are being published every day. Most of them have the same "infrastructure" code refering Kubernetes operators/controllers and bootstrapping a new operator can be slow or repetitive. +For example Kubebuilder comes with: -At this moment there is no standard, although there are some projects like [rook operator kit](https://github.com/rook/operator-kit) or [Giantswarm operator kit](https://github.com/giantswarm/operatorkit) that are trying to create it. +- Admission webhooks. +- Folder/package structure conventions. +- CRD clients generation. +- RBAC manifest generation. +- Not pluggable/flexible usage of metrics, logging, HTTP/K8s API clients... +- ... -Spotahome studied these projects before developing Kooper and they didn't fit the requirements: +Kooper instead solves most of the core controller/operator problems but as a simple, small and flexible library, and let the other problems (like admission webhooks) be solved by other libraries specialiced on that. e.g -- Clear and maintanable code. -- Easy to test and mock. -- Well tested library. -- Easy and clear programming API. -- Good abstraction and structure to focus on domain logic (the meat of the controller). -- Reduce complexity in all the operators and controllers that use the library. -- Not only operators, controllers as first class citizen also. +- Use whatever you want to create your CRD clients, maybe you don't have CRDs at all! (e.g [kube-code-generator]). +- You can setup your admission webhooks outside your controller by using other libraries like (e.g [Kubewebhook]). +- You can create your RBAC manifests as you wish and evolve while you develop your controller. +- Set you prefered logging system/style (comes with logrus implementation). +- Implement your prefered metrics backend (comes with Prometheus implementaion). +- Use your own Kubernetes clients (Kubernetes go library, implemented by your own for a special case...). +- ... -## Installing +### Simplicty VS optimization -Any dependency manager can get Kooper or directly with go get the latest version: +For example Kubebuilder sacrifies API simplicity/client in favor of aggresive cache usage. Kooper instead embraces simplicity over optimization: -```bash -go get -u github.com/spotahome/kooper -``` +- Most of the controller/operators don't need this kind of optimization. Adding complexity on all controllers for a small optimized controller use cases doesn't make sense. +- If you need optimization related with Kubernetes resources, you can use Kubebuilder or implement your own solution for that specific use case like a cache based Kubernetes client or service. +- If you need otimization and is not related with Kubernetes API itself, it doesn't matter the controller library optimization. -## Using Kooper as a dependency +## More examples -Managing a project that uses different kubernetes libs as dependencies can be tricky at first because of the different versions of all these libraries(apimachinery, client-go, api...). [Here][dependency-example] you have an example of how would you use kooper as a dependency in a project and setting the kubernetes libraries to the version that you want along with kooper (using [dep][dep-project]). +On the [examples] folder you have different examples, like regular controllers, operators, metrics based, leader election, multiresource type controllers... -## Compatibility matrix +## Core concepts -| | Kubernetes <=1.9 | Kubernetes 1.10 | Kubernetes 1.11 | Kubernetes 1.12 | Kubernetes 1.13 | Kubernetes 1.14 | -| ----------- | ---------------- | --------------- | --------------- | --------------- | --------------- | --------------- | -| kooper 0.1 | ✓ | ? | ? | ? | ? | ? | -| kooper 0.2 | ✓ | ? | ? | ? | ? | ? | -| kooper 0.3 | ?+ | ✓ | ? | ? | ? | ? | -| kooper 0.4 | ?+ | ✓ | ? | ? | ? | ? | -| kooper 0.5 | ? | ?+ | ✓ | ? | ? | ? | -| kooper 0.6 | ? | ? | ?+ | ✓ | ? | ? | -| kooper HEAD | ? | ? | ? | ?+ | ✓? | ? | +Concept doesn't do a distinction between Operators and controllers, all are controllers, the difference of both is on what resources are retrieved. -Based on this matrix Kooper needs different versions of Kubernetes dependencies. +A controller is based on 3 simple concepts: -An example would be. If the cluster that will use kooper operators/controllers is a 1.9.x Kubernetes cluster, the version of kooper would be `0.2.x` and the kubernetes libraries in `1.9.x` compatibility style. For example the project that uses kooper as a dependency, its dep file would be something like this: +### Retriever -```yaml -... +The component that lists and watch the resources the controller will handle when there is a change. Kooper comes with some helpers to create fast retrievers and combination of multiple of them: -[[override]] - name = "k8s.io/api" - version = "kubernetes-1.9.6" +- `Retriever`: The core retriever it needs to implement list (list objects), and watch, subscribe to object changes. +- `MultiRetriever`: A retriever that can combine multiple retriever of different types to handle changes on the same controller `Handler`. +- `RetrieverFromListerWatcher`: Converts a Kubernetes ListerWatcher into a kooper Retriever. -[[override]] - name = "k8s.io/apimachinery" - version = "kubernetes-1.9.6" +The `Retriever` can be based on Kubernetes base resources (Pod, Deployment, Service...) or based on CRDs, theres no distinction. -[[override]] - name = "k8s.io/client-go" - version = "kubernetes-1.9.6" +The `Retriever` is an interface so you can use the middleware/wrapper/decorator pattern to extend (e.g add custom metrics). -[[constraint]] - name = "github.com/spotahome/kooper" - version = "0.2.0" +### Handler -... -``` +Kooper handles all the events on the same handler: + +- `Handler`: The interface that knows how to handle kubernetes objects. +- `HandlerFunc`: A helper that gets a `Handler` from a function so you don't need to create a new type to define your `Handler`. + +The `Handler` is an interface so you can use the middleware/wrapper/decorator pattern to extend (e.g add custom metrics). -## Documentation +### Controller -Kooper comes with different topics as documentation. +The controller is the component that uses the `Handler` and `Retriever` to start a feedback loop controller process: -### Core +- On the first start it will use `controller.Retriever.List` to get all the resources and pass them to the `controller.Handler`. +- Then it will call `controller.Handler` for every change done in the resources using the `controller.Retriever.Watcher`. +- At regular intervals (3 minute by default) it will call `controller.Handler` with all resources in case we have missed a `Watch` event. -- [Basic concepts](docs/concepts.md) -- [Controller tutorial](docs/controller-tutorial.md) -- [Operator tutorial](docs/operator-tutorial.md) +## Other concepts -### Other +### Leader election -- [Metrics](docs/metrics.md) -- [Logger](docs/logger.md) -- [Leader election](docs/leader-election.md) -- [Tracing](docs/tracing.md) +Check [Leader election](docs/leader-election.md). -The starting point would be to check the [concepts](docs/concepts.md) and then continue with the controller and operator tutorials. +### Garbage collection -## Who is using kooper +Kooper only handles the events of resources that exist that are triggered when these resources change or are created. There is no delete event, so in order to clean the resources you have 2 ways of doing these: -- [redis-operator](https://github.com/spotahome/redis-operator) -- [node-labeler-operator](https://github.com/barpilot/node-labeler-operator) -- [source-ranges-controller](https://github.com/jeffersongirao/source-ranges-controller) +If your controller creates as a side effect new Kubernetes resources you can use [owner references][owner-ref] on the created objects. + +On the other hand if you want a more flexible clean up process (e.g clean from a database or a 3rd party service) you can use [finalizers], check the [pod-terminator-operator][finalizer-example] example. + +## Compatibility matrix + +| | Kubernetes <=1.9 | Kubernetes 1.10 | Kubernetes 1.11 | Kubernetes 1.12 | Kubernetes 1.13 | Kubernetes 1.14 | +| ----------- | ---------------- | --------------- | --------------- | --------------- | --------------- | --------------- | +| kooper 0.1 | ✓ | ? | ? | ? | ? | ? | +| kooper 0.2 | ✓ | ? | ? | ? | ? | ? | +| kooper 0.3 | ?+ | ✓ | ? | ? | ? | ? | +| kooper 0.4 | ?+ | ✓ | ? | ? | ? | ? | +| kooper 0.5 | ? | ?+ | ✓ | ? | ? | ? | +| kooper 0.6 | ? | ? | ?+ | ✓ | ? | ? | +| kooper HEAD | ? | ? | ? | ?+ | ✓? | ? | [travis-image]: https://travis-ci.org/spotahome/kooper.svg?branch=master [travis-url]: https://travis-ci.org/spotahome/kooper @@ -182,7 +150,14 @@ The starting point would be to check the [concepts](docs/concepts.md) and then c [goreport-url]: https://goreportcard.com/report/github.com/spotahome/kooper [godoc-image]: https://godoc.org/github.com/spotahome/kooper?status.svg [godoc-url]: https://godoc.org/github.com/spotahome/kooper -[dependency-example]: https://github.com/slok/kooper-as-dependency -[dep-project]: https://github.com/golang/dep -[opentracing-url]: http://opentracing.io/ + +[examples]: examples/ [grafana-dashboard]: https://grafana.com/dashboards/7082 +[controllers]: https://kubernetes.io/docs/concepts/architecture/controller/ +[Kubebuilder]: https://github.com/kubernetes-sigs/kubebuilder +[operator-framework]: https://github.com/operator-framework +[Kubewebhook]: https://github.com/slok/kubewebhook +[kube-code-generator]: https://github.com/slok/kube-code-generator +[owner-ref]: https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/#owners-and-dependents +[finalizers]: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#finalizers +[finalizer-example]: examples/pod-terminator-operator/operator/operator.go \ No newline at end of file diff --git a/docs/concepts.md b/docs/concepts.md deleted file mode 100644 index c32fc5ec..00000000 --- a/docs/concepts.md +++ /dev/null @@ -1,114 +0,0 @@ - -Start -===== - -Kooper motivation: Help creating Kubernetes operators and controllers by giving a framework, libraries or a set of tools (pick the name that you prefer). - -If you are new to kooper first get familiar with the basic concepts and design of this toolkit, after that you will be redirected to some simple operators and controllers explained so you can see what can you do with this toolkit. - -## Concepts - -These will be the pieces of the framework required to implement and know to create an operator. -The required pieces you need to implement and know to create an operator are: -* `controller`: Take actions to comply the needs of resources. -* `retriever`: Knows how to list, watch and create a void object. -* `CRD`: A retriever that knows how to Initialize. -* `Handler`: Will be called when were events in the resources. - -### Controller - -A Kubernetes controller is something that listens/watch the status of a Kubernetes resource and takes actions so it meets the state required by the resource. Example: - -I listen to a `replicaset` resource that wants N instances of X `pod`. My controller should take whatever action it considers so the cluster has N instances of X. In this case It will create N `pod` resources. - -In this framework it is defined as an interface [here](https://github.com/spotahome/kooper/tree/master/operator/controller) - -```go -package controller - -type Controller interface { - Run(stopper <-chan struct{}) error -} -``` - -### Retriever - -In kubernetes world the way of retrieving resources is listing or watching (streaming events) them. In Kubernetes client libraries they are called [listerwatchers](https://github.com/kubernetes/client-go/blob/1b825e3a786379cb2ae2edc98a39e9c8cd68ee3c/tools/cache/listwatch.go#L35-L41). They know how to list and watch a resource kind. - -In this framework we have the concept of `Retriever` It knows how to list, watch and create a void object -so we can know the kind of the Retriever by inspecting the object. It is defined [here](https://github.com/spotahome/kooper/tree/master/operator/retrieve) - -```go -type Retriever interface { - GetListerWatcher() cache.ListerWatcher - GetObject() runtime.Object -} -``` - -### CRD - -A `crd` is a Custom Resource Definition (the evolution of the TPRs). It's a Kubernetes resource that is not a Kubernetes base resource (`pod`, `deployment`, `secret`...). It's used so we (as Kubernetes users) can create our custom resources (in the end a manifest/spec/definition) and use new resource kinds inside the cluster. - -In this framework we have the concept of `CRD`. It's just a retriever that knows how to Initialize. -The Initialize exists because when you want to use it in a Kubernetes cluster you need to ensure that the CRD is previously present (registered). - -```go -type CRD interface { - retrieve.Retriever - Initialize() error -} -``` - -### Handler - -The Handler is where our operator/controller logic will be placed. In other words, every time the resource we are *listwatching* for its events (delete, create, changed) the handler will be called. - -```go -type Handler interface { - Add(context.Context, runtime.Object) error - Delete(context.Context, string) error -} -``` - -Maybe you have some doubts like... - -* Where is the update method? -In the Kuberntes world there is the concept of state and eventual consistency and reconciliation loops. In a few words, You don't need to know -if the resource is new or not, only that the state should be this and eventually that state will be real in the cluster. - -* What happens if it errors my handling? -The event will be requeued for a new handling in the future until it rate limits the maximum times allowed (if this isn't rate limited you could get stuck forever handling same resources) - -* A context as parameter? -The context can be ignored if you don't need it at all, but if tracing is active the context will have the parent span. - - -### Operator - -All the concepts described above are glued together and create an operator. An [operator](https://coreos.com/operators/) its a concept invented by the CoreOS devs that is basically a regular controller that automates operator (a person) actions, so we manage CRDs to represent resources that our controllers will manage them. Example: - -I need to deploy a Prometheus, set up configuration... Instead of doing this myself (as a human operator) I create a new resource kind called `Prometheus`, or in other words a CRD called `Prometheus` with different options and create a controller that knows how to set the required state for our Prometheus CRD (this means deploying, configuring, backups, updating... a Promethus instance.). - -This way we just automated repetitive stuff, remove toil, errors... - -In this framework the concept operator is: controller(s) + CRD(s) = Operator. In other words an Operator initializes one or more CRDs and the runs one or more controllers related to one or more CRDs previously initialized. - -```go -type Operator interface { - Initialize() error - controller.Controller -} -``` - -## Usage - -In the end when using this framework or toolkit you need to know the concepts, but you will not use -all of them directly. For example we provide ways of bootstraping controllers and operators with already implemented utils. - -## Next - -At this moment you have the basic knowledge or the pillars that sustain this framework/toolkit, the next -thing you need to know is how to create and run stuff built with this toolkit: - -* [Controller tutorial.](controller-tutorial.md) -* [Operator tutorial.](operator-tutorial.md) diff --git a/docs/logger.md b/docs/logger.md deleted file mode 100644 index 83408366..00000000 --- a/docs/logger.md +++ /dev/null @@ -1,42 +0,0 @@ -# Logger - -In kooper everything is pluggable, this is because it embraces dependency injection in all of its components, that's why you can plug your own logger, the only requirement is to implement the [`Logger`][logger-interface] interface. - -```golang -type Logger interface { - Infof(format string, args ...interface{}) - Warningf(format string, args ...interface{}) - Errorf(format string, args ...interface{}) -} -``` - -Although it comes with some loggers by default. - -## Available loggers - -If you don't need a custom logger, you can pass - -* Dummy: doesn't log anything (mainly for the tests). -* Std: Uses default go logger (this logger is a global logger) - -## Use a logger in the controller - -```golang -import ( - "github.com/spotahome/kooper/log" - ... -) -... - -log := &log.Std{} - -... - -ctrl := controller.NewSequential(30*time.Second, hand, retr, m, log) -... -``` - -**Note: if you pass nil as the logger to the controller it will use `log.Std` logger by default** - - -[logger-interface]: https://github.com/spotahome/kooper/blob/master/log/log.go diff --git a/docs/metrics.md b/docs/metrics.md deleted file mode 100644 index 2ec1a4f3..00000000 --- a/docs/metrics.md +++ /dev/null @@ -1,79 +0,0 @@ -# Metrics - -Kooper comes with metrics support, this means that when you use kooper to bootstrap your operator or controller you have the possibility of instrumenting your controller for free. - -## Backends - -At this moment these are the supported backends: - -- Prometheus. - -## Custom backend - -ALthough Kooper supports by default some of the de-facto standard instrumenting backends, you could create your own backend, you just need to implement the provided [interface][metrics-interface] that is named as `Recorder`: - -```golang -type Recorder interface { - ... -} -``` - -## Measured metrics - -The measured metrics are: - -- Number of delete and add queued events (to be processed). -- Number of delete and add processed events. -- Number of delete and add processed events with an error. -- Duration of delete and add processed events. - -## How to use the recorder with the controller. - -When you create a controller you can pass the metrics recorder that you want. -**Note: If you pass a `nil` backend, it will not record any metric** - -If you want a full example, there is a controller [example][metrics-example] that uses the different metric backends - -### Prometheus - -Prometheus backend needs a prometheus [registerer][prometheus-registerer] and a namespace (a prefix for the metircs). - -For example: - -```golang -import ( - ... - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" - ... -) - -... - - reg := prometheus.NewRegistry() - m := metrics.NewPrometheus(metricsPrefix, reg) - - ... - - ctrl := controller.NewSequential(30*time.Second, hand, retr, m, log) - - ... - - h := promhttp.HandlerFor(reg, promhttp.HandlerOpts{}) - logger.Infof("serving metrics at %s", metricsAddr) - http.ListenAndServe(metricsAddr, h) - - return m -} -``` - -If you are using the default prometheus methods instead of a custom registry, you could get that from `prometheus.DefaultRegisterer` instead of creating a new registry `reg := prometheus.NewRegistry()` - -## Grafana dashboard (For Prometheus metrics) - -There is a grafana dashboard for your Kubernetes controllers. You can get it [here][grafana-dashboard] - -[metrics-interface]: https://github.com/spotahome/kooper/blob/master/monitoring/metrics/metrics.go -[metrics-example]: https://github.com/spotahome/kooper/tree/master/examples/metrics-controller -[prometheus-registerer]: https://godoc.org/github.com/prometheus/client_golang/prometheus#Registerer -[grafana-dashboard]: https://grafana.com/dashboards/7082 diff --git a/docs/tracing.md b/docs/tracing.md deleted file mode 100644 index f48111e9..00000000 --- a/docs/tracing.md +++ /dev/null @@ -1,134 +0,0 @@ -# Tracing - -Kooper has builting tracing support using [Opentracing][opentracing-url]. This will allow a controller/operator trace all the process from the moment kooper controller starts handling the event until it finishes handling it. The handlers of the objects will receive a `context` where the parent span (coming from Kooper) can be retrieved and continue the trace. - -## Root span - -Kooper will create a trace for each object received using the `Retriever` (using resync/list or watch events). This will create a root span that will set important data. - -### Relevant tags - -* `kubernetes.object.namespace`: The namespace of the object being processed. -* `kubernetes.object.name`: the name of the object being processed. -* `kubernetes.object.key`: Key of the object, with namespace/objectName style. -* `component`: Always kooper. -* `kooper.controller`: The name of the controller (got from the configuration). -* `controller.cfg.*`: Some tags with the configuration of the controller. -* `kubernetes.object.total_processed_times`: the number of times the same object has been processed (1 + retries). -* `kubernetes.object.processing_retry`: Marks if this processing object is a retry of a previous processed one (means that the past time it errored). -* `span.kind`: Always consumer kind. -* `error`: flag marking if it finished with error. - -### Relevant Logs - -The most relevan logs of the root span are the processing and error logs. -this is an example of a log in a processed object: - -* `0.03ms: event=baggage, key=kubernetes.object.key, value=kube-system/kube-dns-7785f4d7dc-4brdg` -* `0.05ms: event=process_object, kubernetes.object.key=kube-system/kube-dns-7785f4d7dc-4brdg` -* `1.32s: event=error, message=randomly failed` -* `1.32s: event=forget, kubernetes.object.key=kube-system/kube-dns-7785f4d7dc-4brdg, message=max number of retries reached after failing, forgetting object key` -* `1.32s: success=false` - -### Baggage - -The [baggage][ot-baggage-url] is data that will be propagated in all the trace. Kooper adds one single baggage (it's expensive, must be careful), is the `kubernetes.object.key` that has the object key of the object being processed that started all the trace. - -## Add/Delete handling span - -After the root span, Kooper will create a new child span from this that will be a processing Add or a Delete depending of what kind of event is. - -## Context on handler - -Handler receives the context that has the parten span. - -```go -type Handler interface { - Add(context.Context, runtime.Object) error - Delete(context.Context, string) error -} -``` - -You can get the parent span using opentracing API. For example: - -```go -hand := &handler.HandlerFunc{ - AddFunc: func(ctx context.Context, obj runtime.Object) error { - // Get the parent span. - pSpan := opentracing.SpanFromContext(ctx) - - // Create a new span. - span := tracer.StartSpan("AddFunc", opentracing.ChildOf(pSpan.Context())) - defer span.Finish() - - // Do stuff... - - return nil - }, - DeleteFunc: func(ctx context.Context, s string) error { - // Get the parent span. - pSpan := opentracing.SpanFromContext(ctx) - - // Create a new span. - span := tracer.StartSpan("DeleteFunc", opentracing.ChildOf(pSpan.Context())) - defer span.Finish() - - // Do stuff... - - return nil - }, -} -``` - -## Span diagram example - -Kooper creates the first two spans and a user using kooper in it's controller creates 4 new spans. - -```text -––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time - - [ Kooper processJob ········································] - [Kooper handleAddObject/handleDeleteObject···············] - [User span1......] - [User span2·····] - [User span3··········] - [User span4····] -``` - -## Tracer Service - -The service name that identifies the traces created by kooper is managed by the tracer instance passed to the controller when creating the controller, this is that the tracer service name will be the trace service name. - -## Example - -Kooper comes with an [example][traced-controller], it's just the pod print controller example with faked calls to different services like Redis or AWS but tracing this calls. It uses the opentracing implementation of [Jaeger][jaeger-url]. - -First you need to run a development instance of Jaeger in localhost, the tracer passed to kooper controller will send the generated traces to this jaeger instance. - -Run jaeger with docker. - -```bash -docker run --rm -it \ - -p5775:5775/udp \ - -p6831:6831/udp \ - -p6832:6832/udp \ - -p5778:5778 \ - -p16686:16686 \ - -p14268:14268 \ - -p9411:9411 \ - jaegertracing/all-in-one:latest -``` - -Now just run the example, you need kubectl set to the cluster, it will make fake calls to faked services and will error randomly on the service calls, making retries on the object processing and creating different kind of traces. - -```bash -go run ./examples/traced-controller/* -``` - -Check the created traces of the controller in [jaeger ui][jaeger-ui-local-url] - -[opentracing-url][http://opentracing.io] -[ot-baggage-url][https://github.com/opentracing/specification/blob/master/specification.md#set-a-baggage-item] -[traced-controller][https://github.com/spotahome/kooper/tree/master/examples/traced-controller] -[jaeger-url][http://www.jaegertracing.io/] -[jaeger-ui-local-url][http://127.0.0.1:16686] \ No newline at end of file From 2539de460c45bd366d86d9f0c1eff28f00abf8ee Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Sun, 12 Apr 2020 11:51:41 +0200 Subject: [PATCH 16/31] Update non K8s deps Signed-off-by: Xabier Larrakoetxea --- go.mod | 7 ++- go.sum | 50 ++++++++++++++++------ test/integration/helper/prepare/prepare.go | 6 +-- 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index 2b45ad2b..7acb8db0 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,9 @@ module github.com/spotahome/kooper require ( - github.com/Pallinder/go-randomdata v1.2.0 - github.com/prometheus/client_golang v1.1.0 - github.com/sirupsen/logrus v1.4.2 - github.com/stretchr/testify v1.4.0 + github.com/prometheus/client_golang v1.5.1 + github.com/sirupsen/logrus v1.5.0 + github.com/stretchr/testify v1.5.1 k8s.io/api v0.15.10 k8s.io/apimachinery v0.15.12-beta.0 k8s.io/client-go v0.15.10 diff --git a/go.sum b/go.sum index c60f9ef3..4cec94a0 100644 --- a/go.sum +++ b/go.sum @@ -2,15 +2,17 @@ cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/Azure/go-autorest v11.1.2+incompatible h1:viZ3tV5l4gE2Sw0xrasFHytCGtzYCrT+um/rrSQ1BfA= github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Pallinder/go-randomdata v1.2.0 h1:DZ41wBchNRb/0GfsePLiSwb0PHZmT67XY00lCDlaYPg= -github.com/Pallinder/go-randomdata v1.2.0/go.mod h1:yHmJgulpD2Nfrm0cR9tI/+oAgRqCQQixsA8HyRZfV9Y= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -23,6 +25,7 @@ github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6 github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -40,6 +43,9 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -58,12 +64,17 @@ github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -82,27 +93,32 @@ github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuB github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= +github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q= +github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -113,6 +129,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= @@ -131,6 +149,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -138,8 +157,8 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU= @@ -147,6 +166,8 @@ golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5f golang.org/x/time v0.0.0-20161028155119-f51c12702a4d h1:TnM+PKb3ylGmZvyPXmo9m/wktg7Jn/a/fNmr33HSj8g= golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= @@ -154,6 +175,8 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o= @@ -164,11 +187,12 @@ gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= k8s.io/api v0.15.10 h1:g6t2OLNjupSeoepE0zlIcvoT6Q+QDfdfEUwk5lwHXAo= k8s.io/api v0.15.10/go.mod h1:PffiEKNf0aFiv2naEYSGTFHIGA9V8Qwt22DZIAokOzQ= -k8s.io/api v0.17.4 h1:HbwOhDapkguO8lTAE8OX3hdF2qp8GtpC9CW/MQATXXo= k8s.io/apimachinery v0.15.10/go.mod h1:ZRw+v83FjgEqlzqaBkxL3XB21MSLYdzjsY9Bgxclhdw= k8s.io/apimachinery v0.15.12-beta.0 h1:aGvobE1kXnMyyAgzsYe6bfyyAcoIy2vqwPtSO/PgGBg= k8s.io/apimachinery v0.15.12-beta.0/go.mod h1:ZRw+v83FjgEqlzqaBkxL3XB21MSLYdzjsY9Bgxclhdw= diff --git a/test/integration/helper/prepare/prepare.go b/test/integration/helper/prepare/prepare.go index 9a0e012e..8c337058 100644 --- a/test/integration/helper/prepare/prepare.go +++ b/test/integration/helper/prepare/prepare.go @@ -1,10 +1,10 @@ package prepare import ( - "strings" + "fmt" "testing" + "time" - randomdata "github.com/Pallinder/go-randomdata" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -22,7 +22,7 @@ type Preparer struct { func New(cli kubernetes.Interface, t *testing.T) *Preparer { ns := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ - Name: strings.ToLower(randomdata.SillyName()), + Name: fmt.Sprintf("kooper-integration-test-%d", time.Now().UTC().UnixNano()), }, } return &Preparer{ From 3779c9bdaac10fd2e03dc438ae007f05e12d7ea4 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Sun, 12 Apr 2020 12:07:38 +0200 Subject: [PATCH 17/31] Add multiple Kubernetes versions on integration tests Signed-off-by: Xabier Larrakoetxea --- .travis.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 756ea0c3..ad5811b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ services: language: go go: - - "1.13" + - "1.14" before_install: - curl -Lo kind https://github.com/kubernetes-sigs/kind/releases/download/${KIND_VERSION}/kind-linux-amd64 && chmod +x kind && sudo mv kind /usr/local/bin/ @@ -16,6 +16,11 @@ script: env: global: - - GO111MODULE=on - KIND_VERSION=v0.7.0 - - KUBERNETES_VERSION=1.15.7 + +matrix: + include: + - env: KUBERNETES_VERSION=1.15.7 + - env: KUBERNETES_VERSION=1.16.4 + - env: KUBERNETES_VERSION=1.17.0 + - env: KUBERNETES_VERSION=1.18.0 \ No newline at end of file From cc7b727f34052c265d387c52c2deb896e8d24fb0 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Mon, 13 Apr 2020 09:37:47 +0200 Subject: [PATCH 18/31] Update Kooper for Kubernetes v1.17 Signed-off-by: Xabier Larrakoetxea --- README.md | 10 + examples/pod-terminator-operator/Makefile | 2 +- .../k8s/clientset/versioned/clientset.go | 7 + examples/pod-terminator-operator/go.mod | 10 +- examples/pod-terminator-operator/go.sum | 134 +++++++++++++ .../chaos.spotahome.com_podterminators.yaml | 4 +- go.mod | 6 +- go.sum | 181 +++++++++++++----- 8 files changed, 299 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 45c71bdb..4b5ec0d5 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,16 @@ Kooper is a Go library to create simple and flexible [controllers]/operators, in In other words, is a small alternative to big frameworks like [Kubebuilder] or [operator-framework]. +## Features + +- Easy usage and fast to get it working. +- Extensible (Kooper doesn't get in your way). +- Simple core concepts (`Retriever` + `Handler` == controller && controller == operator). +- Metrics (extensible with Prometheus already implementated). +- Ready for core Kubernetes resources (pods, ingress, deployments...) and CRDs. +- Optional leader election system for controllers. +- Optional MultiResource controllers (e.g deployments and pods). + ## Getting started The simplest example that prints pods would be this: diff --git a/examples/pod-terminator-operator/Makefile b/examples/pod-terminator-operator/Makefile index 605efdaf..8cf0f91d 100644 --- a/examples/pod-terminator-operator/Makefile +++ b/examples/pod-terminator-operator/Makefile @@ -1,5 +1,5 @@ -CODE_GENERATOR_IMAGE := quay.io/slok/kube-code-generator:v1.15.10 +CODE_GENERATOR_IMAGE := quay.io/slok/kube-code-generator:v1.17.3 CRD_GENERATOR_IMAGE := quay.io/slok/kube-code-generator:latest DIRECTORY := $(PWD) ROOT_DIRECTORY := $(DIRECTORY)/../.. diff --git a/examples/pod-terminator-operator/client/k8s/clientset/versioned/clientset.go b/examples/pod-terminator-operator/client/k8s/clientset/versioned/clientset.go index 6ee779a4..cb16b2e5 100644 --- a/examples/pod-terminator-operator/client/k8s/clientset/versioned/clientset.go +++ b/examples/pod-terminator-operator/client/k8s/clientset/versioned/clientset.go @@ -19,6 +19,8 @@ limitations under the License. package versioned import ( + "fmt" + chaosv1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1" discovery "k8s.io/client-go/discovery" rest "k8s.io/client-go/rest" @@ -51,9 +53,14 @@ func (c *Clientset) Discovery() discovery.DiscoveryInterface { } // NewForConfig creates a new Clientset for the given config. +// If config's RateLimiter is not set and QPS and Burst are acceptable, +// NewForConfig will generate a rate-limiter in configShallowCopy. func NewForConfig(c *rest.Config) (*Clientset, error) { configShallowCopy := *c if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + if configShallowCopy.Burst <= 0 { + return nil, fmt.Errorf("Burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") + } configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) } var cs Clientset diff --git a/examples/pod-terminator-operator/go.mod b/examples/pod-terminator-operator/go.mod index b6421bb8..f81e1c57 100644 --- a/examples/pod-terminator-operator/go.mod +++ b/examples/pod-terminator-operator/go.mod @@ -5,11 +5,11 @@ go 1.14 replace github.com/spotahome/kooper => ../../ require ( - github.com/sirupsen/logrus v1.4.2 + github.com/sirupsen/logrus v1.5.0 github.com/spotahome/kooper v0.0.0 - github.com/stretchr/testify v1.4.0 - k8s.io/api v0.15.10 + github.com/stretchr/testify v1.5.1 + k8s.io/api v0.17.4 k8s.io/apiextensions-apiserver v0.15.10 - k8s.io/apimachinery v0.15.12-beta.0 - k8s.io/client-go v0.15.10 + k8s.io/apimachinery v0.17.4 + k8s.io/client-go v0.17.4 ) diff --git a/examples/pod-terminator-operator/go.sum b/examples/pod-terminator-operator/go.sum index a3186b33..d2e303a8 100644 --- a/examples/pod-terminator-operator/go.sum +++ b/examples/pod-terminator-operator/go.sum @@ -1,33 +1,51 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v11.1.2+incompatible h1:viZ3tV5l4gE2Sw0xrasFHytCGtzYCrT+um/rrSQ1BfA= github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/Pallinder/go-randomdata v1.2.0/go.mod h1:yHmJgulpD2Nfrm0cR9tI/+oAgRqCQQixsA8HyRZfV9Y= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-oidc v0.0.0-20180117170138-065b426bd416/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.0.0-20180108230905-e214231b295a/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda h1:NyywMz59neOoVRFDz+ccfKWxn784fiHMDnZSy6T+JXY= github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= @@ -35,30 +53,38 @@ github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkg github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.17.2/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= @@ -67,45 +93,73 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8 h1:L9JPKrtsHMQ4VCRQfHvbbHBfB2Urn8xf6QZeXZ+OrN4= github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v0.0.0-20170330212424-2500245aa611/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jonboulle/clockwork v0.0.0-20141017032234-72f9bd7c4e0c/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -113,6 +167,7 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -120,13 +175,19 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3 h1:EooPXg51Tn+xmWPXJUGCnJhJSpeuMlBmfJVcqIRmmv8= github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= @@ -135,93 +196,149 @@ github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cobra v0.0.0-20180319062004-c439c4fa0937/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68= golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20161028155119-f51c12702a4d h1:TnM+PKb3ylGmZvyPXmo9m/wktg7Jn/a/fNmr33HSj8g= golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20170731182057-09f6ed296fc6/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o= gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0-20150622162204-20b71e5b60d7/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/square/go-jose.v2 v2.0.0-20180411045311-89060dee6a84/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -229,33 +346,50 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.15.10 h1:g6t2OLNjupSeoepE0zlIcvoT6Q+QDfdfEUwk5lwHXAo= k8s.io/api v0.15.10/go.mod h1:PffiEKNf0aFiv2naEYSGTFHIGA9V8Qwt22DZIAokOzQ= +k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA= k8s.io/apiextensions-apiserver v0.15.10 h1:S5CJaYVIceZ58yg/AkyPPS+X2CPLkB6DpY9+pFHoHd0= k8s.io/apiextensions-apiserver v0.15.10/go.mod h1:W0KZ5vYUt8GuAgEVfRcx4NUBXs/Gp5X76hrjvi7nTZE= k8s.io/apimachinery v0.15.10/go.mod h1:ZRw+v83FjgEqlzqaBkxL3XB21MSLYdzjsY9Bgxclhdw= k8s.io/apimachinery v0.15.12-beta.0 h1:aGvobE1kXnMyyAgzsYe6bfyyAcoIy2vqwPtSO/PgGBg= k8s.io/apimachinery v0.15.12-beta.0/go.mod h1:ZRw+v83FjgEqlzqaBkxL3XB21MSLYdzjsY9Bgxclhdw= +k8s.io/apimachinery v0.17.4 h1:UzM+38cPUJnzqSQ+E1PY4YxMHIzQyCg29LOoGfo79Zw= +k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= k8s.io/apiserver v0.15.10/go.mod h1:6KNMxtnZD0q3kmaRiBF8xyuBeF/D6HlAHgtOR0CsNBc= k8s.io/client-go v0.15.10 h1:WyH6P2wgULDTFqqClm8cP1E2ELj2qbrxJGR76/SLBQw= k8s.io/client-go v0.15.10/go.mod h1:UkLY6FcnDCJhI7sabgHI+WovVUTZKvmcOxFrSLOVr4g= +k8s.io/client-go v0.17.4/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc= k8s.io/code-generator v0.15.10/go.mod h1:G8bQwmHm2eafm5bgtX67XDZQ8CWKSGu9DekI+yN4Y5I= k8s.io/component-base v0.15.10/go.mod h1:cYv0GMIaJQEHd1Pgdva5eBRxu1ZXwi1dyYAM0vRhyW0= k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68= k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30 h1:TRb4wNWoBVrH9plmkp2q86FIDppkbrEXdXlxU3a3BMI= k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/utils v0.0.0-20190221042446-c2654d5206da h1:ElyM7RPonbKnQqOcw7dG2IK5uvQQn3b/WPHqD5mBvP4= k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= sigs.k8s.io/structured-merge-diff v0.0.0-20190302045857-e85c7b244fd2/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/examples/pod-terminator-operator/manifests/chaos.spotahome.com_podterminators.yaml b/examples/pod-terminator-operator/manifests/chaos.spotahome.com_podterminators.yaml index bf4f7308..69c26a3a 100644 --- a/examples/pod-terminator-operator/manifests/chaos.spotahome.com_podterminators.yaml +++ b/examples/pod-terminator-operator/manifests/chaos.spotahome.com_podterminators.yaml @@ -28,12 +28,12 @@ spec: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' diff --git a/go.mod b/go.mod index 7acb8db0..06e47105 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,9 @@ require ( github.com/prometheus/client_golang v1.5.1 github.com/sirupsen/logrus v1.5.0 github.com/stretchr/testify v1.5.1 - k8s.io/api v0.15.10 - k8s.io/apimachinery v0.15.12-beta.0 - k8s.io/client-go v0.15.10 + k8s.io/api v0.17.4 + k8s.io/apimachinery v0.17.4 + k8s.io/client-go v0.17.4 ) go 1.14 diff --git a/go.sum b/go.sum index 4cec94a0..21e7376a 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,25 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/Azure/go-autorest v11.1.2+incompatible h1:viZ3tV5l4gE2Sw0xrasFHytCGtzYCrT+um/rrSQ1BfA= -github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0 h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -13,60 +31,85 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda h1:NyywMz59neOoVRFDz+ccfKWxn784fiHMDnZSy6T+JXY= -github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac= -github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8 h1:L9JPKrtsHMQ4VCRQfHvbbHBfB2Urn8xf6QZeXZ+OrN4= -github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= -github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -75,25 +118,33 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3 h1:EooPXg51Tn+xmWPXJUGCnJhJSpeuMlBmfJVcqIRmmv8= -github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -119,11 +170,14 @@ github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q= github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= -github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -131,56 +185,90 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68= -golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= -golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU= -golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/time v0.0.0-20161028155119-f51c12702a4d h1:TnM+PKb3ylGmZvyPXmo9m/wktg7Jn/a/fNmr33HSj8g= -golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o= -gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= @@ -191,18 +279,23 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -k8s.io/api v0.15.10 h1:g6t2OLNjupSeoepE0zlIcvoT6Q+QDfdfEUwk5lwHXAo= -k8s.io/api v0.15.10/go.mod h1:PffiEKNf0aFiv2naEYSGTFHIGA9V8Qwt22DZIAokOzQ= -k8s.io/apimachinery v0.15.10/go.mod h1:ZRw+v83FjgEqlzqaBkxL3XB21MSLYdzjsY9Bgxclhdw= -k8s.io/apimachinery v0.15.12-beta.0 h1:aGvobE1kXnMyyAgzsYe6bfyyAcoIy2vqwPtSO/PgGBg= -k8s.io/apimachinery v0.15.12-beta.0/go.mod h1:ZRw+v83FjgEqlzqaBkxL3XB21MSLYdzjsY9Bgxclhdw= -k8s.io/client-go v0.15.10 h1:WyH6P2wgULDTFqqClm8cP1E2ELj2qbrxJGR76/SLBQw= -k8s.io/client-go v0.15.10/go.mod h1:UkLY6FcnDCJhI7sabgHI+WovVUTZKvmcOxFrSLOVr4g= -k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68= -k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30 h1:TRb4wNWoBVrH9plmkp2q86FIDppkbrEXdXlxU3a3BMI= -k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= -k8s.io/utils v0.0.0-20190221042446-c2654d5206da h1:ElyM7RPonbKnQqOcw7dG2IK5uvQQn3b/WPHqD5mBvP4= -k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/api v0.17.4 h1:HbwOhDapkguO8lTAE8OX3hdF2qp8GtpC9CW/MQATXXo= +k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA= +k8s.io/apimachinery v0.17.4 h1:UzM+38cPUJnzqSQ+E1PY4YxMHIzQyCg29LOoGfo79Zw= +k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= +k8s.io/client-go v0.17.4 h1:VVdVbpTY70jiNHS1eiFkUt7ZIJX3txd29nDxxXH4en8= +k8s.io/client-go v0.17.4/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= From 5d9a12e0a95db78896563a3a6afe65971aee60f9 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Sun, 3 May 2020 10:12:39 +0200 Subject: [PATCH 19/31] Remove multiretriever and update the example to have the same functionality using multiple controllers Signed-off-by: Xabier Larrakoetxea --- CHANGELOG.md | 1 - README.md | 27 ++++- controller/controller.go | 2 +- controller/retrieve.go | 115 --------------------- controller/retrieve_test.go | 106 ------------------- examples/multi-resource-controller/main.go | 92 ++++++++++------- go.mod | 4 +- go.sum | 31 ++++-- 8 files changed, 101 insertions(+), 277 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 403c126a..883530d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,6 @@ NOTE: Breaking release in controllers. - Refactor Logger with structured logging. - Add Logrus helper wrapper. - Refactor to simplify the retrievers. -- Add multiretriever to retriever different resource types on the same controller. - Refactor metrics recorder implementation including the prometheus backend. - Refactor internal controller queue into a decorator implementation approach. - Remove `Delete` method from `controller.Handler` and simplify to only `Handle` method diff --git a/README.md b/README.md index 4b5ec0d5..626e6f86 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ In other words, is a small alternative to big frameworks like [Kubebuilder] or [ - Metrics (extensible with Prometheus already implementated). - Ready for core Kubernetes resources (pods, ingress, deployments...) and CRDs. - Optional leader election system for controllers. -- Optional MultiResource controllers (e.g deployments and pods). ## Getting started @@ -101,10 +100,9 @@ A controller is based on 3 simple concepts: ### Retriever -The component that lists and watch the resources the controller will handle when there is a change. Kooper comes with some helpers to create fast retrievers and combination of multiple of them: +The component that lists and watch the resources the controller will handle when there is a change. Kooper comes with some helpers to create fast retrievers: - `Retriever`: The core retriever it needs to implement list (list objects), and watch, subscribe to object changes. -- `MultiRetriever`: A retriever that can combine multiple retriever of different types to handle changes on the same controller `Handler`. - `RetrieverFromListerWatcher`: Converts a Kubernetes ListerWatcher into a kooper Retriever. The `Retriever` can be based on Kubernetes base resources (Pod, Deployment, Service...) or based on CRDs, theres no distinction. @@ -142,6 +140,26 @@ If your controller creates as a side effect new Kubernetes resources you can use On the other hand if you want a more flexible clean up process (e.g clean from a database or a 3rd party service) you can use [finalizers], check the [pod-terminator-operator][finalizer-example] example. +### Multiresource or secondary resources + +Sometimes we have contorllers that work on a main or primary resource and we want to also handle the events of a secondary resource that is based on the first one. For example a deployment controller that watches the pods that belong to the deployment handled. + +After experiencing with controllers and implementing a way of creating multiresource controllers we though that is not necesary becase: + +- Adds complexity. +- Adds corner cases and bugs, e.g + - Internal cache be based on IDs of `{namespace}/{name}` receiving a deletion watch event of one type removes the other type object with the same name from the cache + - Internal cache be based on IDs of `{namespace}/{name}`, the different resources that share name, only processsing one of the types. + - An error on one of the retrieval types stops all the controller process and not only the one based on that type. +- The benefit of having this is to reuse the handler (we already can do this, a `Handler` is easy to reuse). + +The solution to this embraces simplicity once again, is to create multiple controllers using the same `Handler` but with a different `ListerWatcher`, the `Handler` API is easy enough to reuse it across multiple controllers, check an [example][multiresource-example] of creating a multiresource controller. This has more benefits: + +- Different controller interval depending on the type (fast changing secondary objects can reconcile faster than the primary one, or viceversa). +- Wrap the controller handler with a middlewre only for a particular type. +- One of the type retrieval fails, the other type controller continues working (running in degradation mode). +- Flexibility, e.g leader election for the primary type, no leader election for the secondary type. + ## Compatibility matrix | | Kubernetes <=1.9 | Kubernetes 1.10 | Kubernetes 1.11 | Kubernetes 1.12 | Kubernetes 1.13 | Kubernetes 1.14 | @@ -170,4 +188,5 @@ On the other hand if you want a more flexible clean up process (e.g clean from a [kube-code-generator]: https://github.com/slok/kube-code-generator [owner-ref]: https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/#owners-and-dependents [finalizers]: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#finalizers -[finalizer-example]: examples/pod-terminator-operator/operator/operator.go \ No newline at end of file +[finalizer-example]: examples/pod-terminator-operator/operator/operator.go +[multiresource-example]: examples/pod-terminator-operator/multi-resource-controller \ No newline at end of file diff --git a/controller/controller.go b/controller/controller.go index a53dee24..a28eb708 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -160,7 +160,7 @@ func New(cfg *Config) (Controller, error) { } queue.Add(context.TODO(), key) }, - UpdateFunc: func(old interface{}, new interface{}) { + UpdateFunc: func(_ interface{}, new interface{}) { key, err := cache.MetaNamespaceKeyFunc(new) if err != nil { cfg.Logger.Warningf("could not add item from 'update' event to queue: %s", err) diff --git a/controller/retrieve.go b/controller/retrieve.go index b65c8a14..f4b3c306 100644 --- a/controller/retrieve.go +++ b/controller/retrieve.go @@ -3,9 +3,7 @@ package controller import ( "context" "fmt" - "sync" - "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" @@ -50,116 +48,3 @@ func (l listerWatcherRetriever) List(_ context.Context, options metav1.ListOptio func (l listerWatcherRetriever) Watch(_ context.Context, options metav1.ListOptions) (watch.Interface, error) { return l.lw.Watch(options) } - -type multiRetriever struct { - rts []Retriever -} - -// NewMultiRetriever returns a lister watcher that will list multiple types -// -// With this multi lister watcher a controller can receive updates in multiple types -// for example on pods and a deployments. -func NewMultiRetriever(retrievers ...Retriever) (Retriever, error) { - for _, r := range retrievers { - if r == nil { - return nil, fmt.Errorf("at least one of the retrievers is nil") - } - } - - return multiRetriever{ - rts: retrievers, - }, nil -} - -func (m multiRetriever) List(ctx context.Context, options metav1.ListOptions) (runtime.Object, error) { - ls := &metav1.List{} - for _, r := range m.rts { - lo, err := r.List(ctx, *options.DeepCopy()) - if err != nil { - return nil, err - } - - items, err := meta.ExtractList(lo) - if err != nil { - return nil, err - } - for _, item := range items { - ls.Items = append(ls.Items, runtime.RawExtension{Object: item}) - } - } - - return ls, nil -} - -func (m multiRetriever) Watch(ctx context.Context, options metav1.ListOptions) (watch.Interface, error) { - ws := make([]watch.Interface, len(m.rts)) - for i, rt := range m.rts { - w, err := rt.Watch(ctx, options) - if err != nil { - return nil, err - } - ws[i] = w - } - - return newMultiWatcher(ws...), nil -} - -type multiWatcher struct { - stopped bool - mu sync.Mutex - stop chan struct{} - ch chan watch.Event - ws []watch.Interface -} - -func newMultiWatcher(ws ...watch.Interface) watch.Interface { - m := &multiWatcher{ - stop: make(chan struct{}), - ch: make(chan watch.Event), - ws: ws, - } - - // Run all watchers. - // TODO(slok): call run here or lazy on ResultChan(), this last option can be dangerous (multiple calls). - for _, w := range ws { - go m.run(w) - } - - return m -} - -func (m *multiWatcher) Stop() { - m.mu.Lock() - defer m.mu.Unlock() - if m.stopped { - return - } - - for _, w := range m.ws { - w.Stop() - } - - close(m.stop) - close(m.ch) - m.stopped = true -} - -func (m *multiWatcher) ResultChan() <-chan watch.Event { - return m.ch -} - -func (m *multiWatcher) run(w watch.Interface) { - c := w.ResultChan() - for { - select { - case <-m.stop: - return - case e, ok := <-c: - // Channel has been closed no need this loop anymore. - if !ok { - return - } - m.ch <- e - } - } -} diff --git a/controller/retrieve_test.go b/controller/retrieve_test.go index cb43ee4a..d7dafee2 100644 --- a/controller/retrieve_test.go +++ b/controller/retrieve_test.go @@ -3,15 +3,11 @@ package controller_test import ( "context" "fmt" - "sort" "testing" - "time" "github.com/spotahome/kooper/controller" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" @@ -116,105 +112,3 @@ func TestRetrieverFromListerWatcher(t *testing.T) { }) } } - -func TestMultiRetriever(t *testing.T) { - tests := map[string]struct { - retrievers []controller.Retriever - expList func() runtime.Object - expListErr bool - expWatch func() []watch.Event - expWatchErr bool - }{ - "A List error or a watch error should be propagated to the upper layer if any of the retrievers fail.": { - retrievers: []controller.Retriever{ - controller.MustRetrieverFromListerWatcher(&cache.ListWatch{ - ListFunc: testPodListFunc(testPodList), - WatchFunc: testEventWatchFunc(testEventList), - }), - controller.MustRetrieverFromListerWatcher(&cache.ListWatch{ - ListFunc: func(_ metav1.ListOptions) (runtime.Object, error) { return nil, fmt.Errorf("wanted error") }, - WatchFunc: func(_ metav1.ListOptions) (watch.Interface, error) { return nil, fmt.Errorf("wanted error") }, - }), - controller.MustRetrieverFromListerWatcher(&cache.ListWatch{ - ListFunc: testPodListFunc(testPodList), - WatchFunc: testEventWatchFunc(testEventList), - }), - }, - expList: func() runtime.Object { return nil }, - expListErr: true, - expWatch: func() []watch.Event { return nil }, - expWatchErr: true, - }, - - "the lists and watch should be merged with the different retrievers result.": { - retrievers: []controller.Retriever{ - controller.MustRetrieverFromListerWatcher(&cache.ListWatch{ - ListFunc: testPodListFunc(&corev1.PodList{Items: testPodList.Items[0:3]}), - WatchFunc: testEventWatchFunc(testEventList[0:3]), - }), - controller.MustRetrieverFromListerWatcher(&cache.ListWatch{ - ListFunc: testPodListFunc(&corev1.PodList{Items: testPodList.Items[3:5]}), - WatchFunc: testEventWatchFunc(testEventList[3:5]), - }), - controller.MustRetrieverFromListerWatcher(&cache.ListWatch{ - ListFunc: testPodListFunc(&corev1.PodList{Items: testPodList.Items[5:7]}), - WatchFunc: testEventWatchFunc(testEventList[5:7]), - }), - }, - expList: func() runtime.Object { - items, _ := meta.ExtractList(testPodList) - l := &metav1.List{} - for _, item := range items { - l.Items = append(l.Items, runtime.RawExtension{Object: item}) - } - - return l - }, - expWatch: func() []watch.Event { - return testEventList - }, - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - assert := assert.New(t) - require := require.New(t) - - ret, err := controller.NewMultiRetriever(test.retrievers...) - require.NoError(err) - - // Test list. - objs, err := ret.List(context.TODO(), metav1.ListOptions{}) - if test.expListErr { - assert.Error(err) - } else if assert.NoError(err) { - assert.Equal(test.expList(), objs) - } - - // Test watch. - w, err := ret.Watch(context.TODO(), metav1.ListOptions{}) - evs := []watch.Event{} - if test.expWatchErr { - assert.Error(err) - } else if assert.NoError(err) { - // Stop the watcher after some time so we can continue with the test. - // We assume that we had enough time to get all the events. - go func() { - time.Sleep(20 * time.Millisecond) - w.Stop() - }() - for ev := range w.ResultChan() { - evs = append(evs, ev) - } - - // Sort by object name. - sort.SliceStable(evs, func(i, j int) bool { - return evs[i].Object.(metav1.Object).GetName() < evs[j].Object.(metav1.Object).GetName() - }) - - assert.Equal(test.expWatch(), evs) - } - }) - } -} diff --git a/examples/multi-resource-controller/main.go b/examples/multi-resource-controller/main.go index ff9d72c8..466b1b97 100644 --- a/examples/multi-resource-controller/main.go +++ b/examples/multi-resource-controller/main.go @@ -44,34 +44,6 @@ func run() error { return fmt.Errorf("error creating kubernetes client: %w", err) } - // Create our retriever so the controller knows how to get/listen for deployments and statefulsets. - retr, err := controller.NewMultiRetriever( - controller.MustRetrieverFromListerWatcher( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - return k8scli.AppsV1().Deployments("").List(options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - return k8scli.AppsV1().Deployments("").Watch(options) - }, - }, - ), - controller.MustRetrieverFromListerWatcher( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - return k8scli.AppsV1().StatefulSets("").List(options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - return k8scli.AppsV1().StatefulSets("").Watch(options) - }, - }, - ), - ) - - if err != nil { - return fmt.Errorf("could not create a multi retriever: %w", err) - } - // Our domain logic that will print every add/sync/update and delete event we . hand := controller.HandlerFunc(func(_ context.Context, obj runtime.Object) error { dep, ok := obj.(*appsv1.Deployment) @@ -89,27 +61,67 @@ func run() error { return nil }) - // Create the controller with custom configuration. - cfg := &controller.Config{ - Name: "multi-resource-controller", - Handler: hand, - Retriever: retr, - Logger: logger, + // Create the controller for deployments. + ctrlDep, err := controller.New(&controller.Config{ + Name: "multi-resource-controller-deployments", + Handler: hand, + Retriever: controller.MustRetrieverFromListerWatcher( + &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + return k8scli.AppsV1().Deployments("").List(options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + return k8scli.AppsV1().Deployments("").Watch(options) + }, + }, + ), + Logger: logger, ProcessingJobRetries: 5, ResyncInterval: 45 * time.Second, ConcurrentWorkers: 1, - } - ctrl, err := controller.New(cfg) + }) if err != nil { - return fmt.Errorf("could not create controller: %w", err) + return fmt.Errorf("could not create deployment resource controller: %w", err) } - // Start our controller. + // Create the controller for statefulsets. + ctrlSt, err := controller.New(&controller.Config{ + Name: "multi-resource-controller-statefulsets", + Handler: hand, + Retriever: controller.MustRetrieverFromListerWatcher( + &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + return k8scli.AppsV1().StatefulSets("").List(options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + return k8scli.AppsV1().StatefulSets("").Watch(options) + }, + }, + ), + Logger: logger, + + ProcessingJobRetries: 5, + ResyncInterval: 45 * time.Second, + ConcurrentWorkers: 1, + }) + + // Start our controllers. stopC := make(chan struct{}) - err = ctrl.Run(stopC) + defer close(stopC) + errC := make(chan error) + go func() { + errC <- ctrlDep.Run(stopC) + }() + + go func() { + errC <- ctrlSt.Run(stopC) + }() + + // Wait unitl one finishes + err = <-errC if err != nil { - return fmt.Errorf("error running controller: %w", err) + return fmt.Errorf("error running controllers: %w", err) } return nil diff --git a/go.mod b/go.mod index 06e47105..ab72b206 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module github.com/spotahome/kooper require ( - github.com/prometheus/client_golang v1.5.1 - github.com/sirupsen/logrus v1.5.0 + github.com/prometheus/client_golang v1.6.0 + github.com/sirupsen/logrus v1.6.0 github.com/stretchr/testify v1.5.1 k8s.io/api v0.17.4 k8s.io/apimachinery v0.17.4 diff --git a/go.sum b/go.sum index 21e7376a..a9e0208a 100644 --- a/go.sum +++ b/go.sum @@ -72,6 +72,12 @@ github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -112,6 +118,8 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -149,8 +157,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= -github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.6.0 h1:YVPodQOcK15POxhgARIvnDRVpLcuK8mglnMrWfyrw6A= +github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= @@ -162,14 +170,14 @@ github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI= +github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q= -github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -230,8 +238,9 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -259,6 +268,12 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 4bf03ecb5e4f4d1f0a730d70b8da7dca15fb73ca Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Sun, 3 May 2020 10:37:31 +0200 Subject: [PATCH 20/31] Fix metrics Signed-off-by: Xabier Larrakoetxea --- metrics/prometheus/prometheus.go | 2 +- metrics/prometheus/prometheus_test.go | 66 +++++++++++++-------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index b0b1f667..9816044b 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -63,7 +63,7 @@ func New(cfg Config) *Recorder { inQueueEventDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: promNamespace, Subsystem: promControllerSubsystem, - Name: "event_in_queue_duration_total", + Name: "event_in_queue_duration_seconds", Help: "The duration of an event in the queue.", Buckets: cfg.InQueueBuckets, }, []string{"controller"}), diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go index 07289e43..604462f2 100644 --- a/metrics/prometheus/prometheus_test.go +++ b/metrics/prometheus/prometheus_test.go @@ -50,33 +50,33 @@ func TestPrometheusRecorder(t *testing.T) { r.ObserveResourceInQueueDuration(ctx, "ctrl2", t0.Add(-17*time.Millisecond)) }, expMetrics: []string{ - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="0.005"} 0`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="0.01"} 0`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="0.025"} 0`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="0.05"} 0`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="0.1"} 0`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="0.25"} 0`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="0.5"} 1`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="1"} 1`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="2.5"} 1`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="5"} 2`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="10"} 2`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="+Inf"} 2`, - `kooper_controller_event_in_queue_duration_total_count{controller="ctrl1"} 2`, - - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="0.005"} 0`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="0.01"} 0`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="0.025"} 1`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="0.05"} 2`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="0.1"} 2`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="0.25"} 2`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="0.5"} 2`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="1"} 3`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="2.5"} 3`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="5"} 3`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="10"} 4`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl2",le="+Inf"} 4`, - `kooper_controller_event_in_queue_duration_total_count{controller="ctrl2"} 4`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="0.005"} 0`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="0.01"} 0`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="0.025"} 0`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="0.05"} 0`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="0.1"} 0`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="0.25"} 0`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="0.5"} 1`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="1"} 1`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="2.5"} 1`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="5"} 2`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="10"} 2`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="+Inf"} 2`, + `kooper_controller_event_in_queue_duration_seconds_count{controller="ctrl1"} 2`, + + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl2",le="0.005"} 0`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl2",le="0.01"} 0`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl2",le="0.025"} 1`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl2",le="0.05"} 2`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl2",le="0.1"} 2`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl2",le="0.25"} 2`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl2",le="0.5"} 2`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl2",le="1"} 3`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl2",le="2.5"} 3`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl2",le="5"} 3`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl2",le="10"} 4`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl2",le="+Inf"} 4`, + `kooper_controller_event_in_queue_duration_seconds_count{controller="ctrl2"} 4`, }, }, @@ -95,12 +95,12 @@ func TestPrometheusRecorder(t *testing.T) { }, expMetrics: []string{ - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="10"} 1`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="20"} 2`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="30"} 3`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="50"} 3`, - `kooper_controller_event_in_queue_duration_total_bucket{controller="ctrl1",le="+Inf"} 5`, - `kooper_controller_event_in_queue_duration_total_count{controller="ctrl1"} 5`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="10"} 1`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="20"} 2`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="30"} 3`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="50"} 3`, + `kooper_controller_event_in_queue_duration_seconds_bucket{controller="ctrl1",le="+Inf"} 5`, + `kooper_controller_event_in_queue_duration_seconds_count{controller="ctrl1"} 5`, }, }, From fa85290ac3683328aa76c949f1d544d86d160747 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Sun, 3 May 2020 10:44:05 +0200 Subject: [PATCH 21/31] Improve docs Signed-off-by: Xabier Larrakoetxea --- README.md | 17 +++++++------- examples/multi-resource-controller/main.go | 26 +++++++++++++--------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 626e6f86..eb576bbd 100644 --- a/README.md +++ b/README.md @@ -142,18 +142,19 @@ On the other hand if you want a more flexible clean up process (e.g clean from a ### Multiresource or secondary resources -Sometimes we have contorllers that work on a main or primary resource and we want to also handle the events of a secondary resource that is based on the first one. For example a deployment controller that watches the pods that belong to the deployment handled. +Sometimes we have controllers that work on a main or primary resource and we also want to handle the events of a secondary resource that is based on the first one. For example, a deployment controller that watches the pods that belong to the deployment handled. -After experiencing with controllers and implementing a way of creating multiresource controllers we though that is not necesary becase: +After using them and experiencing with controllers, we though that is not necesary becase: - Adds complexity. -- Adds corner cases and bugs, e.g - - Internal cache be based on IDs of `{namespace}/{name}` receiving a deletion watch event of one type removes the other type object with the same name from the cache - - Internal cache be based on IDs of `{namespace}/{name}`, the different resources that share name, only processsing one of the types. - - An error on one of the retrieval types stops all the controller process and not only the one based on that type. +- Adds corner cases, this translates in bugs, e.g + - Internal cache based on IDs of `{namespace}/{name}` scheme. + - Receiving a deletion watch event of one type removes the other type object with the same name from the cache + - The different resources that share name and ns, will only process one of the types (sometimes is useful, others adds bugs and corner cases). +- An error on one of the retrieval types stops all the controller process and not only the one based on that type. - The benefit of having this is to reuse the handler (we already can do this, a `Handler` is easy to reuse). -The solution to this embraces simplicity once again, is to create multiple controllers using the same `Handler` but with a different `ListerWatcher`, the `Handler` API is easy enough to reuse it across multiple controllers, check an [example][multiresource-example] of creating a multiresource controller. This has more benefits: +The solution to this problems embraces simplicity once again, and mainly is to create multiple controllers using the same `Handler` but with a different `ListerWatcher`, the `Handler` API is easy enough to reuse it across multiple controllers, check an [example][multiresource-example] of creating a multiresource controller(s). Also, this comes with extra benefits: - Different controller interval depending on the type (fast changing secondary objects can reconcile faster than the primary one, or viceversa). - Wrap the controller handler with a middlewre only for a particular type. @@ -189,4 +190,4 @@ The solution to this embraces simplicity once again, is to create multiple contr [owner-ref]: https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/#owners-and-dependents [finalizers]: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#finalizers [finalizer-example]: examples/pod-terminator-operator/operator/operator.go -[multiresource-example]: examples/pod-terminator-operator/multi-resource-controller \ No newline at end of file +[multiresource-example]: examples/multi-resource-controller \ No newline at end of file diff --git a/examples/multi-resource-controller/main.go b/examples/multi-resource-controller/main.go index 466b1b97..2def5151 100644 --- a/examples/multi-resource-controller/main.go +++ b/examples/multi-resource-controller/main.go @@ -61,6 +61,12 @@ func run() error { return nil }) + const ( + retries = 5 + resyncInterval = 45 * time.Second + workers = 1 + ) + // Create the controller for deployments. ctrlDep, err := controller.New(&controller.Config{ Name: "multi-resource-controller-deployments", @@ -75,11 +81,10 @@ func run() error { }, }, ), - Logger: logger, - - ProcessingJobRetries: 5, - ResyncInterval: 45 * time.Second, - ConcurrentWorkers: 1, + Logger: logger, + ProcessingJobRetries: retries, + ResyncInterval: resyncInterval, + ConcurrentWorkers: workers, }) if err != nil { return fmt.Errorf("could not create deployment resource controller: %w", err) @@ -99,11 +104,10 @@ func run() error { }, }, ), - Logger: logger, - - ProcessingJobRetries: 5, - ResyncInterval: 45 * time.Second, - ConcurrentWorkers: 1, + Logger: logger, + ProcessingJobRetries: retries, + ResyncInterval: resyncInterval, + ConcurrentWorkers: workers, }) // Start our controllers. @@ -118,7 +122,7 @@ func run() error { errC <- ctrlSt.Run(stopC) }() - // Wait unitl one finishes + // Wait until one finishes. err = <-errC if err != nil { return fmt.Errorf("error running controllers: %w", err) From bc19092b3a6a2a24c1108567f62a3c8b576b4b5d Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Sun, 3 May 2020 18:14:48 +0200 Subject: [PATCH 22/31] Add disabling resync on controllers Signed-off-by: Xabier Larrakoetxea --- CHANGELOG.md | 1 + README.md | 2 ++ controller/controller.go | 10 +++++++++- .../controller-concurrency-handling/main.go | 18 +++--------------- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 883530d6..a9a77ca7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ NOTE: Breaking release in controllers. - Refactor metrics recorder implementation including the prometheus backend. - Refactor internal controller queue into a decorator implementation approach. - Remove `Delete` method from `controller.Handler` and simplify to only `Handle` method +- Add `DisableResync` flag on controller configuration to disable the resync of all resources. ## [0.8.0] - 2019-12-11 diff --git a/README.md b/README.md index eb576bbd..b5557ca9 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,8 @@ The solution to this problems embraces simplicity once again, and mainly is to c - One of the type retrieval fails, the other type controller continues working (running in degradation mode). - Flexibility, e.g leader election for the primary type, no leader election for the secondary type. +Controller config has a handy flag to disable resync (`DisableResync`), sometimes this can be useful on secondary resources. + ## Compatibility matrix | | Kubernetes <=1.9 | Kubernetes 1.10 | Kubernetes 1.11 | Kubernetes 1.12 | Kubernetes 1.13 | Kubernetes 1.14 | diff --git a/controller/controller.go b/controller/controller.go index a28eb708..f6270954 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -53,6 +53,10 @@ type Config struct { ResyncInterval time.Duration // ProcessingJobRetries is the number of times the job will try to reprocess the event before returning a real error. ProcessingJobRetries int + // DisableResync will disable resyncing, if disabled the controller only will react on event updates and resync + // all when it runs for the first time. + // This is useful for secondary resource controllers (e.g pod controller of a primary controller based on deployments). + DisableResync bool } func (c *Config) setDefaults() error { @@ -90,6 +94,10 @@ func (c *Config) setDefaults() error { c.ResyncInterval = 3 * time.Minute } + if c.DisableResync { + c.ResyncInterval = 0 // 0 == resync disabled. + } + if c.ProcessingJobRetries < 0 { c.ProcessingJobRetries = 0 } @@ -149,7 +157,7 @@ func New(cfg *Config) (Controller, error) { informer := cache.NewSharedIndexInformer(lw, nil, cfg.ResyncInterval, store) // Set up our informer event handler. - // Objects are already in our local store. Add only keys/jobs on the queue so they can bre processed + // Objects are already in our local store. Add only keys/jobs on the queue so they can re processed // afterwards. informer.AddEventHandlerWithResyncPeriod(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { diff --git a/examples/controller-concurrency-handling/main.go b/examples/controller-concurrency-handling/main.go index 4abbdaeb..46c2b7dc 100644 --- a/examples/controller-concurrency-handling/main.go +++ b/examples/controller-concurrency-handling/main.go @@ -30,6 +30,7 @@ var ( sleepMS int intervalS int retries int + disableResync bool ) func initFlags() error { @@ -38,27 +39,13 @@ func initFlags() error { fg.IntVar(&sleepMS, "sleep-ms", 25, "The number of milliseconds to sleep on each event handling") fg.IntVar(&intervalS, "interval-s", 45, "The number of seconds to for reconciliation loop intervals") fg.IntVar(&retries, "retries", 3, "The number of retries in case of error") + fg.BoolVar(&disableResync, "disable-resync", false, "Disables the resync") err := fg.Parse(os.Args[1:]) if err != nil { return err } - if concurrentWorkers < 1 { - concurrentWorkers = 1 - } - - if sleepMS < 1 { - sleepMS = 25 - } - - if intervalS < 1 { - intervalS = 300 - } - if retries < 0 { - retries = 0 - } - return nil } @@ -119,6 +106,7 @@ func run() error { ProcessingJobRetries: retries, ResyncInterval: time.Duration(intervalS) * time.Second, ConcurrentWorkers: concurrentWorkers, + DisableResync: disableResync, } ctrl, err := controller.New(cfg) if err != nil { From b81ba1440b3a65ed6f16705d4483fcf93f886544 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Wed, 6 May 2020 10:49:07 +0200 Subject: [PATCH 23/31] Clean format and old code Signed-off-by: Xabier Larrakoetxea --- controller/controller_test.go | 38 ------------------------------ test/integration/helper/cli/cli.go | 1 - 2 files changed, 39 deletions(-) diff --git a/controller/controller_test.go b/controller/controller_test.go index fa1b7123..e307f1b5 100644 --- a/controller/controller_test.go +++ b/controller/controller_test.go @@ -23,12 +23,6 @@ import ( mcontroller "github.com/spotahome/kooper/mocks/controller" ) -// Namespace knows how to retrieve namespaces. -type namespaceRetriever struct { - lw cache.ListerWatcher - obj runtime.Object -} - // NewNamespace returns a Namespace retriever. func newNamespaceRetriever(client kubernetes.Interface) controller.Retriever { return controller.MustRetrieverFromListerWatcher(&cache.ListWatch{ @@ -41,38 +35,6 @@ func newNamespaceRetriever(client kubernetes.Interface) controller.Retriever { }) } -// GetListerWatcher knows how to retrieve Namespaces. -func (n *namespaceRetriever) GetListerWatcher() cache.ListerWatcher { - return n.lw -} - -// GetObject returns the namespace Object. -func (n *namespaceRetriever) GetObject() runtime.Object { - return n.obj -} - -func onKubeClientWatchNamespaceReturn(client *fake.Clientset, adds []*corev1.Namespace, updates []*corev1.Namespace, deletes []*corev1.Namespace) { - w := watch.NewFake() - client.AddWatchReactor("namespaces", func(action kubetesting.Action) (bool, watch.Interface, error) { - return true, w, nil - }) - - go func() { - // Adds. - for _, obj := range adds { - w.Add(obj) - } - // Updates. - for _, obj := range updates { - w.Modify(obj) - } - // Deletes. - for _, obj := range deletes { - w.Delete(obj) - } - }() -} - func onKubeClientListNamespaceReturn(client *fake.Clientset, nss *corev1.NamespaceList) { client.AddReactor("list", "namespaces", func(action kubetesting.Action) (bool, runtime.Object, error) { return true, nss, nil diff --git a/test/integration/helper/cli/cli.go b/test/integration/helper/cli/cli.go index a9171c6b..904350fb 100644 --- a/test/integration/helper/cli/cli.go +++ b/test/integration/helper/cli/cli.go @@ -11,7 +11,6 @@ import ( "k8s.io/client-go/util/homedir" ) - // GetK8sClient returns k8s client. func GetK8sClient(kubehome string) (kubernetes.Interface, error) { // Try fallbacks. From f3dfc95223c905eae399a4aaa046e947c2676a45 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Wed, 6 May 2020 10:49:30 +0200 Subject: [PATCH 24/31] Update readme Signed-off-by: Xabier Larrakoetxea --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b5557ca9..fb0ce263 100644 --- a/README.md +++ b/README.md @@ -134,17 +134,16 @@ Check [Leader election](docs/leader-election.md). ### Garbage collection -Kooper only handles the events of resources that exist that are triggered when these resources change or are created. There is no delete event, so in order to clean the resources you have 2 ways of doing these: +Kooper only handles the events of resources that exist, these are triggered when the resources being watched are updated or created. There is no delete event, so in order to clean the resources you have 2 ways of doing these: -If your controller creates as a side effect new Kubernetes resources you can use [owner references][owner-ref] on the created objects. - -On the other hand if you want a more flexible clean up process (e.g clean from a database or a 3rd party service) you can use [finalizers], check the [pod-terminator-operator][finalizer-example] example. +- If your controller creates as a side effect new Kubernetes resources you can use [owner references][owner-ref] on the created objects. +- If you want a more flexible clean up process (e.g clean from a database or a 3rd party service) you can use [finalizers], check the [pod-terminator-operator][finalizer-example] example. ### Multiresource or secondary resources -Sometimes we have controllers that work on a main or primary resource and we also want to handle the events of a secondary resource that is based on the first one. For example, a deployment controller that watches the pods that belong to the deployment handled. +Sometimes we have controllers that work on a main or primary resource and we also want to handle the events of a secondary resource that is based on the first one. For example, a deployment controller that watches the pods (secondary) that belong to the deployment (primary) handled. -After using them and experiencing with controllers, we though that is not necesary becase: +After using multiresource controllers/retrievers, we though that we don't need a multiresource controller is not necesary becase: - Adds complexity. - Adds corner cases, this translates in bugs, e.g @@ -154,7 +153,7 @@ After using them and experiencing with controllers, we though that is not necesa - An error on one of the retrieval types stops all the controller process and not only the one based on that type. - The benefit of having this is to reuse the handler (we already can do this, a `Handler` is easy to reuse). -The solution to this problems embraces simplicity once again, and mainly is to create multiple controllers using the same `Handler` but with a different `ListerWatcher`, the `Handler` API is easy enough to reuse it across multiple controllers, check an [example][multiresource-example] of creating a multiresource controller(s). Also, this comes with extra benefits: +The solution to these problems embrace simplicity once again, and mainly is creating multiple controllers using the same `Handler`, each controller with a different `ListerWatcher`. The `Handler` API is easy enough to reuse it across multiple controllers, check an [example][multiresource-example]. Also, this comes with extra benefits: - Different controller interval depending on the type (fast changing secondary objects can reconcile faster than the primary one, or viceversa). - Wrap the controller handler with a middlewre only for a particular type. From cd306a38eaa5907c49121576f04aaf1723e4862e Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Wed, 6 May 2020 11:09:24 +0200 Subject: [PATCH 25/31] Add code checks, improve test execution with race detection and clean build system Signed-off-by: Xabier Larrakoetxea --- .gitignore | 17 ++++--- .travis.yml | 2 +- Makefile | 90 ++++++++++------------------------- controller/controller_test.go | 9 ++++ docker/dev/Dockerfile | 34 ++++++++----- hack/scripts/check.sh | 6 +++ hack/scripts/unit-test.sh | 3 +- 7 files changed, 73 insertions(+), 88 deletions(-) create mode 100755 hack/scripts/check.sh diff --git a/.gitignore b/.gitignore index 0d16cc57..d5ae9b87 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,23 @@ -# Binaries for programs and plugins +# Binaries for programs and plugins. *.exe *.dll *.so *.dylib -# Test binary, build with `go test -c` +# Test binary, build with `go test -c`. *.test -# Output of the go coverage tool, specifically when used with LiteIDE +# Output of the go coverage tool, specifically when used with LiteIDE. *.out -# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736. .glide/ -# binary +# Binary. bin/ -# vendor -vendor/ \ No newline at end of file +# Vendor. +vendor/ + +# Test coverage. +.test_coverage.txt \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index ad5811b1..7db99990 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ before_install: - curl -Lo kubectl https://storage.googleapis.com/kubernetes-release/release/v${KUBERNETES_VERSION}/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/ script: - - make ci + - make check && make ci env: global: diff --git a/Makefile b/Makefile index cfe6aa59..007ee8a3 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,8 @@ - -# Name of this service/application SERVICE_NAME := kooper - -# Path of the go service inside docker -DOCKER_GO_SERVICE_PATH := /src - -# Shell to use for running scripts SHELL := $(shell which bash) - -# Get docker path or an empty string DOCKER := $(shell command -v docker) - -# Get the main unix group for the user running make (to be used by docker-compose later) +OSTYPE := $(shell uname) GID := $(shell id -g) - -# Get the unix user id for the user running make (to be used by docker-compose later) UID := $(shell id -u) # cmds @@ -22,81 +10,51 @@ UNIT_TEST_CMD := ./hack/scripts/unit-test.sh INTEGRATION_TEST_CMD := ./hack/scripts/integration-test.sh CI_INTEGRATION_TEST_CMD := ./hack/scripts/integration-test-kind.sh MOCKS_CMD := ./hack/scripts/mockgen.sh -DOCKER_RUN_CMD := docker run -v ${PWD}:$(DOCKER_GO_SERVICE_PATH) --rm -it $(SERVICE_NAME) -RUN_EXAMPLE_POD_ECHO := go run ./examples/echo-pod-controller/cmd/* --development -RUN_EXAMPLE_POD_ECHO_ONEFILE := go run ./examples/onefile-echo-pod-controller/main.go --development -RUN_EXAMPLE_POD_TERM := go run ./examples/pod-terminator-operator/cmd/* --development +DOCKER_RUN_CMD := docker run --env ostype=$(OSTYPE) -v ${PWD}:/src --rm -it ${SERVICE_NAME} DEPS_CMD := go mod tidy -K8S_VERSION := "1.15.10" -SET_K8S_DEPS_CMD := go mod edit \ - -require=k8s.io/apiextensions-apiserver@kubernetes-${K8S_VERSION} \ - -require=k8s.io/client-go@kubernetes-${K8S_VERSION} \ - -require=k8s.io/apimachinery@kubernetes-${K8S_VERSION} \ - -require=k8s.io/api@kubernetes-${K8S_VERSION} && \ - $(DEPS_CMD) +CHECK_CMD := ./hack/scripts/check.sh + -# environment dirs -DEV_DIR := docker/dev +help: ## Show this help + @echo "Help" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[93m %s\n", $$1, $$2}' -# The default action of this Makefile is to build the development docker image .PHONY: default -default: build +default: help -# Test if the dependencies we need to run this Makefile are installed -.PHONY: deps-development -deps-development: -ifndef DOCKER - @echo "Docker is not available. Please install docker" - @exit 1 -endif -# Build the development docker image .PHONY: build -build: +build: ## Build the development docker image. docker build -t $(SERVICE_NAME) --build-arg uid=$(UID) --build-arg gid=$(GID) -f ./docker/dev/Dockerfile . -# Dependency stuff. -.PHONY: set-k8s-deps -set-k8s-deps: - $(SET_K8S_DEPS_CMD) - .PHONY: deps -deps: +deps: ## Updates the required dependencies. $(DEPS_CMD) -# Test stuff in dev -.PHONY: unit-test -unit-test: build - $(DOCKER_RUN_CMD) /bin/sh -c '$(UNIT_TEST_CMD)' .PHONY: integration-test -integration-test: build +integration-test: build ## Runs integration tests out of CI. echo "[WARNING] Requires a kubernetes cluster configured (and running) on your kubeconfig!!" $(INTEGRATION_TEST_CMD) + .PHONY: test -test: unit-test +test: build ## Runs unit tests out of CI. + $(DOCKER_RUN_CMD) /bin/sh -c '$(UNIT_TEST_CMD)' + +.PHONY: check +check: build ## Runs checks. + @$(DOCKER_RUN_CMD) /bin/sh -c '$(CHECK_CMD)' -# Test stuff in ci .PHONY: ci-unit-test -ci-unit-test: +ci-unit-test: ## Runs unit tests in CI. $(UNIT_TEST_CMD) + .PHONY: ci-integration-test -ci-integration-test: +ci-integration-test: ## Runs integration tests in CI. $(CI_INTEGRATION_TEST_CMD) -.PHONY: ci + +.PHONY: ci ## Runs all tests in CI. ci: ci-unit-test ci-integration-test -# Mocks stuff in dev .PHONY: mocks -mocks: build +mocks: build ## Generates mocks. $(DOCKER_RUN_CMD) /bin/sh -c '$(MOCKS_CMD)' - -# Run examples. -.PHONY: controller-example -controller-example: - $(RUN_EXAMPLE_POD_ECHO) -.PHONY: controller-example-onefile -controller-example-onefile: - $(RUN_EXAMPLE_POD_ECHO_ONEFILE) -.PHONY: operator-example -operator-example: - $(RUN_EXAMPLE_POD_TERM) diff --git a/controller/controller_test.go b/controller/controller_test.go index e307f1b5..a87265ae 100644 --- a/controller/controller_test.go +++ b/controller/controller_test.go @@ -2,6 +2,7 @@ package controller_test import ( "fmt" + "sync" "testing" "time" @@ -95,9 +96,14 @@ func TestGenericControllerHandle(t *testing.T) { // Mock our handler and set expects. callHandling := 0 // used to track the number of calls. mh := &mcontroller.Handler{} + + var mu sync.Mutex for _, ns := range test.expNSAdds { mh.On("Handle", mock.Anything, ns).Once().Return(nil).Run(func(args mock.Arguments) { + mu.Lock() + defer mu.Unlock() callHandling++ + // Check last call, if is the last call expected then stop the controller so // we can assert the expectations of the calls and finish the test. if callHandling == len(test.expNSAdds) { @@ -167,9 +173,12 @@ func TestGenericControllerErrorRetries(t *testing.T) { err := fmt.Errorf("wanted error") // Expect all the retries + var mu sync.Mutex for range test.nsList.Items { callsPerNS := test.retryNumber + 1 // initial call + retries. mh.On("Handle", mock.Anything, mock.Anything).Return(err).Times(callsPerNS).Run(func(args mock.Arguments) { + mu.Lock() + defer mu.Unlock() totalCalls-- // Check last call, if is the last call expected then stop the controller so // we can assert the expectations of the calls and finish the test. diff --git a/docker/dev/Dockerfile b/docker/dev/Dockerfile index fd13e18d..90d4ef36 100644 --- a/docker/dev/Dockerfile +++ b/docker/dev/Dockerfile @@ -1,27 +1,35 @@ -FROM golang:1.13-alpine +FROM golang:1.14 -RUN apk --no-cache add \ - g++ \ - git +ARG GOLANGCI_LINT_VERSION="1.25.0" +ARG ostype=Linux -# Mock creator -RUN go get -u github.com/vektra/mockery/.../ +RUN apt-get update && apt-get install -y \ + git \ + bash \ + zip -# Create user + +RUN wget https://github.com/golangci/golangci-lint/releases/download/v${GOLANGCI_LINT_VERSION}/golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz && \ + tar zxvf golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz --strip 1 -C /usr/local/bin/ && \ + rm golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz + +RUN go get -u github.com/vektra/mockery/... + +# Create user. ARG uid=1000 ARG gid=1000 -RUN addgroup -g $gid kooper && \ - adduser -D -u $uid -G kooper kooper +RUN bash -c 'if [ ${ostype} == Linux ]; then addgroup -gid $gid app; else addgroup app; fi && \ + adduser --disabled-password -uid $uid --ingroup app --gecos "" app && \ + chown app:app -R /go' # Fill go mod cache. RUN mkdir /tmp/cache COPY go.mod /tmp/cache COPY go.sum /tmp/cache +RUN chown app:app -R /tmp/cache +USER app RUN cd /tmp/cache && \ go mod download -RUN chown kooper:kooper -R /go -USER kooper - -WORKDIR /src \ No newline at end of file +WORKDIR /src diff --git a/hack/scripts/check.sh b/hack/scripts/check.sh new file mode 100755 index 00000000..532753e8 --- /dev/null +++ b/hack/scripts/check.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env sh + +set -o errexit +set -o nounset + +golangci-lint run -E goimports --timeout 3m \ No newline at end of file diff --git a/hack/scripts/unit-test.sh b/hack/scripts/unit-test.sh index fc016643..b3b3a088 100755 --- a/hack/scripts/unit-test.sh +++ b/hack/scripts/unit-test.sh @@ -3,4 +3,5 @@ set -o errexit set -o nounset -go test `go list ./... | grep -v vendor` -v \ No newline at end of file +go test -race -coverprofile=.test_coverage.txt ./... +go tool cover -func=.test_coverage.txt | tail -n1 | awk '{print "Total test coverage: " $3}' \ No newline at end of file From f9797ce4d2b6c706578435060e9bf3dc5ec5fd4a Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Wed, 6 May 2020 11:42:05 +0200 Subject: [PATCH 26/31] Migrate from Travis to Github actions Signed-off-by: Xabier Larrakoetxea --- .github/workflows/ci.yaml | 41 +++++++++++++++++++++++++++++++++++++++ .travis.yml | 26 ------------------------- 2 files changed, 41 insertions(+), 26 deletions(-) create mode 100644 .github/workflows/ci.yaml delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000..260dc281 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,41 @@ +name: CI + +on: [push, pull_request] + +jobs: + check: + name: Check + runs-on: ubuntu-latest + # Execute the checks inside the container instead the VM. + container: golangci/golangci-lint:v1.24.0-alpine + steps: + - uses: actions/checkout@v1 + - run: ./hack/scripts/check.sh + + unit-test: + name: Unit test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-go@v2-beta + with: + go-version: 1.14 + - run: make ci-unit-test + + integration-test: + name: Integration test + runs-on: ubuntu-latest + needs: [check, unit-test] + strategy: + matrix: + kubernetes: [1.15.7, 1.16.4, 1.17.0, 1.18.0] + env: + KIND_VERSION: v0.7.0 + steps: + - uses: actions/checkout@v1 + - run: curl -Lo kind https://github.com/kubernetes-sigs/kind/releases/download/${KIND_VERSION}/kind-linux-amd64 && chmod +x kind && sudo mv kind /usr/local/bin/ + - run: curl -Lo kubectl https://storage.googleapis.com/kubernetes-release/release/v${{ matrix.kubernetes }}/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/ + - run: KUBERNETES_VERSION=${{ matrix.kubernetes }} make ci-integration-test + + + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7db99990..00000000 --- a/.travis.yml +++ /dev/null @@ -1,26 +0,0 @@ -sudo: required -dist: trusty -services: - - docker - -language: go -go: - - "1.14" - -before_install: - - curl -Lo kind https://github.com/kubernetes-sigs/kind/releases/download/${KIND_VERSION}/kind-linux-amd64 && chmod +x kind && sudo mv kind /usr/local/bin/ - - curl -Lo kubectl https://storage.googleapis.com/kubernetes-release/release/v${KUBERNETES_VERSION}/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/ - -script: - - make check && make ci - -env: - global: - - KIND_VERSION=v0.7.0 - -matrix: - include: - - env: KUBERNETES_VERSION=1.15.7 - - env: KUBERNETES_VERSION=1.16.4 - - env: KUBERNETES_VERSION=1.17.0 - - env: KUBERNETES_VERSION=1.18.0 \ No newline at end of file From 91d3c21e1cc3371068b7bdaf0d91303cc4cc7c20 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Wed, 6 May 2020 13:04:14 +0200 Subject: [PATCH 27/31] Remove compatibility matrix from readme Signed-off-by: Xabier Larrakoetxea --- README.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/README.md b/README.md index fb0ce263..93fa6a07 100644 --- a/README.md +++ b/README.md @@ -162,17 +162,6 @@ The solution to these problems embrace simplicity once again, and mainly is crea Controller config has a handy flag to disable resync (`DisableResync`), sometimes this can be useful on secondary resources. -## Compatibility matrix - -| | Kubernetes <=1.9 | Kubernetes 1.10 | Kubernetes 1.11 | Kubernetes 1.12 | Kubernetes 1.13 | Kubernetes 1.14 | -| ----------- | ---------------- | --------------- | --------------- | --------------- | --------------- | --------------- | -| kooper 0.1 | ✓ | ? | ? | ? | ? | ? | -| kooper 0.2 | ✓ | ? | ? | ? | ? | ? | -| kooper 0.3 | ?+ | ✓ | ? | ? | ? | ? | -| kooper 0.4 | ?+ | ✓ | ? | ? | ? | ? | -| kooper 0.5 | ? | ?+ | ✓ | ? | ? | ? | -| kooper 0.6 | ? | ? | ?+ | ✓ | ? | ? | -| kooper HEAD | ? | ? | ? | ?+ | ✓? | ? | [travis-image]: https://travis-ci.org/spotahome/kooper.svg?branch=master [travis-url]: https://travis-ci.org/spotahome/kooper From 3ceda9e921d405a118fa78a428ddf7d659abfd69 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Thu, 9 Jul 2020 19:26:47 +0200 Subject: [PATCH 28/31] Get ready for Kooper v2 Signed-off-by: Xabier Larrakoetxea --- controller/controller.go | 4 ++-- controller/controller_test.go | 8 +++---- controller/doc.go | 2 +- controller/leaderelection/leaderelection.go | 2 +- controller/processor.go | 2 +- controller/queue.go | 2 +- controller/retrieve_test.go | 2 +- docs/leader-election.md | 21 +++++++++---------- examples/config-custom-controller/main.go | 6 +++--- .../controller-concurrency-handling/main.go | 6 +++--- examples/leader-election-controller/main.go | 8 +++---- examples/metrics-controller/main.go | 8 +++---- examples/multi-resource-controller/main.go | 6 +++--- .../apis/chaos/v1alpha1/register.go | 2 +- .../k8s/clientset/versioned/clientset.go | 2 +- .../versioned/fake/clientset_generated.go | 6 +++--- .../k8s/clientset/versioned/fake/register.go | 2 +- .../clientset/versioned/scheme/register.go | 2 +- .../typed/chaos/v1alpha1/chaos_client.go | 4 ++-- .../chaos/v1alpha1/fake/fake_chaos_client.go | 2 +- .../chaos/v1alpha1/fake/fake_podterminator.go | 2 +- .../typed/chaos/v1alpha1/podterminator.go | 4 ++-- examples/pod-terminator-operator/cmd/flags.go | 2 +- examples/pod-terminator-operator/cmd/main.go | 10 ++++----- examples/pod-terminator-operator/log/log.go | 2 +- .../operator/operator.go | 10 ++++----- .../service/chaos/chaos.go | 4 ++-- .../service/chaos/podkill.go | 4 ++-- .../service/chaos/podkill_test.go | 6 +++--- go.mod | 2 +- log/logrus/logrus.go | 2 +- metrics/prometheus/prometheus.go | 2 +- metrics/prometheus/prometheus_test.go | 2 +- mocks/doc.go | 2 +- .../integration/controller/controller_test.go | 8 +++---- test/integration/controller/generic_test.go | 4 ++-- 36 files changed, 81 insertions(+), 82 deletions(-) diff --git a/controller/controller.go b/controller/controller.go index f6270954..36254f46 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -14,8 +14,8 @@ import ( "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" - "github.com/spotahome/kooper/controller/leaderelection" - "github.com/spotahome/kooper/log" + "github.com/spotahome/kooper/v2/controller/leaderelection" + "github.com/spotahome/kooper/v2/log" ) var ( diff --git a/controller/controller_test.go b/controller/controller_test.go index a87265ae..16c8f427 100644 --- a/controller/controller_test.go +++ b/controller/controller_test.go @@ -18,10 +18,10 @@ import ( kubetesting "k8s.io/client-go/testing" "k8s.io/client-go/tools/cache" - "github.com/spotahome/kooper/controller" - "github.com/spotahome/kooper/controller/leaderelection" - "github.com/spotahome/kooper/log" - mcontroller "github.com/spotahome/kooper/mocks/controller" + "github.com/spotahome/kooper/v2/controller" + "github.com/spotahome/kooper/v2/controller/leaderelection" + "github.com/spotahome/kooper/v2/log" + mcontroller "github.com/spotahome/kooper/v2/mocks/controller" ) // NewNamespace returns a Namespace retriever. diff --git a/controller/doc.go b/controller/doc.go index 97c6ef45..ebcdb79a 100644 --- a/controller/doc.go +++ b/controller/doc.go @@ -1,2 +1,2 @@ // Package controller contains implementation and defition to create kubernetes controllers. -package controller // import "github.com/spotahome/kooper/operator/controller" +package controller // import "github.com/spotahome/kooper/v2/operator/controller" diff --git a/controller/leaderelection/leaderelection.go b/controller/leaderelection/leaderelection.go index 9dd0deba..61f00b70 100644 --- a/controller/leaderelection/leaderelection.go +++ b/controller/leaderelection/leaderelection.go @@ -14,7 +14,7 @@ import ( "k8s.io/client-go/tools/leaderelection/resourcelock" "k8s.io/client-go/tools/record" - "github.com/spotahome/kooper/log" + "github.com/spotahome/kooper/v2/log" ) const ( diff --git a/controller/processor.go b/controller/processor.go index 9910bf7f..390bfebd 100644 --- a/controller/processor.go +++ b/controller/processor.go @@ -8,7 +8,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/cache" - "github.com/spotahome/kooper/log" + "github.com/spotahome/kooper/v2/log" ) // processor knows how to process object keys. diff --git a/controller/queue.go b/controller/queue.go index a095839b..27d7005b 100644 --- a/controller/queue.go +++ b/controller/queue.go @@ -8,7 +8,7 @@ import ( "k8s.io/client-go/util/workqueue" - "github.com/spotahome/kooper/log" + "github.com/spotahome/kooper/v2/log" ) // blockingQueue is a queue that any of its implementations should diff --git a/controller/retrieve_test.go b/controller/retrieve_test.go index d7dafee2..56d8e877 100644 --- a/controller/retrieve_test.go +++ b/controller/retrieve_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/spotahome/kooper/controller" + "github.com/spotahome/kooper/v2/controller" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/docs/leader-election.md b/docs/leader-election.md index 483dd2cf..86181295 100644 --- a/docs/leader-election.md +++ b/docs/leader-election.md @@ -8,12 +8,12 @@ A controller can be run with multiple instances in HA and only one will be reall The default controllers don't run in leader election mode: -* `controller.NewSequential` -* `controller.NewConcurrent` +- `controller.NewSequential` +- `controller.NewConcurrent` To use the leader election you can use: -* `controller.New` +- `controller.New` These method accepts a [`leaderelection.Runner`][leaderelection-src] service that manages the leader election of the controller, if this service is a `nil` object the controller will fallback to a regular controller mode. @@ -24,7 +24,7 @@ Lets take and example of creating using the default leader election service. ```golang import ( ... - "github.com/spotahome/kooper/operator/controller/leaderelection" + "github.com/spotahome/kooper/v2/operator/controller/leaderelection" ... ) @@ -40,7 +40,7 @@ Another example customizing the lock would be: ```golang import ( ... - "github.com/spotahome/kooper/operator/controller/leaderelection" + "github.com/spotahome/kooper/v2/operator/controller/leaderelection" ... ) @@ -109,11 +109,10 @@ docker run --name ctrl2 \ Now you can test disconnecting and connecting them using these commands and checking the results. -* `docker network disconnect bridge ctrl2` -* `docker network disconnect bridge ctrl1` -* `docker network connect bridge ctrl2` -* `docker network connect bridge ctrl1` - +- `docker network disconnect bridge ctrl2` +- `docker network disconnect bridge ctrl1` +- `docker network connect bridge ctrl2` +- `docker network connect bridge ctrl1` [leaderelection-src]: https://github.com/spotahome/kooper/tree/master/operator/controller/leaderelection -[leaderelection-example]: https://github.com/spotahome/kooper/tree/master/examples/leader-election-controller \ No newline at end of file +[leaderelection-example]: https://github.com/spotahome/kooper/tree/master/examples/leader-election-controller diff --git a/examples/config-custom-controller/main.go b/examples/config-custom-controller/main.go index 3d7b7723..62c73e68 100644 --- a/examples/config-custom-controller/main.go +++ b/examples/config-custom-controller/main.go @@ -19,9 +19,9 @@ import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" - "github.com/spotahome/kooper/controller" - "github.com/spotahome/kooper/log" - kooperlogrus "github.com/spotahome/kooper/log/logrus" + "github.com/spotahome/kooper/v2/controller" + "github.com/spotahome/kooper/v2/log" + kooperlogrus "github.com/spotahome/kooper/v2/log/logrus" ) func run() error { diff --git a/examples/controller-concurrency-handling/main.go b/examples/controller-concurrency-handling/main.go index 46c2b7dc..e7129f64 100644 --- a/examples/controller-concurrency-handling/main.go +++ b/examples/controller-concurrency-handling/main.go @@ -20,9 +20,9 @@ import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" - "github.com/spotahome/kooper/controller" - "github.com/spotahome/kooper/log" - kooperlogrus "github.com/spotahome/kooper/log/logrus" + "github.com/spotahome/kooper/v2/controller" + "github.com/spotahome/kooper/v2/log" + kooperlogrus "github.com/spotahome/kooper/v2/log/logrus" ) var ( diff --git a/examples/leader-election-controller/main.go b/examples/leader-election-controller/main.go index 71a207e3..799720a1 100644 --- a/examples/leader-election-controller/main.go +++ b/examples/leader-election-controller/main.go @@ -22,10 +22,10 @@ import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" - "github.com/spotahome/kooper/controller" - "github.com/spotahome/kooper/controller/leaderelection" - "github.com/spotahome/kooper/log" - kooperlogrus "github.com/spotahome/kooper/log/logrus" + "github.com/spotahome/kooper/v2/controller" + "github.com/spotahome/kooper/v2/controller/leaderelection" + "github.com/spotahome/kooper/v2/log" + kooperlogrus "github.com/spotahome/kooper/v2/log/logrus" ) const ( diff --git a/examples/metrics-controller/main.go b/examples/metrics-controller/main.go index 68b391ee..650a89aa 100644 --- a/examples/metrics-controller/main.go +++ b/examples/metrics-controller/main.go @@ -23,10 +23,10 @@ import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" - "github.com/spotahome/kooper/controller" - "github.com/spotahome/kooper/log" - kooperlogrus "github.com/spotahome/kooper/log/logrus" - kooperprometheus "github.com/spotahome/kooper/metrics/prometheus" + "github.com/spotahome/kooper/v2/controller" + "github.com/spotahome/kooper/v2/log" + kooperlogrus "github.com/spotahome/kooper/v2/log/logrus" + kooperprometheus "github.com/spotahome/kooper/v2/metrics/prometheus" ) const ( diff --git a/examples/multi-resource-controller/main.go b/examples/multi-resource-controller/main.go index 2def5151..7880bd68 100644 --- a/examples/multi-resource-controller/main.go +++ b/examples/multi-resource-controller/main.go @@ -19,9 +19,9 @@ import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" - "github.com/spotahome/kooper/controller" - "github.com/spotahome/kooper/log" - kooperlogrus "github.com/spotahome/kooper/log/logrus" + "github.com/spotahome/kooper/v2/controller" + "github.com/spotahome/kooper/v2/log" + kooperlogrus "github.com/spotahome/kooper/v2/log/logrus" ) func run() error { diff --git a/examples/pod-terminator-operator/apis/chaos/v1alpha1/register.go b/examples/pod-terminator-operator/apis/chaos/v1alpha1/register.go index baa763ac..88cdb68b 100644 --- a/examples/pod-terminator-operator/apis/chaos/v1alpha1/register.go +++ b/examples/pod-terminator-operator/apis/chaos/v1alpha1/register.go @@ -6,7 +6,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - podterminatoroperatorchaos "github.com/spotahome/kooper/examples/pod-terminator-operator/apis/chaos" + podterminatoroperatorchaos "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/apis/chaos" ) const ( diff --git a/examples/pod-terminator-operator/client/k8s/clientset/versioned/clientset.go b/examples/pod-terminator-operator/client/k8s/clientset/versioned/clientset.go index cb16b2e5..fc79d7ea 100644 --- a/examples/pod-terminator-operator/client/k8s/clientset/versioned/clientset.go +++ b/examples/pod-terminator-operator/client/k8s/clientset/versioned/clientset.go @@ -21,7 +21,7 @@ package versioned import ( "fmt" - chaosv1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1" + chaosv1alpha1 "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1" discovery "k8s.io/client-go/discovery" rest "k8s.io/client-go/rest" flowcontrol "k8s.io/client-go/util/flowcontrol" diff --git a/examples/pod-terminator-operator/client/k8s/clientset/versioned/fake/clientset_generated.go b/examples/pod-terminator-operator/client/k8s/clientset/versioned/fake/clientset_generated.go index 683fd7ac..07e84df9 100644 --- a/examples/pod-terminator-operator/client/k8s/clientset/versioned/fake/clientset_generated.go +++ b/examples/pod-terminator-operator/client/k8s/clientset/versioned/fake/clientset_generated.go @@ -19,9 +19,9 @@ limitations under the License. package fake import ( - clientset "github.com/spotahome/kooper/examples/pod-terminator-operator/client/k8s/clientset/versioned" - chaosv1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1" - fakechaosv1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/fake" + clientset "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/client/k8s/clientset/versioned" + chaosv1alpha1 "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1" + fakechaosv1alpha1 "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/fake" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/discovery" diff --git a/examples/pod-terminator-operator/client/k8s/clientset/versioned/fake/register.go b/examples/pod-terminator-operator/client/k8s/clientset/versioned/fake/register.go index 53afb58c..1f734d51 100644 --- a/examples/pod-terminator-operator/client/k8s/clientset/versioned/fake/register.go +++ b/examples/pod-terminator-operator/client/k8s/clientset/versioned/fake/register.go @@ -19,7 +19,7 @@ limitations under the License. package fake import ( - chaosv1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/apis/chaos/v1alpha1" + chaosv1alpha1 "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/apis/chaos/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" diff --git a/examples/pod-terminator-operator/client/k8s/clientset/versioned/scheme/register.go b/examples/pod-terminator-operator/client/k8s/clientset/versioned/scheme/register.go index 517200c1..9b2c3aab 100644 --- a/examples/pod-terminator-operator/client/k8s/clientset/versioned/scheme/register.go +++ b/examples/pod-terminator-operator/client/k8s/clientset/versioned/scheme/register.go @@ -19,7 +19,7 @@ limitations under the License. package scheme import ( - chaosv1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/apis/chaos/v1alpha1" + chaosv1alpha1 "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/apis/chaos/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" diff --git a/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/chaos_client.go b/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/chaos_client.go index 35a10b8c..d593bca6 100644 --- a/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/chaos_client.go +++ b/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/chaos_client.go @@ -19,8 +19,8 @@ limitations under the License. package v1alpha1 import ( - v1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/apis/chaos/v1alpha1" - "github.com/spotahome/kooper/examples/pod-terminator-operator/client/k8s/clientset/versioned/scheme" + v1alpha1 "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/apis/chaos/v1alpha1" + "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/client/k8s/clientset/versioned/scheme" rest "k8s.io/client-go/rest" ) diff --git a/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/fake/fake_chaos_client.go b/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/fake/fake_chaos_client.go index 0472433c..051cfa03 100644 --- a/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/fake/fake_chaos_client.go +++ b/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/fake/fake_chaos_client.go @@ -19,7 +19,7 @@ limitations under the License. package fake import ( - v1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1" + v1alpha1 "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1" rest "k8s.io/client-go/rest" testing "k8s.io/client-go/testing" ) diff --git a/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/fake/fake_podterminator.go b/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/fake/fake_podterminator.go index 79158494..b6f29f86 100644 --- a/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/fake/fake_podterminator.go +++ b/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/fake/fake_podterminator.go @@ -19,7 +19,7 @@ limitations under the License. package fake import ( - v1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/apis/chaos/v1alpha1" + v1alpha1 "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/apis/chaos/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" labels "k8s.io/apimachinery/pkg/labels" schema "k8s.io/apimachinery/pkg/runtime/schema" diff --git a/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/podterminator.go b/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/podterminator.go index 5bfa00f7..7c866b3f 100644 --- a/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/podterminator.go +++ b/examples/pod-terminator-operator/client/k8s/clientset/versioned/typed/chaos/v1alpha1/podterminator.go @@ -21,8 +21,8 @@ package v1alpha1 import ( "time" - v1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/apis/chaos/v1alpha1" - scheme "github.com/spotahome/kooper/examples/pod-terminator-operator/client/k8s/clientset/versioned/scheme" + v1alpha1 "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/apis/chaos/v1alpha1" + scheme "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/client/k8s/clientset/versioned/scheme" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" diff --git a/examples/pod-terminator-operator/cmd/flags.go b/examples/pod-terminator-operator/cmd/flags.go index 523e1513..5ce61f9b 100644 --- a/examples/pod-terminator-operator/cmd/flags.go +++ b/examples/pod-terminator-operator/cmd/flags.go @@ -8,7 +8,7 @@ import ( "k8s.io/client-go/util/homedir" - "github.com/spotahome/kooper/examples/pod-terminator-operator/operator" + "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/operator" ) // Flags are the controller flags. diff --git a/examples/pod-terminator-operator/cmd/main.go b/examples/pod-terminator-operator/cmd/main.go index a98a3eb8..c7cc5dad 100644 --- a/examples/pod-terminator-operator/cmd/main.go +++ b/examples/pod-terminator-operator/cmd/main.go @@ -13,11 +13,11 @@ import ( "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" - podtermk8scli "github.com/spotahome/kooper/examples/pod-terminator-operator/client/k8s/clientset/versioned" - "github.com/spotahome/kooper/examples/pod-terminator-operator/log" - "github.com/spotahome/kooper/examples/pod-terminator-operator/operator" - kooperlog "github.com/spotahome/kooper/log" - kooperlogrus "github.com/spotahome/kooper/log/logrus" + podtermk8scli "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/client/k8s/clientset/versioned" + "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/log" + "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/operator" + kooperlog "github.com/spotahome/kooper/v2/log" + kooperlogrus "github.com/spotahome/kooper/v2/log/logrus" ) // Main is the main program. diff --git a/examples/pod-terminator-operator/log/log.go b/examples/pod-terminator-operator/log/log.go index a52f92e2..8d7e1e8f 100644 --- a/examples/pod-terminator-operator/log/log.go +++ b/examples/pod-terminator-operator/log/log.go @@ -1,7 +1,7 @@ package log import ( - "github.com/spotahome/kooper/log" + "github.com/spotahome/kooper/v2/log" ) // Logger is the interface of the operator logger. This is an example diff --git a/examples/pod-terminator-operator/operator/operator.go b/examples/pod-terminator-operator/operator/operator.go index 88e53e92..d9bd2ad6 100644 --- a/examples/pod-terminator-operator/operator/operator.go +++ b/examples/pod-terminator-operator/operator/operator.go @@ -5,17 +5,17 @@ import ( "fmt" "time" - "github.com/spotahome/kooper/controller" + "github.com/spotahome/kooper/v2/controller" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" - chaosv1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/apis/chaos/v1alpha1" - podtermk8scli "github.com/spotahome/kooper/examples/pod-terminator-operator/client/k8s/clientset/versioned" - "github.com/spotahome/kooper/examples/pod-terminator-operator/log" - "github.com/spotahome/kooper/examples/pod-terminator-operator/service/chaos" + chaosv1alpha1 "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/apis/chaos/v1alpha1" + podtermk8scli "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/client/k8s/clientset/versioned" + "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/log" + "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/service/chaos" ) // Config is the controller configuration. diff --git a/examples/pod-terminator-operator/service/chaos/chaos.go b/examples/pod-terminator-operator/service/chaos/chaos.go index 02971c8f..ac7534f9 100644 --- a/examples/pod-terminator-operator/service/chaos/chaos.go +++ b/examples/pod-terminator-operator/service/chaos/chaos.go @@ -5,8 +5,8 @@ import ( "k8s.io/client-go/kubernetes" - chaosv1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/apis/chaos/v1alpha1" - "github.com/spotahome/kooper/examples/pod-terminator-operator/log" + chaosv1alpha1 "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/apis/chaos/v1alpha1" + "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/log" ) // Syncer is the interface that every chaos service implementation diff --git a/examples/pod-terminator-operator/service/chaos/podkill.go b/examples/pod-terminator-operator/service/chaos/podkill.go index 29382ef9..69783161 100644 --- a/examples/pod-terminator-operator/service/chaos/podkill.go +++ b/examples/pod-terminator-operator/service/chaos/podkill.go @@ -13,8 +13,8 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/kubernetes" - chaosv1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/apis/chaos/v1alpha1" - "github.com/spotahome/kooper/examples/pod-terminator-operator/log" + chaosv1alpha1 "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/apis/chaos/v1alpha1" + "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/log" ) // TimeWrapper is a wrapper around time so it can be mocked diff --git a/examples/pod-terminator-operator/service/chaos/podkill_test.go b/examples/pod-terminator-operator/service/chaos/podkill_test.go index 2cc95a99..8d777614 100644 --- a/examples/pod-terminator-operator/service/chaos/podkill_test.go +++ b/examples/pod-terminator-operator/service/chaos/podkill_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/spotahome/kooper/log" + "github.com/spotahome/kooper/v2/log" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -13,8 +13,8 @@ import ( "k8s.io/client-go/kubernetes/fake" kubetesting "k8s.io/client-go/testing" - chaosv1alpha1 "github.com/spotahome/kooper/examples/pod-terminator-operator/apis/chaos/v1alpha1" - "github.com/spotahome/kooper/examples/pod-terminator-operator/service/chaos" + chaosv1alpha1 "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/apis/chaos/v1alpha1" + "github.com/spotahome/kooper/v2/examples/pod-terminator-operator/service/chaos" ) type timeMock struct { diff --git a/go.mod b/go.mod index ab72b206..0a8370b0 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/spotahome/kooper +module github.com/spotahome/kooper/v2 require ( github.com/prometheus/client_golang v1.6.0 diff --git a/log/logrus/logrus.go b/log/logrus/logrus.go index ec746c05..4209a8d8 100644 --- a/log/logrus/logrus.go +++ b/log/logrus/logrus.go @@ -3,7 +3,7 @@ package logrus import ( "github.com/sirupsen/logrus" - "github.com/spotahome/kooper/log" + "github.com/spotahome/kooper/v2/log" ) type logger struct { diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index 9816044b..6f659938 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -6,7 +6,7 @@ import ( "time" "github.com/prometheus/client_golang/prometheus" - "github.com/spotahome/kooper/controller" + "github.com/spotahome/kooper/v2/controller" ) const ( diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go index 604462f2..0fbbb2e0 100644 --- a/metrics/prometheus/prometheus_test.go +++ b/metrics/prometheus/prometheus_test.go @@ -11,7 +11,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/stretchr/testify/assert" - kooperprometheus "github.com/spotahome/kooper/metrics/prometheus" + kooperprometheus "github.com/spotahome/kooper/v2/metrics/prometheus" ) func TestPrometheusRecorder(t *testing.T) { diff --git a/mocks/doc.go b/mocks/doc.go index 7930419a..fab02d31 100644 --- a/mocks/doc.go +++ b/mocks/doc.go @@ -2,7 +2,7 @@ Package mocks will have all the mocks of the library, we'll try to use mocking using blackbox testing and integration tests whenever is possible. */ -package mocks // import "github.com/spotahome/kooper/mocks" +package mocks // import "github.com/spotahome/kooper/v2/mocks" // Operator tooling mocks. //go:generate mockery -output ./controller -outpkg controller -dir ../controller -name Controller diff --git a/test/integration/controller/controller_test.go b/test/integration/controller/controller_test.go index 8164875e..b1dbc699 100644 --- a/test/integration/controller/controller_test.go +++ b/test/integration/controller/controller_test.go @@ -16,10 +16,10 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/cache" - "github.com/spotahome/kooper/controller" - "github.com/spotahome/kooper/log" - "github.com/spotahome/kooper/test/integration/helper/cli" - "github.com/spotahome/kooper/test/integration/helper/prepare" + "github.com/spotahome/kooper/v2/controller" + "github.com/spotahome/kooper/v2/log" + "github.com/spotahome/kooper/v2/test/integration/helper/cli" + "github.com/spotahome/kooper/v2/test/integration/helper/prepare" ) // TestControllerHandleEvents will test the controller receives the resources list and watch diff --git a/test/integration/controller/generic_test.go b/test/integration/controller/generic_test.go index 0f9c1be7..b8072a39 100644 --- a/test/integration/controller/generic_test.go +++ b/test/integration/controller/generic_test.go @@ -16,8 +16,8 @@ import ( "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/tools/cache" - "github.com/spotahome/kooper/controller" - "github.com/spotahome/kooper/log" + "github.com/spotahome/kooper/v2/controller" + "github.com/spotahome/kooper/v2/log" ) const ( From b686659a0dea597339115e3af31fa2da26cd0e06 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Fri, 10 Jul 2020 17:50:13 +0200 Subject: [PATCH 29/31] Update Readme for v2 Signed-off-by: Xabier Larrakoetxea --- README.md | 55 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 93fa6a07..b7e1b7f8 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ Kooper is a Go library to create simple and flexible [controllers]/operators, in In other words, is a small alternative to big frameworks like [Kubebuilder] or [operator-framework]. +**Library refactored (`v2`), for `v2` use `import "github.com/spotahome/kooper/v2"`** + ## Features - Easy usage and fast to get it working. @@ -13,6 +15,25 @@ In other words, is a small alternative to big frameworks like [Kubebuilder] or [ - Ready for core Kubernetes resources (pods, ingress, deployments...) and CRDs. - Optional leader election system for controllers. +## V0 vs V2 + +First of all, we used `v2` instead of `v[01]`, because it changes the library as a whole, theres no backwards compatibility, +`v0` is stable and used in production, although you eventually will want to update to `v2` becasuse `v0` will not be updated. + +Import with: + +```golang +import "github.com/spotahome/kooper/v2" +``` + +Regarding the changes... To know all of them check the changelog but mainly we simplified everything. The +most relevant changes you will need to be aware and could impact are: + +- Before there were operators and controllers, now only controllers (one single concept). +- Now CRD management in the library, CRDs now should be already registered when the controller starts (you can use [this][kube-code-generator] to generate these manifests). +- Refactor Prometheus metrics, so you will need to change dashboards/alerts. +- `Delete` event removed because wasn't reliable (Check `Garbage-collection` section). + ## Getting started The simplest example that prints pods would be this: @@ -51,6 +72,10 @@ The simplest example that prints pods would be this: ctrl.Run(make(chan struct{})) ``` +## Kubernetes version compatibility + +Kooper at this moment uses as base `v1.17`. But [check the integration test in CI][ci] to know the supported versions. + ## When should I use Kooper? ### Alternatives @@ -84,9 +109,9 @@ Kooper instead solves most of the core controller/operator problems but as a sim For example Kubebuilder sacrifies API simplicity/client in favor of aggresive cache usage. Kooper instead embraces simplicity over optimization: -- Most of the controller/operators don't need this kind of optimization. Adding complexity on all controllers for a small optimized controller use cases doesn't make sense. +- Most of the controller/operators don't need this kind of optimizations. Adding complexity on all controllers for a small portion of the controllers out there is not the best approach for most of the developers. - If you need optimization related with Kubernetes resources, you can use Kubebuilder or implement your own solution for that specific use case like a cache based Kubernetes client or service. -- If you need otimization and is not related with Kubernetes API itself, it doesn't matter the controller library optimization. +- If you need optimization and is not related with Kubernetes API itself, it doesn't matter the controller library optimization. ## More examples @@ -143,25 +168,23 @@ Kooper only handles the events of resources that exist, these are triggered when Sometimes we have controllers that work on a main or primary resource and we also want to handle the events of a secondary resource that is based on the first one. For example, a deployment controller that watches the pods (secondary) that belong to the deployment (primary) handled. -After using multiresource controllers/retrievers, we though that we don't need a multiresource controller is not necesary becase: +After using multiresource controllers/retrievers, we though that we don't need a multiresource controller, this is not necesary becase: - Adds complexity. - Adds corner cases, this translates in bugs, e.g - - Internal cache based on IDs of `{namespace}/{name}` scheme. - - Receiving a deletion watch event of one type removes the other type object with the same name from the cache - - The different resources that share name and ns, will only process one of the types (sometimes is useful, others adds bugs and corner cases). -- An error on one of the retrieval types stops all the controller process and not only the one based on that type. -- The benefit of having this is to reuse the handler (we already can do this, a `Handler` is easy to reuse). + - Internal object cache based on IDs of `{namespace}/{name}` scheme (ignoring types). + - Receiving a deletion watch event of one type removes the other type object with the same name from the cache (service and deployment have same ns and same name). + - The different resources that share name and ns, will be only process one of the types (sometimes is useful, others adds bugs and corner cases). +- An error on one of the retrieval types stops all the controller handling process and not only the one based on that type. +- Programatically speaking, you can reuse the `Handler` in multiple controllers. -The solution to these problems embrace simplicity once again, and mainly is creating multiple controllers using the same `Handler`, each controller with a different `ListerWatcher`. The `Handler` API is easy enough to reuse it across multiple controllers, check an [example][multiresource-example]. Also, this comes with extra benefits: +The solution to these problems, is to embrace simplicity once again, and mainly is creating multiple controllers using the same `Handler`, each controller with a different `ListerWatcher`. The `Handler` API is easy enough to reuse it across multiple controllers, check an [example][multiresource-example]. Also, this comes with extra benefits: - Different controller interval depending on the type (fast changing secondary objects can reconcile faster than the primary one, or viceversa). - Wrap the controller handler with a middlewre only for a particular type. - One of the type retrieval fails, the other type controller continues working (running in degradation mode). - Flexibility, e.g leader election for the primary type, no leader election for the secondary type. - -Controller config has a handy flag to disable resync (`DisableResync`), sometimes this can be useful on secondary resources. - +- Controller config has a handy flag to disable resync (`DisableResync`), sometimes this can be useful on secondary resources (only act on changes). [travis-image]: https://travis-ci.org/spotahome/kooper.svg?branch=master [travis-url]: https://travis-ci.org/spotahome/kooper @@ -169,15 +192,15 @@ Controller config has a handy flag to disable resync (`DisableResync`), sometime [goreport-url]: https://goreportcard.com/report/github.com/spotahome/kooper [godoc-image]: https://godoc.org/github.com/spotahome/kooper?status.svg [godoc-url]: https://godoc.org/github.com/spotahome/kooper - [examples]: examples/ [grafana-dashboard]: https://grafana.com/dashboards/7082 [controllers]: https://kubernetes.io/docs/concepts/architecture/controller/ -[Kubebuilder]: https://github.com/kubernetes-sigs/kubebuilder +[kubebuilder]: https://github.com/kubernetes-sigs/kubebuilder [operator-framework]: https://github.com/operator-framework -[Kubewebhook]: https://github.com/slok/kubewebhook +[kubewebhook]: https://github.com/slok/kubewebhook [kube-code-generator]: https://github.com/slok/kube-code-generator [owner-ref]: https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/#owners-and-dependents [finalizers]: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#finalizers [finalizer-example]: examples/pod-terminator-operator/operator/operator.go -[multiresource-example]: examples/multi-resource-controller \ No newline at end of file +[multiresource-example]: examples/multi-resource-controller +[ci]: https://github.com/spotahome/kooper/actions From 1ac02f51d1026af6f04a43f02337962fac634f66 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Gallego Date: Fri, 10 Jul 2020 17:57:01 +0200 Subject: [PATCH 30/31] Update README.md Co-authored-by: Sergio Ballesteros --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7e1b7f8..6ad3f7d1 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Regarding the changes... To know all of them check the changelog but mainly we s most relevant changes you will need to be aware and could impact are: - Before there were operators and controllers, now only controllers (one single concept). -- Now CRD management in the library, CRDs now should be already registered when the controller starts (you can use [this][kube-code-generator] to generate these manifests). +- Before CRD management in the library, CRDs now should be already registered when the controller starts (you can use [this][kube-code-generator] to generate these manifests). - Refactor Prometheus metrics, so you will need to change dashboards/alerts. - `Delete` event removed because wasn't reliable (Check `Garbage-collection` section). From bf29ac815f08d48c6657419cccd935445088e619 Mon Sep 17 00:00:00 2001 From: Xabier Larrakoetxea Date: Fri, 10 Jul 2020 18:02:24 +0200 Subject: [PATCH 31/31] Improved readme major changes for v2 Signed-off-by: Xabier Larrakoetxea --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6ad3f7d1..391619f0 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,11 @@ import "github.com/spotahome/kooper/v2" Regarding the changes... To know all of them check the changelog but mainly we simplified everything. The most relevant changes you will need to be aware and could impact are: -- Before there were operators and controllers, now only controllers (one single concept). -- Before CRD management in the library, CRDs now should be already registered when the controller starts (you can use [this][kube-code-generator] to generate these manifests). -- Refactor Prometheus metrics, so you will need to change dashboards/alerts. +- Before there were concepts like `operator` and `controller`, now only `controller` (this is at library level, you can continue creating controllers/operators). +- Before the CRD management was inside the library, now this should be managed outside Kooper. + - You can use [this][kube-code-generator] to generate these manifests to register outside Kooper. + - This is because controllers and CRDs have different lifecycles. +- Refactored Prometheus metrics to be more reliable, so you will need to change dashboards/alerts. - `Delete` event removed because wasn't reliable (Check `Garbage-collection` section). ## Getting started