From e9f5b24dd91da76642cd00c9c6053ab81a7d1598 Mon Sep 17 00:00:00 2001 From: Jason Hall Date: Fri, 26 Oct 2018 21:57:32 -0400 Subject: [PATCH] Migrate to reconciler pattern for Build (#446) * Move controller -> reconciler * Fix tests * update-codegen * now with updated dep-collector * Reverse IsDone condition * Remove another indentation --- Gopkg.lock | 13 +- cmd/controller/main.go | 24 +- pkg/controller/build/controller.go | 411 --------------- pkg/reconciler/build/build.go | 202 ++++++-- .../build/build_test.go} | 120 ++--- .../build/template_common.go | 0 .../build/validate_build.go | 4 +- .../build/validation_test.go | 5 +- third_party/VENDOR-LICENSE | 405 --------------- vendor/github.com/golang/groupcache/LICENSE | 191 ------- .../github.com/golang/groupcache/lru/lru.go | 121 ----- vendor/k8s.io/client-go/tools/record/doc.go | 18 - vendor/k8s.io/client-go/tools/record/event.go | 322 ------------ .../client-go/tools/record/events_cache.go | 467 ------------------ vendor/k8s.io/client-go/tools/record/fake.go | 58 --- 15 files changed, 219 insertions(+), 2142 deletions(-) delete mode 100644 pkg/controller/build/controller.go rename pkg/{controller/build/controller_test.go => reconciler/build/build_test.go} (81%) rename pkg/{controller => reconciler}/build/template_common.go (100%) rename pkg/{controller => reconciler}/build/validate_build.go (97%) rename pkg/{controller => reconciler}/build/validation_test.go (99%) delete mode 100644 vendor/github.com/golang/groupcache/LICENSE delete mode 100644 vendor/github.com/golang/groupcache/lru/lru.go delete mode 100644 vendor/k8s.io/client-go/tools/record/doc.go delete mode 100644 vendor/k8s.io/client-go/tools/record/event.go delete mode 100644 vendor/k8s.io/client-go/tools/record/events_cache.go delete mode 100644 vendor/k8s.io/client-go/tools/record/fake.go diff --git a/Gopkg.lock b/Gopkg.lock index 0a4440d7..564ac017 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -77,13 +77,6 @@ pruneopts = "NUT" revision = "44145f04b68cf362d9c4df2182967c2275eaefed" -[[projects]] - digest = "1:7672c206322f45b33fac1ae2cb899263533ce0adcc6481d207725560208ec84e" - name = "github.com/golang/groupcache" - packages = ["lru"] - pruneopts = "NUT" - revision = "02826c3e79038b59d737d3b1c0a1d937f71a4433" - [[projects]] digest = "1:0d390d7037c2aecc37e78c2cfbe43d020d6f1fa83fd22266b7ec621189447d57" name = "github.com/golang/protobuf" @@ -566,7 +559,7 @@ version = "kubernetes-1.11.3" [[projects]] - digest = "1:4304c52cf9c36a599212012e341568cf9eb10847aa25c26e0138b361f288c904" + digest = "1:7e3c0eda5d345d93eac8aae75359b359f11138e57f2b66dd1508fbd042c16316" name = "k8s.io/client-go" packages = [ "discovery", @@ -721,7 +714,6 @@ "tools/clientcmd/api/v1", "tools/metrics", "tools/pager", - "tools/record", "tools/reference", "transport", "util/buffer", @@ -831,7 +823,6 @@ "k8s.io/apimachinery/pkg/types", "k8s.io/apimachinery/pkg/util/runtime", "k8s.io/apimachinery/pkg/util/sets/types", - "k8s.io/apimachinery/pkg/util/wait", "k8s.io/apimachinery/pkg/watch", "k8s.io/client-go/discovery", "k8s.io/client-go/discovery/fake", @@ -846,9 +837,7 @@ "k8s.io/client-go/testing", "k8s.io/client-go/tools/cache", "k8s.io/client-go/tools/clientcmd", - "k8s.io/client-go/tools/record", "k8s.io/client-go/util/flowcontrol", - "k8s.io/client-go/util/workqueue", "k8s.io/code-generator/cmd/client-gen", "k8s.io/code-generator/cmd/deepcopy-gen", "k8s.io/code-generator/cmd/defaulter-gen", diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 8b288eff..2ed50f3c 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -21,6 +21,12 @@ import ( "log" "time" + cachingclientset "github.com/knative/caching/pkg/client/clientset/versioned" + cachinginformers "github.com/knative/caching/pkg/client/informers/externalversions" + "github.com/knative/pkg/configmap" + "github.com/knative/pkg/logging" + "github.com/knative/pkg/logging/logkey" + "github.com/knative/pkg/signals" "go.uber.org/zap" "golang.org/x/sync/errgroup" kubeinformers "k8s.io/client-go/informers" @@ -32,20 +38,12 @@ import ( // _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" onclusterbuilder "github.com/knative/build/pkg/builder/cluster" + buildclientset "github.com/knative/build/pkg/client/clientset/versioned" + informers "github.com/knative/build/pkg/client/informers/externalversions" "github.com/knative/build/pkg/controller" - buildctrl "github.com/knative/build/pkg/controller/build" "github.com/knative/build/pkg/reconciler/build" "github.com/knative/build/pkg/reconciler/buildtemplate" "github.com/knative/build/pkg/reconciler/clusterbuildtemplate" - - buildclientset "github.com/knative/build/pkg/client/clientset/versioned" - informers "github.com/knative/build/pkg/client/informers/externalversions" - cachingclientset "github.com/knative/caching/pkg/client/clientset/versioned" - cachinginformers "github.com/knative/caching/pkg/client/informers/externalversions" - "github.com/knative/pkg/configmap" - "github.com/knative/pkg/logging" - "github.com/knative/pkg/logging/logkey" - "github.com/knative/pkg/signals" ) const ( @@ -110,11 +108,7 @@ func main() { // Build all of our controllers, with the clients constructed above. controllers := []controller.Interface{ - // TODO(mattmoor): Move the Build controller logic into pkg/reconciler/build - buildctrl.NewController(bldr, kubeClient, buildClient, - kubeInformerFactory, buildInformerFactory, logger), - - build.NewController(logger, kubeClient, buildClient, buildInformer), + build.NewController(logger, kubeClient, buildClient, buildInformer, buildTemplateInformer, clusterBuildTemplateInformer, bldr), clusterbuildtemplate.NewController(logger, kubeClient, buildClient, cachingClient, clusterBuildTemplateInformer, imageInformer), buildtemplate.NewController(logger, kubeClient, buildClient, diff --git a/pkg/controller/build/controller.go b/pkg/controller/build/controller.go deleted file mode 100644 index c362fbfe..00000000 --- a/pkg/controller/build/controller.go +++ /dev/null @@ -1,411 +0,0 @@ -/* -Copyright 2018 The Knative 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. -*/ - -package build - -import ( - "fmt" - "time" - - "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - kubeinformers "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/scheme" - typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/record" - "k8s.io/client-go/util/workqueue" - - v1alpha1 "github.com/knative/build/pkg/apis/build/v1alpha1" - "github.com/knative/build/pkg/builder" - clientset "github.com/knative/build/pkg/client/clientset/versioned" - informers "github.com/knative/build/pkg/client/informers/externalversions" - listers "github.com/knative/build/pkg/client/listers/build/v1alpha1" - "github.com/knative/build/pkg/controller" - duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" - "github.com/knative/pkg/logging/logkey" -) - -const controllerAgentName = "build-controller" - -const ( - // SuccessSynced is used as part of the Event 'reason' when a Build is synced - SuccessSynced = "Synced" - // ErrResourceExists is used as part of the Event 'reason' when a Build fails - // to sync due to a Deployment of the same name already existing. - ErrResourceExists = "ErrResourceExists" - - // MessageResourceSynced is the message used for an Event fired when a Build - // is synced successfully - MessageResourceSynced = "Build synced successfully" -) - -// Controller is the controller implementation for Build resources -type Controller struct { - // kubeclientset is a standard kubernetes clientset - kubeclientset kubernetes.Interface - // buildclientset is a clientset for our own API group - buildclientset clientset.Interface - - buildsLister listers.BuildLister - buildTemplatesLister listers.BuildTemplateLister - clusterBuildTemplatesLister listers.ClusterBuildTemplateLister - buildsSynced cache.InformerSynced - - // The builder through which work will be done. - builder builder.Interface - - // workqueue is a rate limited work queue. This is used to queue work to be - // processed instead of performing it as soon as a change happens. This - // means we can ensure we only process a fixed amount of resources at a - // time, and makes it easy to ensure we are never processing the same item - // simultaneously in two different workers. - workqueue workqueue.RateLimitingInterface - // recorder is an event recorder for recording Event resources to the - // Kubernetes API. - recorder record.EventRecorder - // Sugared logger is easier to use but is not as performant as the - // raw logger. In performance critical paths, call logger.Desugar() - // and use the returned raw logger instead. In addition to the - // performance benefits, raw logger also preserves type-safety at - // the expense of slightly greater verbosity. - logger *zap.SugaredLogger -} - -// NewController returns a new build controller -func NewController( - builder builder.Interface, - kubeclientset kubernetes.Interface, - buildclientset clientset.Interface, - kubeInformerFactory kubeinformers.SharedInformerFactory, - buildInformerFactory informers.SharedInformerFactory, - logger *zap.SugaredLogger, -) controller.Interface { - - // obtain a reference to a shared index informer for the Build type. - buildInformer := buildInformerFactory.Build().V1alpha1().Builds() - buildTemplateInformer := buildInformerFactory.Build().V1alpha1().BuildTemplates() - clusterBuildTemplateInformer := buildInformerFactory.Build().V1alpha1().ClusterBuildTemplates() - - // Enrich the logs with controller name - logger = logger.Named(controllerAgentName).With(zap.String(logkey.ControllerType, controllerAgentName)) - - // Create event broadcaster - logger.Info("Creating event broadcaster") - - eventBroadcaster := record.NewBroadcaster() - eventBroadcaster.StartLogging(logger.Infof) - eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeclientset.CoreV1().Events("")}) - recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) - - controller := &Controller{ - builder: builder, - kubeclientset: kubeclientset, - buildclientset: buildclientset, - buildsLister: buildInformer.Lister(), - buildTemplatesLister: buildTemplateInformer.Lister(), - clusterBuildTemplatesLister: clusterBuildTemplateInformer.Lister(), - buildsSynced: buildInformer.Informer().HasSynced, - workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Builds"), - recorder: recorder, - logger: logger, - } - - logger.Info("Setting up event handlers") - // Set up an event handler for when Build resources change - buildInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: controller.enqueueBuild, - UpdateFunc: func(old, new interface{}) { - controller.enqueueBuild(new) - }, - }) - - return controller -} - -// Run will set up the event handlers for types we are interested in, as well -// as syncing informer caches and starting workers. It will block until stopCh -// is closed, at which point it will shutdown the workqueue and wait for -// workers to finish processing their current work items. -func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error { - defer runtime.HandleCrash() - defer c.workqueue.ShutDown() - - // Start the informer factories to begin populating the informer caches - c.logger.Info("Starting Build controller") - - // Wait for the caches to be synced before starting workers - c.logger.Info("Waiting for informer caches to sync") - if ok := cache.WaitForCacheSync(stopCh, c.buildsSynced); !ok { - return fmt.Errorf("failed to wait for caches to sync") - } - - c.logger.Info("Starting workers") - // Launch two workers to process Build resources - for i := 0; i < threadiness; i++ { - go wait.Until(c.runWorker, time.Second, stopCh) - } - - c.logger.Info("Started workers") - <-stopCh - c.logger.Info("Shutting down workers") - - return nil -} - -// runWorker is a long-running function that will continually call the -// processNextWorkItem function in order to read and process a message on the -// workqueue. -func (c *Controller) runWorker() { - for c.processNextWorkItem() { - } -} - -// processNextWorkItem will read a single work item off the workqueue and -// attempt to process it, by calling the syncHandler. -func (c *Controller) processNextWorkItem() bool { - obj, shutdown := c.workqueue.Get() - - if shutdown { - return false - } - - // We wrap this block in a func so we can defer c.workqueue.Done. - if err := func(obj interface{}) error { - // We call Done here so the workqueue knows we have finished - // processing this item. We also must remember to call Forget if we - // do not want this work item being re-queued. For example, we do - // not call Forget if a transient error occurs, instead the item is - // put back on the workqueue and attempted again after a back-off - // period. - defer c.workqueue.Done(obj) - // We expect strings to come off the workqueue. These are of the - // form namespace/name. We do this as the delayed nature of the - // workqueue means the items in the informer cache may actually be - // more up to date that when the item was initially put onto the - // workqueue. - key, ok := obj.(string) - if !ok { - // As the item in the workqueue is actually invalid, we call - // Forget here else we'd go into a loop of attempting to - // process a work item that is invalid. - c.workqueue.Forget(obj) - runtime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) - return nil - } - // Run the syncHandler, passing it the namespace/name string of the - // Build resource to be synced. - if err := c.syncHandler(key); err != nil { - return fmt.Errorf("error syncing '%s': %s", key, err.Error()) - } - // Finally, if no error occurs we Forget this item so it does not - // get queued again until another change happens. - c.workqueue.Forget(obj) - c.logger.Infof("Successfully synced '%s'", key) - return nil - }(obj); err != nil { - runtime.HandleError(err) - } - - return true -} - -// enqueueBuild takes a Build resource and converts it into a namespace/name -// string which is then put onto the work queue. This method should *not* be -// passed resources of any type other than Build. -func (c *Controller) enqueueBuild(obj interface{}) { - var key string - var err error - if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { - runtime.HandleError(err) - return - } - c.workqueue.AddRateLimited(key) -} - -// syncHandler compares the actual state with the desired, and attempts to -// converge the two. It then updates the Status block of the Build resource -// with the current status of the resource. -func (c *Controller) syncHandler(key string) error { - // Convert the namespace/name string into a distinct namespace and name - namespace, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - runtime.HandleError(fmt.Errorf("invalid resource key: %s", key)) - return nil - } - - // Get the Build resource with this namespace/name - build, err := c.buildsLister.Builds(namespace).Get(name) - if err != nil { - // The Build resource may no longer exist, in which case we stop - // processing. - if errors.IsNotFound(err) { - runtime.HandleError(fmt.Errorf("build '%s' in work queue no longer exists", key)) - return nil - } - - return err - } - - // Don't mutate the informer's copy of our object. - build = build.DeepCopy() - - // Validate build - if err = c.validateBuild(build); err != nil { - c.logger.Errorf("Failed to validate build: %v", err) - return err - } - - // If the build's done, then ignore it. - if !builder.IsDone(&build.Status) { - // If the build is not done, but is in progress (has an operation), then asynchronously wait for it. - // TODO(mattmoor): Check whether the Builder matches the kind of our c.builder. - if build.Status.Builder != "" { - op, err := c.builder.OperationFromStatus(&build.Status) - if err != nil { - return err - } - - // Check if build has timed out - if builder.IsTimeout(&build.Status, build.Spec.Timeout) { - //cleanup operation and update status - timeoutMsg := fmt.Sprintf("Build %q failed to finish within %q", build.Name, build.Spec.Timeout.Duration.String()) - - if err := op.Terminate(); err != nil { - c.logger.Errorf("Failed to terminate pod: %v", err) - return err - } - - build.Status.SetCondition(&duckv1alpha1.Condition{ - Type: v1alpha1.BuildSucceeded, - Status: corev1.ConditionFalse, - Reason: "BuildTimeout", - Message: timeoutMsg, - }) - // update build completed time - build.Status.CompletionTime = metav1.Now() - c.recorder.Eventf(build, corev1.EventTypeWarning, "BuildTimeout", timeoutMsg) - - if _, err := c.updateStatus(build); err != nil { - c.logger.Errorf("Failed to update status for pod: %v", err) - return err - } - - c.logger.Errorf("Timeout: %v", timeoutMsg) - return nil - } - - // if not timed out then wait async - go c.waitForOperation(build, op) - } else { - build.Status.Builder = c.builder.Builder() - // If the build hasn't even started, then start it and record the operation in our status. - // Note that by recording our status, we will trigger a reconciliation, so the wait above - // will kick in. - var tmpl v1alpha1.BuildTemplateInterface - if build.Spec.Template != nil { - if build.Spec.Template.Kind == v1alpha1.ClusterBuildTemplateKind { - tmpl, err = c.clusterBuildTemplatesLister.Get(build.Spec.Template.Name) - if err != nil { - // The ClusterBuildTemplate resource may not exist. - if errors.IsNotFound(err) { - runtime.HandleError(fmt.Errorf("cluster build template %q does not exist", build.Spec.Template.Name)) - } - return err - } - } else { - tmpl, err = c.buildTemplatesLister.BuildTemplates(namespace).Get(build.Spec.Template.Name) - if err != nil { - // The BuildTemplate resource may not exist. - if errors.IsNotFound(err) { - runtime.HandleError(fmt.Errorf("build template %q in namespace %q does not exist", build.Spec.Template.Name, namespace)) - } - return err - } - } - } - build, err = builder.ApplyTemplate(build, tmpl) - if err != nil { - return err - } - // TODO: Validate build except steps+template - b, err := c.builder.BuildFromSpec(build) - if err != nil { - return err - } - op, err := b.Execute() - if err != nil { - build.Status.SetCondition(&duckv1alpha1.Condition{ - Type: v1alpha1.BuildSucceeded, - Status: corev1.ConditionFalse, - Reason: "BuildExecuteFailed", - Message: err.Error(), - }) - - c.recorder.Eventf(build, corev1.EventTypeWarning, "BuildExecuteFailed", "Failed to execute Build %q: %v", build.Name, err) - - if _, err := c.updateStatus(build); err != nil { - return err - } - return err - } - if err := op.Checkpoint(build, &build.Status); err != nil { - return err - } - build, err = c.updateStatus(build) - if err != nil { - return err - } - } - } - - c.recorder.Event(build, corev1.EventTypeNormal, SuccessSynced, MessageResourceSynced) - return nil -} - -func (c *Controller) waitForOperation(build *v1alpha1.Build, op builder.Operation) error { - status, err := op.Wait() - if err != nil { - c.logger.Errorf("Error while waiting for operation: %v", err) - return err - } - build.Status = *status - if _, err := c.updateStatus(build); err != nil { - c.logger.Errorf("Error updating build status: %v", err) - return err - } - return nil -} - -func (c *Controller) updateStatus(u *v1alpha1.Build) (*v1alpha1.Build, error) { - buildClient := c.buildclientset.BuildV1alpha1().Builds(u.Namespace) - newu, err := buildClient.Get(u.Name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - newu.Status = u.Status - - // Until #38113 is merged, we must use Update instead of UpdateStatus to - // update the Status block of the Build resource. UpdateStatus will not - // allow changes to the Spec of the resource, which is ideal for ensuring - // nothing other than resource status has been updated. - return buildClient.Update(newu) -} diff --git a/pkg/reconciler/build/build.go b/pkg/reconciler/build/build.go index a039eb86..9cc52b4d 100644 --- a/pkg/reconciler/build/build.go +++ b/pkg/reconciler/build/build.go @@ -18,22 +18,27 @@ package build import ( "context" + "fmt" - "go.uber.org/zap" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/tools/cache" - - "github.com/knative/pkg/controller" - "github.com/knative/pkg/logging" - "github.com/knative/pkg/logging/logkey" - + v1alpha1 "github.com/knative/build/pkg/apis/build/v1alpha1" + "github.com/knative/build/pkg/builder" clientset "github.com/knative/build/pkg/client/clientset/versioned" buildscheme "github.com/knative/build/pkg/client/clientset/versioned/scheme" informers "github.com/knative/build/pkg/client/informers/externalversions/build/v1alpha1" listers "github.com/knative/build/pkg/client/listers/build/v1alpha1" "github.com/knative/build/pkg/reconciler" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + "github.com/knative/pkg/controller" + "github.com/knative/pkg/logging" + "github.com/knative/pkg/logging/logkey" + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/cache" ) const controllerAgentName = "build-controller" @@ -45,7 +50,11 @@ type Reconciler struct { // buildclientset is a clientset for our own API group buildclientset clientset.Interface - buildsLister listers.BuildLister + buildsLister listers.BuildLister + buildTemplatesLister listers.BuildTemplateLister + clusterBuildTemplatesLister listers.ClusterBuildTemplateLister + + builder builder.Interface // Sugared logger is easier to use but is not as performant as the // raw logger. In performance critical paths, call logger.Desugar() @@ -70,16 +79,22 @@ func NewController( kubeclientset kubernetes.Interface, buildclientset clientset.Interface, buildInformer informers.BuildInformer, + buildTemplateInformer informers.BuildTemplateInformer, + clusterBuildTemplateInformer informers.ClusterBuildTemplateInformer, + builder builder.Interface, ) *controller.Impl { // Enrich the logs with controller name logger = logger.Named(controllerAgentName).With(zap.String(logkey.ControllerType, controllerAgentName)) r := &Reconciler{ - kubeclientset: kubeclientset, - buildclientset: buildclientset, - buildsLister: buildInformer.Lister(), - Logger: logger, + kubeclientset: kubeclientset, + buildclientset: buildclientset, + buildsLister: buildInformer.Lister(), + buildTemplatesLister: buildTemplateInformer.Lister(), + clusterBuildTemplatesLister: clusterBuildTemplateInformer.Lister(), + builder: builder, + Logger: logger, } impl := controller.NewImpl(r, logger, "Builds", reconciler.MustNewStatsReporter("Builds", r.Logger)) @@ -109,7 +124,8 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error { } // Get the Build resource with this namespace/name - if _, err := c.buildsLister.Builds(namespace).Get(name); errors.IsNotFound(err) { + build, err := c.buildsLister.Builds(namespace).Get(name) + if errors.IsNotFound(err) { // The Build resource may no longer exist, in which case we stop processing. logger.Errorf("build %q in work queue no longer exists", key) return nil @@ -117,21 +133,145 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error { return err } - // TODO(jasonhall): adopt the standard reconcile pattern. - // For Build this looks something like: - // podName := names.Pod(build) - // pod, err := c.podLister.Pods(build.Namespace).Get(podName) - // if IsNotFound(err) { - // desired := resources.MakePod(build) - // pod, err = c.kubeclientset.V1().Pods(build.Namespace).Create(desired) - // if err != nil { - // return err - // } - // } else if err != nil { - // return err - // } - // - // // Update build.Status based on pod.Status + // Don't mutate the informer's copy of our object. + build = build.DeepCopy() + + // Validate build + if err = c.validateBuild(build); err != nil { + c.Logger.Errorf("Failed to validate build: %v", err) + return err + } + + // If the build's done, then ignore it. + if builder.IsDone(&build.Status) { + return nil + } + + // If the build is not done, but is in progress (has an operation), then asynchronously wait for it. + // TODO(mattmoor): Check whether the Builder matches the kind of our c.builder. + if build.Status.Builder != "" { + op, err := c.builder.OperationFromStatus(&build.Status) + if err != nil { + return err + } + + // Check if build has timed out + if builder.IsTimeout(&build.Status, build.Spec.Timeout) { + //cleanup operation and update status + timeoutMsg := fmt.Sprintf("Build %q failed to finish within %q", build.Name, build.Spec.Timeout.Duration.String()) + + if err := op.Terminate(); err != nil { + c.Logger.Errorf("Failed to terminate pod: %v", err) + return err + } + build.Status.SetCondition(&duckv1alpha1.Condition{ + Type: v1alpha1.BuildSucceeded, + Status: corev1.ConditionFalse, + Reason: "BuildTimeout", + Message: timeoutMsg, + }) + // update build completed time + build.Status.CompletionTime = metav1.Now() + + if _, err := c.updateStatus(build); err != nil { + c.Logger.Errorf("Failed to update status for pod: %v", err) + return err + } + + c.Logger.Errorf("Timeout: %v", timeoutMsg) + return nil + } + + // if not timed out then wait async + go c.waitForOperation(build, op) + return nil + } + + // If the build hasn't even started, then start it and record the operation in our status. + // Note that by recording our status, we will trigger a reconciliation, so the wait above + // will kick in. + build.Status.Builder = c.builder.Builder() + var tmpl v1alpha1.BuildTemplateInterface + if build.Spec.Template != nil { + if build.Spec.Template.Kind == v1alpha1.ClusterBuildTemplateKind { + tmpl, err = c.clusterBuildTemplatesLister.Get(build.Spec.Template.Name) + if err != nil { + // The ClusterBuildTemplate resource may not exist. + if errors.IsNotFound(err) { + runtime.HandleError(fmt.Errorf("cluster build template %q does not exist", build.Spec.Template.Name)) + } + return err + } + } else { + tmpl, err = c.buildTemplatesLister.BuildTemplates(namespace).Get(build.Spec.Template.Name) + if err != nil { + // The BuildTemplate resource may not exist. + if errors.IsNotFound(err) { + runtime.HandleError(fmt.Errorf("build template %q in namespace %q does not exist", build.Spec.Template.Name, namespace)) + } + return err + } + } + } + build, err = builder.ApplyTemplate(build, tmpl) + if err != nil { + return err + } + // TODO: Validate build except steps+template + b, err := c.builder.BuildFromSpec(build) + if err != nil { + return err + } + op, err := b.Execute() + if err != nil { + build.Status.SetCondition(&duckv1alpha1.Condition{ + Type: v1alpha1.BuildSucceeded, + Status: corev1.ConditionFalse, + Reason: "BuildExecuteFailed", + Message: err.Error(), + }) + + if _, err := c.updateStatus(build); err != nil { + return err + } + return err + } + if err := op.Checkpoint(build, &build.Status); err != nil { + return err + } + build, err = c.updateStatus(build) + if err != nil { + return err + } return nil } + +func (c *Reconciler) waitForOperation(build *v1alpha1.Build, op builder.Operation) error { + status, err := op.Wait() + if err != nil { + c.Logger.Errorf("Error while waiting for operation: %v", err) + return err + } + build.Status = *status + if _, err := c.updateStatus(build); err != nil { + c.Logger.Errorf("Error updating build status: %v", err) + return err + } + return nil +} + +func (c *Reconciler) updateStatus(u *v1alpha1.Build) (*v1alpha1.Build, error) { + buildClient := c.buildclientset.BuildV1alpha1().Builds(u.Namespace) + newu, err := buildClient.Get(u.Name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + newu.Status = u.Status + + // Until #38113 is merged, we must use Update instead of UpdateStatus to + // update the Status block of the Build resource. UpdateStatus will not + // allow changes to the Spec of the resource, which is ideal for ensuring + // nothing other than resource status has been updated. + return buildClient.Update(newu) +} diff --git a/pkg/controller/build/controller_test.go b/pkg/reconciler/build/build_test.go similarity index 81% rename from pkg/controller/build/controller_test.go rename to pkg/reconciler/build/build_test.go index 4f6a8dea..a1998582 100644 --- a/pkg/controller/build/controller_test.go +++ b/pkg/reconciler/build/build_test.go @@ -17,15 +17,16 @@ limitations under the License. package build import ( + "context" "errors" "fmt" - "strings" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + "github.com/knative/pkg/controller" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" kuberrors "k8s.io/apimachinery/pkg/api/errors" @@ -35,7 +36,6 @@ import ( k8sfake "k8s.io/client-go/kubernetes/fake" clientgotesting "k8s.io/client-go/testing" "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/record" v1alpha1 "github.com/knative/build/pkg/apis/build/v1alpha1" "github.com/knative/build/pkg/builder" @@ -58,7 +58,6 @@ type fixture struct { client *fake.Clientset kubeclient *k8sfake.Clientset objects []runtime.Object - eventCh chan string } func newBuild(name string) *v1alpha1.Build { @@ -86,17 +85,14 @@ func (f *fixture) createServceAccount() { } } -func (f *fixture) newController(b builder.Interface, eventCh chan string) (*Controller, informers.SharedInformerFactory, kubeinformers.SharedInformerFactory) { - i := informers.NewSharedInformerFactory(f.client, noResyncPeriod) +func (f *fixture) newController(b builder.Interface) (*controller.Impl, informers.SharedInformerFactory, kubeinformers.SharedInformerFactory) { k8sI := kubeinformers.NewSharedInformerFactory(f.kubeclient, noResyncPeriod) logger := zap.NewExample().Sugar() - c := NewController(b, f.kubeclient, f.client, k8sI, i, logger).(*Controller) - - c.buildsSynced = func() bool { return true } - c.recorder = &record.FakeRecorder{ - Events: eventCh, - } - + i := informers.NewSharedInformerFactory(f.client, noResyncPeriod) + buildInformer := i.Build().V1alpha1().Builds() + buildTemplateInformer := i.Build().V1alpha1().BuildTemplates() + clusterBuildTemplateInformer := i.Build().V1alpha1().ClusterBuildTemplates() + c := NewController(logger, f.kubeclient, f.client, buildInformer, buildTemplateInformer, clusterBuildTemplateInformer, b) return c, i, k8sI } @@ -138,16 +134,14 @@ func TestBuildNotFoundFlow(t *testing.T) { f.client.PrependReactor("*", "*", reactor) stopCh := make(chan struct{}) - eventCh := make(chan string, 1024) defer close(stopCh) - defer close(eventCh) - c, i, k8sI := f.newController(bldr, eventCh) + c, i, k8sI := f.newController(bldr) f.updateIndex(i, []*v1alpha1.Build{build}) i.Start(stopCh) k8sI.Start(stopCh) - if err := c.syncHandler(getKey(build, t)); err == nil { + if err := c.Reconciler.Reconcile(context.Background(), getKey(build, t)); err == nil { t.Errorf("Expect error syncing build") } } @@ -161,10 +155,9 @@ func TestBuildWithBadKey(t *testing.T) { } f.createServceAccount() - eventCh := make(chan string, 1024) - c, _, _ := f.newController(bldr, eventCh) + c, _, _ := f.newController(bldr) - if err := c.syncHandler("bad/worse/worst"); err != nil { + if err := c.Reconciler.Reconcile(context.Background(), "bad/worse/worst"); err != nil { t.Errorf("Unexpected error while syncing build: %s", err.Error()) } } @@ -182,16 +175,14 @@ func TestBuildNotFoundError(t *testing.T) { f.createServceAccount() stopCh := make(chan struct{}) - eventCh := make(chan string, 1024) defer close(stopCh) - defer close(eventCh) - c, i, k8sI := f.newController(bldr, eventCh) + c, i, k8sI := f.newController(bldr) // Don't update build informers with test build object i.Start(stopCh) k8sI.Start(stopCh) - if err := c.syncHandler(getKey(build, t)); err != nil { + if err := c.Reconciler.Reconcile(context.Background(), getKey(build, t)); err != nil { t.Errorf("Unexpected error while syncing build: %s", err.Error()) } } @@ -215,16 +206,14 @@ func TestBuildWithNonExistentTemplates(t *testing.T) { f.createServceAccount() stopCh := make(chan struct{}) - eventCh := make(chan string, 1024) defer close(stopCh) - defer close(eventCh) - c, i, k8sI := f.newController(&nop.Builder{}, eventCh) + c, i, k8sI := f.newController(&nop.Builder{}) f.updateIndex(i, []*v1alpha1.Build{build}) i.Start(stopCh) k8sI.Start(stopCh) - if err := c.syncHandler(getKey(build, t)); err == nil { + if err := c.Reconciler.Reconcile(context.Background(), getKey(build, t)); err == nil { t.Errorf("Expect error syncing build") } else if !kuberrors.IsNotFound(err) { t.Errorf("Expect error to be not found err: %s", err.Error()) @@ -259,11 +248,9 @@ func TestBuildWithTemplate(t *testing.T) { f.createServceAccount() stopCh := make(chan struct{}) - eventCh := make(chan string, 1024) defer close(stopCh) - defer close(eventCh) - c, i, k8sI := f.newController(&nop.Builder{}, eventCh) + c, i, k8sI := f.newController(&nop.Builder{}) err := i.Build().V1alpha1().BuildTemplates().Informer().GetIndexer().Add(tmpl) if err != nil { @@ -274,7 +261,7 @@ func TestBuildWithTemplate(t *testing.T) { i.Start(stopCh) k8sI.Start(stopCh) - if err = c.syncHandler(getKey(build, t)); err != nil { + if err = c.Reconciler.Reconcile(context.Background(), getKey(build, t)); err != nil { t.Errorf("unexpected expecting error while syncing build: %s", err.Error()) } @@ -311,17 +298,16 @@ func TestBasicFlows(t *testing.T) { f.createServceAccount() stopCh := make(chan struct{}) - eventCh := make(chan string, 1024) defer close(stopCh) - defer close(eventCh) - c, i, k8sI := f.newController(test.bldr, eventCh) + c, i, k8sI := f.newController(test.bldr) f.updateIndex(i, []*v1alpha1.Build{build}) i.Start(stopCh) k8sI.Start(stopCh) // Run a single iteration of the syncHandler. - if err := c.syncHandler(getKey(build, t)); err != nil { + ctx := context.Background() + if err := c.Reconciler.Reconcile(ctx, getKey(build, t)); err != nil { t.Errorf("error syncing build: %v", err) } @@ -344,7 +330,7 @@ func TestBasicFlows(t *testing.T) { f.updateIndex(i, []*v1alpha1.Build{first}) // Run a second iteration of the syncHandler. - if err := c.syncHandler(getKey(build, t)); err != nil { + if err := c.Reconciler.Reconcile(ctx, getKey(build, t)); err != nil { t.Errorf("error syncing build: %v", err) } // A second reconciliation will trigger an asynchronous "Wait()", which @@ -363,24 +349,12 @@ func TestBasicFlows(t *testing.T) { if msg, _ := builder.ErrorMessage(&second.Status); test.expectedErrorMessage != msg { t.Errorf("Second ErrorMessage(%d); wanted %q, got %q.", idx, test.expectedErrorMessage, msg) } - - successEvent := "Normal Synced Build synced successfully" - - select { - case statusEvent := <-eventCh: - if statusEvent != successEvent { - t.Errorf("Event; wanted %q, got %q", successEvent, statusEvent) - } - case <-time.After(2 * time.Second): - t.Fatalf("No events published") - } } } func TestErrFlows(t *testing.T) { bldrErr := errors.New("not okay") bldr := &nop.Builder{Err: bldrErr} - expectedErrEventMsg := "Warning BuildExecuteFailed Failed to execute Build" build := newBuild("test-err") f := &fixture{ @@ -392,28 +366,17 @@ func TestErrFlows(t *testing.T) { f.createServceAccount() stopCh := make(chan struct{}) - eventCh := make(chan string, 1024) defer close(stopCh) - defer close(eventCh) - c, i, k8sI := f.newController(bldr, eventCh) + c, i, k8sI := f.newController(bldr) f.updateIndex(i, []*v1alpha1.Build{build}) i.Start(stopCh) k8sI.Start(stopCh) - if err := c.syncHandler(getKey(build, t)); err == nil { + if err := c.Reconciler.Reconcile(context.Background(), getKey(build, t)); err == nil { t.Errorf("Expect error syncing build") } - select { - case statusEvent := <-eventCh: - if !strings.Contains(statusEvent, expectedErrEventMsg) { - t.Errorf("Event message; wanted %q, got %q", expectedErrEventMsg, statusEvent) - } - case <-time.After(2 * time.Second): - t.Fatalf("No events published") - } - // Fetch the build object and check the status buildClient := f.client.BuildV1alpha1().Builds(build.Namespace) b, err := buildClient.Get(build.Name, metav1.GetOptions{}) @@ -444,17 +407,16 @@ func TestTimeoutFlows(t *testing.T) { f.createServceAccount() stopCh := make(chan struct{}) - eventCh := make(chan string, 1024) defer close(stopCh) - defer close(eventCh) - c, i, k8sI := f.newController(&nop.Builder{}, eventCh) + c, i, k8sI := f.newController(&nop.Builder{}) f.updateIndex(i, []*v1alpha1.Build{build}) i.Start(stopCh) k8sI.Start(stopCh) - if err := c.syncHandler(getKey(build, t)); err != nil { + ctx := context.Background() + if err := c.Reconciler.Reconcile(ctx, getKey(build, t)); err != nil { t.Errorf("Not Expect error when syncing build") } @@ -475,7 +437,7 @@ func TestTimeoutFlows(t *testing.T) { f.updateIndex(i, []*v1alpha1.Build{first}) // Run a second iteration of the syncHandler. - if err := c.syncHandler(getKey(build, t)); err != nil { + if err := c.Reconciler.Reconcile(ctx, getKey(build, t)); err != nil { t.Errorf("Unexpected error while syncing build: %v", err) } @@ -500,19 +462,6 @@ func TestTimeoutFlows(t *testing.T) { if d := cmp.Diff(buildStatus, expectedStatus, ignoreLastTransitionTime); d != "" { t.Errorf("Mismatch of build status: expected %#v ; got %#v; diff %s", expectedStatus, buildStatus, d) } - - expectedTimeoutMsg := fmt.Sprintf("Warning BuildTimeout %s", buildStatusMsg) - for i := 0; i < 2; i++ { - select { - case statusEvent := <-eventCh: - // Check 2nd event for timeout error msg. First event will sync build successfully - if !strings.Contains(statusEvent, expectedTimeoutMsg) && i != 0 { - t.Errorf("Event message; wanted %q got %q", expectedTimeoutMsg, statusEvent) - } - case <-time.After(4 * time.Second): - t.Fatalf("No events published") - } - } } func TestTimeoutFlowWithFailedOperation(t *testing.T) { @@ -535,17 +484,16 @@ func TestTimeoutFlowWithFailedOperation(t *testing.T) { f.createServceAccount() stopCh := make(chan struct{}) - eventCh := make(chan string, 1024) defer close(stopCh) - defer close(eventCh) - c, i, k8sI := f.newController(bldr, eventCh) + c, i, k8sI := f.newController(bldr) f.updateIndex(i, []*v1alpha1.Build{build}) i.Start(stopCh) k8sI.Start(stopCh) - if err := c.syncHandler(getKey(build, t)); err != nil { + ctx := context.Background() + if err := c.Reconciler.Reconcile(ctx, getKey(build, t)); err != nil { t.Errorf("Not Expect error when syncing build: %s", err.Error()) } @@ -562,7 +510,7 @@ func TestTimeoutFlowWithFailedOperation(t *testing.T) { f.updateIndex(i, []*v1alpha1.Build{first}) // Run a second iteration of the syncHandler to receive error from operation. - if err = c.syncHandler(getKey(build, t)); err != oppErr { + if err = c.Reconciler.Reconcile(ctx, getKey(build, t)); err != oppErr { t.Errorf("Expect error %#v when syncing build", oppErr) } } @@ -578,13 +526,11 @@ func TestRunController(t *testing.T) { } stopCh := make(chan struct{}) - eventCh := make(chan string, 1024) errChan := make(chan error, 1) - defer close(eventCh) defer close(errChan) - c, i, _ := f.newController(&nop.Builder{}, eventCh) + c, i, _ := f.newController(&nop.Builder{}) i.Start(stopCh) diff --git a/pkg/controller/build/template_common.go b/pkg/reconciler/build/template_common.go similarity index 100% rename from pkg/controller/build/template_common.go rename to pkg/reconciler/build/template_common.go diff --git a/pkg/controller/build/validate_build.go b/pkg/reconciler/build/validate_build.go similarity index 97% rename from pkg/controller/build/validate_build.go rename to pkg/reconciler/build/validate_build.go index 7b7f04e1..46c9061a 100644 --- a/pkg/controller/build/validate_build.go +++ b/pkg/reconciler/build/validate_build.go @@ -24,7 +24,7 @@ import ( "github.com/knative/build/pkg/apis/build/v1alpha1" ) -func (ac *Controller) validateBuild(b *v1alpha1.Build) error { +func (ac *Reconciler) validateBuild(b *v1alpha1.Build) error { if err := ac.validateSecrets(b); err != nil { return err } @@ -70,7 +70,7 @@ func (ac *Controller) validateBuild(b *v1alpha1.Build) error { // validateSecrets checks that if the Build specifies a ServiceAccount, that it // exists, and that any Secrets referenced by it exist, and have valid // annotations. -func (ac *Controller) validateSecrets(b *v1alpha1.Build) error { +func (ac *Reconciler) validateSecrets(b *v1alpha1.Build) error { saName := b.Spec.ServiceAccountName if saName == "" { saName = "default" diff --git a/pkg/controller/build/validation_test.go b/pkg/reconciler/build/validation_test.go similarity index 99% rename from pkg/controller/build/validation_test.go rename to pkg/reconciler/build/validation_test.go index 7ff6e48f..79a71755 100644 --- a/pkg/controller/build/validation_test.go +++ b/pkg/reconciler/build/validation_test.go @@ -328,11 +328,11 @@ func TestValidateBuild(t *testing.T) { } testLogger := zap.NewNop().Sugar() - ac := &Controller{ + ac := &Reconciler{ builder: &nop.Builder{}, kubeclientset: client, buildclientset: buildClient, - logger: testLogger, + Logger: testLogger, } verr := ac.validateBuild(c.build) @@ -342,6 +342,7 @@ func TestValidateBuild(t *testing.T) { }) } } + func TestValidateTemplate(t *testing.T) { for _, c := range []struct { desc string diff --git a/third_party/VENDOR-LICENSE b/third_party/VENDOR-LICENSE index 889c5c2a..ea640ba7 100644 --- a/third_party/VENDOR-LICENSE +++ b/third_party/VENDOR-LICENSE @@ -748,203 +748,6 @@ third-party archives. -=========================================================== -Import: github.com/knative/build/vendor/github.com/golang/groupcache - -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. - - - =========================================================== Import: github.com/knative/build/vendor/github.com/golang/protobuf @@ -4791,211 +4594,3 @@ Import: github.com/knative/build/vendor/k8s.io/client-go See the License for the specific language governing permissions and limitations under the License. - - -=========================================================== -Import: github.com/knative/build/vendor/k8s.io/kube-openapi - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. - diff --git a/vendor/github.com/golang/groupcache/LICENSE b/vendor/github.com/golang/groupcache/LICENSE deleted file mode 100644 index 37ec93a1..00000000 --- a/vendor/github.com/golang/groupcache/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/vendor/github.com/golang/groupcache/lru/lru.go b/vendor/github.com/golang/groupcache/lru/lru.go deleted file mode 100644 index cdfe2991..00000000 --- a/vendor/github.com/golang/groupcache/lru/lru.go +++ /dev/null @@ -1,121 +0,0 @@ -/* -Copyright 2013 Google Inc. - -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. -*/ - -// Package lru implements an LRU cache. -package lru - -import "container/list" - -// Cache is an LRU cache. It is not safe for concurrent access. -type Cache struct { - // MaxEntries is the maximum number of cache entries before - // an item is evicted. Zero means no limit. - MaxEntries int - - // OnEvicted optionally specificies a callback function to be - // executed when an entry is purged from the cache. - OnEvicted func(key Key, value interface{}) - - ll *list.List - cache map[interface{}]*list.Element -} - -// A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators -type Key interface{} - -type entry struct { - key Key - value interface{} -} - -// New creates a new Cache. -// If maxEntries is zero, the cache has no limit and it's assumed -// that eviction is done by the caller. -func New(maxEntries int) *Cache { - return &Cache{ - MaxEntries: maxEntries, - ll: list.New(), - cache: make(map[interface{}]*list.Element), - } -} - -// Add adds a value to the cache. -func (c *Cache) Add(key Key, value interface{}) { - if c.cache == nil { - c.cache = make(map[interface{}]*list.Element) - c.ll = list.New() - } - if ee, ok := c.cache[key]; ok { - c.ll.MoveToFront(ee) - ee.Value.(*entry).value = value - return - } - ele := c.ll.PushFront(&entry{key, value}) - c.cache[key] = ele - if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries { - c.RemoveOldest() - } -} - -// Get looks up a key's value from the cache. -func (c *Cache) Get(key Key) (value interface{}, ok bool) { - if c.cache == nil { - return - } - if ele, hit := c.cache[key]; hit { - c.ll.MoveToFront(ele) - return ele.Value.(*entry).value, true - } - return -} - -// Remove removes the provided key from the cache. -func (c *Cache) Remove(key Key) { - if c.cache == nil { - return - } - if ele, hit := c.cache[key]; hit { - c.removeElement(ele) - } -} - -// RemoveOldest removes the oldest item from the cache. -func (c *Cache) RemoveOldest() { - if c.cache == nil { - return - } - ele := c.ll.Back() - if ele != nil { - c.removeElement(ele) - } -} - -func (c *Cache) removeElement(e *list.Element) { - c.ll.Remove(e) - kv := e.Value.(*entry) - delete(c.cache, kv.key) - if c.OnEvicted != nil { - c.OnEvicted(kv.key, kv.value) - } -} - -// Len returns the number of items in the cache. -func (c *Cache) Len() int { - if c.cache == nil { - return 0 - } - return c.ll.Len() -} diff --git a/vendor/k8s.io/client-go/tools/record/doc.go b/vendor/k8s.io/client-go/tools/record/doc.go deleted file mode 100644 index 657ddecb..00000000 --- a/vendor/k8s.io/client-go/tools/record/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2014 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. -*/ - -// Package record has all client logic for recording and reporting events. -package record // import "k8s.io/client-go/tools/record" diff --git a/vendor/k8s.io/client-go/tools/record/event.go b/vendor/k8s.io/client-go/tools/record/event.go deleted file mode 100644 index 168dfa80..00000000 --- a/vendor/k8s.io/client-go/tools/record/event.go +++ /dev/null @@ -1,322 +0,0 @@ -/* -Copyright 2014 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. -*/ - -package record - -import ( - "fmt" - "math/rand" - "time" - - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/clock" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/watch" - restclient "k8s.io/client-go/rest" - ref "k8s.io/client-go/tools/reference" - - "net/http" - - "github.com/golang/glog" -) - -const maxTriesPerEvent = 12 - -var defaultSleepDuration = 10 * time.Second - -const maxQueuedEvents = 1000 - -// EventSink knows how to store events (client.Client implements it.) -// EventSink must respect the namespace that will be embedded in 'event'. -// It is assumed that EventSink will return the same sorts of errors as -// pkg/client's REST client. -type EventSink interface { - Create(event *v1.Event) (*v1.Event, error) - Update(event *v1.Event) (*v1.Event, error) - Patch(oldEvent *v1.Event, data []byte) (*v1.Event, error) -} - -// EventRecorder knows how to record events on behalf of an EventSource. -type EventRecorder interface { - // Event constructs an event from the given information and puts it in the queue for sending. - // 'object' is the object this event is about. Event will make a reference-- or you may also - // pass a reference to the object directly. - // 'type' of this event, and can be one of Normal, Warning. New types could be added in future - // 'reason' is the reason this event is generated. 'reason' should be short and unique; it - // should be in UpperCamelCase format (starting with a capital letter). "reason" will be used - // to automate handling of events, so imagine people writing switch statements to handle them. - // You want to make that easy. - // 'message' is intended to be human readable. - // - // The resulting event will be created in the same namespace as the reference object. - Event(object runtime.Object, eventtype, reason, message string) - - // Eventf is just like Event, but with Sprintf for the message field. - Eventf(object runtime.Object, eventtype, reason, messageFmt string, args ...interface{}) - - // PastEventf is just like Eventf, but with an option to specify the event's 'timestamp' field. - PastEventf(object runtime.Object, timestamp metav1.Time, eventtype, reason, messageFmt string, args ...interface{}) - - // AnnotatedEventf is just like eventf, but with annotations attached - AnnotatedEventf(object runtime.Object, annotations map[string]string, eventtype, reason, messageFmt string, args ...interface{}) -} - -// EventBroadcaster knows how to receive events and send them to any EventSink, watcher, or log. -type EventBroadcaster interface { - // StartEventWatcher starts sending events received from this EventBroadcaster to the given - // event handler function. The return value can be ignored or used to stop recording, if - // desired. - StartEventWatcher(eventHandler func(*v1.Event)) watch.Interface - - // StartRecordingToSink starts sending events received from this EventBroadcaster to the given - // sink. The return value can be ignored or used to stop recording, if desired. - StartRecordingToSink(sink EventSink) watch.Interface - - // StartLogging starts sending events received from this EventBroadcaster to the given logging - // function. The return value can be ignored or used to stop recording, if desired. - StartLogging(logf func(format string, args ...interface{})) watch.Interface - - // NewRecorder returns an EventRecorder that can be used to send events to this EventBroadcaster - // with the event source set to the given event source. - NewRecorder(scheme *runtime.Scheme, source v1.EventSource) EventRecorder -} - -// Creates a new event broadcaster. -func NewBroadcaster() EventBroadcaster { - return &eventBroadcasterImpl{watch.NewBroadcaster(maxQueuedEvents, watch.DropIfChannelFull), defaultSleepDuration} -} - -func NewBroadcasterForTests(sleepDuration time.Duration) EventBroadcaster { - return &eventBroadcasterImpl{watch.NewBroadcaster(maxQueuedEvents, watch.DropIfChannelFull), sleepDuration} -} - -type eventBroadcasterImpl struct { - *watch.Broadcaster - sleepDuration time.Duration -} - -// StartRecordingToSink starts sending events received from the specified eventBroadcaster to the given sink. -// The return value can be ignored or used to stop recording, if desired. -// TODO: make me an object with parameterizable queue length and retry interval -func (eventBroadcaster *eventBroadcasterImpl) StartRecordingToSink(sink EventSink) watch.Interface { - // The default math/rand package functions aren't thread safe, so create a - // new Rand object for each StartRecording call. - randGen := rand.New(rand.NewSource(time.Now().UnixNano())) - eventCorrelator := NewEventCorrelator(clock.RealClock{}) - return eventBroadcaster.StartEventWatcher( - func(event *v1.Event) { - recordToSink(sink, event, eventCorrelator, randGen, eventBroadcaster.sleepDuration) - }) -} - -func recordToSink(sink EventSink, event *v1.Event, eventCorrelator *EventCorrelator, randGen *rand.Rand, sleepDuration time.Duration) { - // Make a copy before modification, because there could be multiple listeners. - // Events are safe to copy like this. - eventCopy := *event - event = &eventCopy - result, err := eventCorrelator.EventCorrelate(event) - if err != nil { - utilruntime.HandleError(err) - } - if result.Skip { - return - } - tries := 0 - for { - if recordEvent(sink, result.Event, result.Patch, result.Event.Count > 1, eventCorrelator) { - break - } - tries++ - if tries >= maxTriesPerEvent { - glog.Errorf("Unable to write event '%#v' (retry limit exceeded!)", event) - break - } - // Randomize the first sleep so that various clients won't all be - // synced up if the master goes down. - if tries == 1 { - time.Sleep(time.Duration(float64(sleepDuration) * randGen.Float64())) - } else { - time.Sleep(sleepDuration) - } - } -} - -func isKeyNotFoundError(err error) bool { - statusErr, _ := err.(*errors.StatusError) - - if statusErr != nil && statusErr.Status().Code == http.StatusNotFound { - return true - } - - return false -} - -// recordEvent attempts to write event to a sink. It returns true if the event -// was successfully recorded or discarded, false if it should be retried. -// If updateExistingEvent is false, it creates a new event, otherwise it updates -// existing event. -func recordEvent(sink EventSink, event *v1.Event, patch []byte, updateExistingEvent bool, eventCorrelator *EventCorrelator) bool { - var newEvent *v1.Event - var err error - if updateExistingEvent { - newEvent, err = sink.Patch(event, patch) - } - // Update can fail because the event may have been removed and it no longer exists. - if !updateExistingEvent || (updateExistingEvent && isKeyNotFoundError(err)) { - // Making sure that ResourceVersion is empty on creation - event.ResourceVersion = "" - newEvent, err = sink.Create(event) - } - if err == nil { - // we need to update our event correlator with the server returned state to handle name/resourceversion - eventCorrelator.UpdateState(newEvent) - return true - } - - // If we can't contact the server, then hold everything while we keep trying. - // Otherwise, something about the event is malformed and we should abandon it. - switch err.(type) { - case *restclient.RequestConstructionError: - // We will construct the request the same next time, so don't keep trying. - glog.Errorf("Unable to construct event '%#v': '%v' (will not retry!)", event, err) - return true - case *errors.StatusError: - if errors.IsAlreadyExists(err) { - glog.V(5).Infof("Server rejected event '%#v': '%v' (will not retry!)", event, err) - } else { - glog.Errorf("Server rejected event '%#v': '%v' (will not retry!)", event, err) - } - return true - case *errors.UnexpectedObjectError: - // We don't expect this; it implies the server's response didn't match a - // known pattern. Go ahead and retry. - default: - // This case includes actual http transport errors. Go ahead and retry. - } - glog.Errorf("Unable to write event: '%v' (may retry after sleeping)", err) - return false -} - -// StartLogging starts sending events received from this EventBroadcaster to the given logging function. -// The return value can be ignored or used to stop recording, if desired. -func (eventBroadcaster *eventBroadcasterImpl) StartLogging(logf func(format string, args ...interface{})) watch.Interface { - return eventBroadcaster.StartEventWatcher( - func(e *v1.Event) { - logf("Event(%#v): type: '%v' reason: '%v' %v", e.InvolvedObject, e.Type, e.Reason, e.Message) - }) -} - -// StartEventWatcher starts sending events received from this EventBroadcaster to the given event handler function. -// The return value can be ignored or used to stop recording, if desired. -func (eventBroadcaster *eventBroadcasterImpl) StartEventWatcher(eventHandler func(*v1.Event)) watch.Interface { - watcher := eventBroadcaster.Watch() - go func() { - defer utilruntime.HandleCrash() - for watchEvent := range watcher.ResultChan() { - event, ok := watchEvent.Object.(*v1.Event) - if !ok { - // This is all local, so there's no reason this should - // ever happen. - continue - } - eventHandler(event) - } - }() - return watcher -} - -// NewRecorder returns an EventRecorder that records events with the given event source. -func (eventBroadcaster *eventBroadcasterImpl) NewRecorder(scheme *runtime.Scheme, source v1.EventSource) EventRecorder { - return &recorderImpl{scheme, source, eventBroadcaster.Broadcaster, clock.RealClock{}} -} - -type recorderImpl struct { - scheme *runtime.Scheme - source v1.EventSource - *watch.Broadcaster - clock clock.Clock -} - -func (recorder *recorderImpl) generateEvent(object runtime.Object, annotations map[string]string, timestamp metav1.Time, eventtype, reason, message string) { - ref, err := ref.GetReference(recorder.scheme, object) - if err != nil { - glog.Errorf("Could not construct reference to: '%#v' due to: '%v'. Will not report event: '%v' '%v' '%v'", object, err, eventtype, reason, message) - return - } - - if !validateEventType(eventtype) { - glog.Errorf("Unsupported event type: '%v'", eventtype) - return - } - - event := recorder.makeEvent(ref, annotations, eventtype, reason, message) - event.Source = recorder.source - - go func() { - // NOTE: events should be a non-blocking operation - defer utilruntime.HandleCrash() - recorder.Action(watch.Added, event) - }() -} - -func validateEventType(eventtype string) bool { - switch eventtype { - case v1.EventTypeNormal, v1.EventTypeWarning: - return true - } - return false -} - -func (recorder *recorderImpl) Event(object runtime.Object, eventtype, reason, message string) { - recorder.generateEvent(object, nil, metav1.Now(), eventtype, reason, message) -} - -func (recorder *recorderImpl) Eventf(object runtime.Object, eventtype, reason, messageFmt string, args ...interface{}) { - recorder.Event(object, eventtype, reason, fmt.Sprintf(messageFmt, args...)) -} - -func (recorder *recorderImpl) PastEventf(object runtime.Object, timestamp metav1.Time, eventtype, reason, messageFmt string, args ...interface{}) { - recorder.generateEvent(object, nil, timestamp, eventtype, reason, fmt.Sprintf(messageFmt, args...)) -} - -func (recorder *recorderImpl) AnnotatedEventf(object runtime.Object, annotations map[string]string, eventtype, reason, messageFmt string, args ...interface{}) { - recorder.generateEvent(object, annotations, metav1.Now(), eventtype, reason, fmt.Sprintf(messageFmt, args...)) -} - -func (recorder *recorderImpl) makeEvent(ref *v1.ObjectReference, annotations map[string]string, eventtype, reason, message string) *v1.Event { - t := metav1.Time{Time: recorder.clock.Now()} - namespace := ref.Namespace - if namespace == "" { - namespace = metav1.NamespaceDefault - } - return &v1.Event{ - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%v.%x", ref.Name, t.UnixNano()), - Namespace: namespace, - Annotations: annotations, - }, - InvolvedObject: *ref, - Reason: reason, - Message: message, - FirstTimestamp: t, - LastTimestamp: t, - Count: 1, - Type: eventtype, - } -} diff --git a/vendor/k8s.io/client-go/tools/record/events_cache.go b/vendor/k8s.io/client-go/tools/record/events_cache.go deleted file mode 100644 index 6ac767c9..00000000 --- a/vendor/k8s.io/client-go/tools/record/events_cache.go +++ /dev/null @@ -1,467 +0,0 @@ -/* -Copyright 2015 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. -*/ - -package record - -import ( - "encoding/json" - "fmt" - "strings" - "sync" - "time" - - "github.com/golang/groupcache/lru" - - "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/clock" - "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/apimachinery/pkg/util/strategicpatch" - "k8s.io/client-go/util/flowcontrol" -) - -const ( - maxLruCacheEntries = 4096 - - // if we see the same event that varies only by message - // more than 10 times in a 10 minute period, aggregate the event - defaultAggregateMaxEvents = 10 - defaultAggregateIntervalInSeconds = 600 - - // by default, allow a source to send 25 events about an object - // but control the refill rate to 1 new event every 5 minutes - // this helps control the long-tail of events for things that are always - // unhealthy - defaultSpamBurst = 25 - defaultSpamQPS = 1. / 300. -) - -// getEventKey builds unique event key based on source, involvedObject, reason, message -func getEventKey(event *v1.Event) string { - return strings.Join([]string{ - event.Source.Component, - event.Source.Host, - event.InvolvedObject.Kind, - event.InvolvedObject.Namespace, - event.InvolvedObject.Name, - event.InvolvedObject.FieldPath, - string(event.InvolvedObject.UID), - event.InvolvedObject.APIVersion, - event.Type, - event.Reason, - event.Message, - }, - "") -} - -// getSpamKey builds unique event key based on source, involvedObject -func getSpamKey(event *v1.Event) string { - return strings.Join([]string{ - event.Source.Component, - event.Source.Host, - event.InvolvedObject.Kind, - event.InvolvedObject.Namespace, - event.InvolvedObject.Name, - string(event.InvolvedObject.UID), - event.InvolvedObject.APIVersion, - }, - "") -} - -// EventFilterFunc is a function that returns true if the event should be skipped -type EventFilterFunc func(event *v1.Event) bool - -// DefaultEventFilterFunc returns false for all incoming events -func DefaultEventFilterFunc(event *v1.Event) bool { - return false -} - -// EventSourceObjectSpamFilter is responsible for throttling -// the amount of events a source and object can produce. -type EventSourceObjectSpamFilter struct { - sync.RWMutex - - // the cache that manages last synced state - cache *lru.Cache - - // burst is the amount of events we allow per source + object - burst int - - // qps is the refill rate of the token bucket in queries per second - qps float32 - - // clock is used to allow for testing over a time interval - clock clock.Clock -} - -// NewEventSourceObjectSpamFilter allows burst events from a source about an object with the specified qps refill. -func NewEventSourceObjectSpamFilter(lruCacheSize, burst int, qps float32, clock clock.Clock) *EventSourceObjectSpamFilter { - return &EventSourceObjectSpamFilter{ - cache: lru.New(lruCacheSize), - burst: burst, - qps: qps, - clock: clock, - } -} - -// spamRecord holds data used to perform spam filtering decisions. -type spamRecord struct { - // rateLimiter controls the rate of events about this object - rateLimiter flowcontrol.RateLimiter -} - -// Filter controls that a given source+object are not exceeding the allowed rate. -func (f *EventSourceObjectSpamFilter) Filter(event *v1.Event) bool { - var record spamRecord - - // controls our cached information about this event (source+object) - eventKey := getSpamKey(event) - - // do we have a record of similar events in our cache? - f.Lock() - defer f.Unlock() - value, found := f.cache.Get(eventKey) - if found { - record = value.(spamRecord) - } - - // verify we have a rate limiter for this record - if record.rateLimiter == nil { - record.rateLimiter = flowcontrol.NewTokenBucketRateLimiterWithClock(f.qps, f.burst, f.clock) - } - - // ensure we have available rate - filter := !record.rateLimiter.TryAccept() - - // update the cache - f.cache.Add(eventKey, record) - - return filter -} - -// EventAggregatorKeyFunc is responsible for grouping events for aggregation -// It returns a tuple of the following: -// aggregateKey - key the identifies the aggregate group to bucket this event -// localKey - key that makes this event in the local group -type EventAggregatorKeyFunc func(event *v1.Event) (aggregateKey string, localKey string) - -// EventAggregatorByReasonFunc aggregates events by exact match on event.Source, event.InvolvedObject, event.Type and event.Reason -func EventAggregatorByReasonFunc(event *v1.Event) (string, string) { - return strings.Join([]string{ - event.Source.Component, - event.Source.Host, - event.InvolvedObject.Kind, - event.InvolvedObject.Namespace, - event.InvolvedObject.Name, - string(event.InvolvedObject.UID), - event.InvolvedObject.APIVersion, - event.Type, - event.Reason, - }, - ""), event.Message -} - -// EventAggregatorMessageFunc is responsible for producing an aggregation message -type EventAggregatorMessageFunc func(event *v1.Event) string - -// EventAggregratorByReasonMessageFunc returns an aggregate message by prefixing the incoming message -func EventAggregatorByReasonMessageFunc(event *v1.Event) string { - return "(combined from similar events): " + event.Message -} - -// EventAggregator identifies similar events and aggregates them into a single event -type EventAggregator struct { - sync.RWMutex - - // The cache that manages aggregation state - cache *lru.Cache - - // The function that groups events for aggregation - keyFunc EventAggregatorKeyFunc - - // The function that generates a message for an aggregate event - messageFunc EventAggregatorMessageFunc - - // The maximum number of events in the specified interval before aggregation occurs - maxEvents uint - - // The amount of time in seconds that must transpire since the last occurrence of a similar event before it's considered new - maxIntervalInSeconds uint - - // clock is used to allow for testing over a time interval - clock clock.Clock -} - -// NewEventAggregator returns a new instance of an EventAggregator -func NewEventAggregator(lruCacheSize int, keyFunc EventAggregatorKeyFunc, messageFunc EventAggregatorMessageFunc, - maxEvents int, maxIntervalInSeconds int, clock clock.Clock) *EventAggregator { - return &EventAggregator{ - cache: lru.New(lruCacheSize), - keyFunc: keyFunc, - messageFunc: messageFunc, - maxEvents: uint(maxEvents), - maxIntervalInSeconds: uint(maxIntervalInSeconds), - clock: clock, - } -} - -// aggregateRecord holds data used to perform aggregation decisions -type aggregateRecord struct { - // we track the number of unique local keys we have seen in the aggregate set to know when to actually aggregate - // if the size of this set exceeds the max, we know we need to aggregate - localKeys sets.String - // The last time at which the aggregate was recorded - lastTimestamp metav1.Time -} - -// EventAggregate checks if a similar event has been seen according to the -// aggregation configuration (max events, max interval, etc) and returns: -// -// - The (potentially modified) event that should be created -// - The cache key for the event, for correlation purposes. This will be set to -// the full key for normal events, and to the result of -// EventAggregatorMessageFunc for aggregate events. -func (e *EventAggregator) EventAggregate(newEvent *v1.Event) (*v1.Event, string) { - now := metav1.NewTime(e.clock.Now()) - var record aggregateRecord - // eventKey is the full cache key for this event - eventKey := getEventKey(newEvent) - // aggregateKey is for the aggregate event, if one is needed. - aggregateKey, localKey := e.keyFunc(newEvent) - - // Do we have a record of similar events in our cache? - e.Lock() - defer e.Unlock() - value, found := e.cache.Get(aggregateKey) - if found { - record = value.(aggregateRecord) - } - - // Is the previous record too old? If so, make a fresh one. Note: if we didn't - // find a similar record, its lastTimestamp will be the zero value, so we - // create a new one in that case. - maxInterval := time.Duration(e.maxIntervalInSeconds) * time.Second - interval := now.Time.Sub(record.lastTimestamp.Time) - if interval > maxInterval { - record = aggregateRecord{localKeys: sets.NewString()} - } - - // Write the new event into the aggregation record and put it on the cache - record.localKeys.Insert(localKey) - record.lastTimestamp = now - e.cache.Add(aggregateKey, record) - - // If we are not yet over the threshold for unique events, don't correlate them - if uint(record.localKeys.Len()) < e.maxEvents { - return newEvent, eventKey - } - - // do not grow our local key set any larger than max - record.localKeys.PopAny() - - // create a new aggregate event, and return the aggregateKey as the cache key - // (so that it can be overwritten.) - eventCopy := &v1.Event{ - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%v.%x", newEvent.InvolvedObject.Name, now.UnixNano()), - Namespace: newEvent.Namespace, - }, - Count: 1, - FirstTimestamp: now, - InvolvedObject: newEvent.InvolvedObject, - LastTimestamp: now, - Message: e.messageFunc(newEvent), - Type: newEvent.Type, - Reason: newEvent.Reason, - Source: newEvent.Source, - } - return eventCopy, aggregateKey -} - -// eventLog records data about when an event was observed -type eventLog struct { - // The number of times the event has occurred since first occurrence. - count uint - - // The time at which the event was first recorded. - firstTimestamp metav1.Time - - // The unique name of the first occurrence of this event - name string - - // Resource version returned from previous interaction with server - resourceVersion string -} - -// eventLogger logs occurrences of an event -type eventLogger struct { - sync.RWMutex - cache *lru.Cache - clock clock.Clock -} - -// newEventLogger observes events and counts their frequencies -func newEventLogger(lruCacheEntries int, clock clock.Clock) *eventLogger { - return &eventLogger{cache: lru.New(lruCacheEntries), clock: clock} -} - -// eventObserve records an event, or updates an existing one if key is a cache hit -func (e *eventLogger) eventObserve(newEvent *v1.Event, key string) (*v1.Event, []byte, error) { - var ( - patch []byte - err error - ) - eventCopy := *newEvent - event := &eventCopy - - e.Lock() - defer e.Unlock() - - // Check if there is an existing event we should update - lastObservation := e.lastEventObservationFromCache(key) - - // If we found a result, prepare a patch - if lastObservation.count > 0 { - // update the event based on the last observation so patch will work as desired - event.Name = lastObservation.name - event.ResourceVersion = lastObservation.resourceVersion - event.FirstTimestamp = lastObservation.firstTimestamp - event.Count = int32(lastObservation.count) + 1 - - eventCopy2 := *event - eventCopy2.Count = 0 - eventCopy2.LastTimestamp = metav1.NewTime(time.Unix(0, 0)) - eventCopy2.Message = "" - - newData, _ := json.Marshal(event) - oldData, _ := json.Marshal(eventCopy2) - patch, err = strategicpatch.CreateTwoWayMergePatch(oldData, newData, event) - } - - // record our new observation - e.cache.Add( - key, - eventLog{ - count: uint(event.Count), - firstTimestamp: event.FirstTimestamp, - name: event.Name, - resourceVersion: event.ResourceVersion, - }, - ) - return event, patch, err -} - -// updateState updates its internal tracking information based on latest server state -func (e *eventLogger) updateState(event *v1.Event) { - key := getEventKey(event) - e.Lock() - defer e.Unlock() - // record our new observation - e.cache.Add( - key, - eventLog{ - count: uint(event.Count), - firstTimestamp: event.FirstTimestamp, - name: event.Name, - resourceVersion: event.ResourceVersion, - }, - ) -} - -// lastEventObservationFromCache returns the event from the cache, reads must be protected via external lock -func (e *eventLogger) lastEventObservationFromCache(key string) eventLog { - value, ok := e.cache.Get(key) - if ok { - observationValue, ok := value.(eventLog) - if ok { - return observationValue - } - } - return eventLog{} -} - -// EventCorrelator processes all incoming events and performs analysis to avoid overwhelming the system. It can filter all -// incoming events to see if the event should be filtered from further processing. It can aggregate similar events that occur -// frequently to protect the system from spamming events that are difficult for users to distinguish. It performs de-duplication -// to ensure events that are observed multiple times are compacted into a single event with increasing counts. -type EventCorrelator struct { - // the function to filter the event - filterFunc EventFilterFunc - // the object that performs event aggregation - aggregator *EventAggregator - // the object that observes events as they come through - logger *eventLogger -} - -// EventCorrelateResult is the result of a Correlate -type EventCorrelateResult struct { - // the event after correlation - Event *v1.Event - // if provided, perform a strategic patch when updating the record on the server - Patch []byte - // if true, do no further processing of the event - Skip bool -} - -// NewEventCorrelator returns an EventCorrelator configured with default values. -// -// The EventCorrelator is responsible for event filtering, aggregating, and counting -// prior to interacting with the API server to record the event. -// -// The default behavior is as follows: -// * Aggregation is performed if a similar event is recorded 10 times in a -// in a 10 minute rolling interval. A similar event is an event that varies only by -// the Event.Message field. Rather than recording the precise event, aggregation -// will create a new event whose message reports that it has combined events with -// the same reason. -// * Events are incrementally counted if the exact same event is encountered multiple -// times. -// * A source may burst 25 events about an object, but has a refill rate budget -// per object of 1 event every 5 minutes to control long-tail of spam. -func NewEventCorrelator(clock clock.Clock) *EventCorrelator { - cacheSize := maxLruCacheEntries - spamFilter := NewEventSourceObjectSpamFilter(cacheSize, defaultSpamBurst, defaultSpamQPS, clock) - return &EventCorrelator{ - filterFunc: spamFilter.Filter, - aggregator: NewEventAggregator( - cacheSize, - EventAggregatorByReasonFunc, - EventAggregatorByReasonMessageFunc, - defaultAggregateMaxEvents, - defaultAggregateIntervalInSeconds, - clock), - - logger: newEventLogger(cacheSize, clock), - } -} - -// EventCorrelate filters, aggregates, counts, and de-duplicates all incoming events -func (c *EventCorrelator) EventCorrelate(newEvent *v1.Event) (*EventCorrelateResult, error) { - if newEvent == nil { - return nil, fmt.Errorf("event is nil") - } - aggregateEvent, ckey := c.aggregator.EventAggregate(newEvent) - observedEvent, patch, err := c.logger.eventObserve(aggregateEvent, ckey) - if c.filterFunc(observedEvent) { - return &EventCorrelateResult{Skip: true}, nil - } - return &EventCorrelateResult{Event: observedEvent, Patch: patch}, err -} - -// UpdateState based on the latest observed state from server -func (c *EventCorrelator) UpdateState(event *v1.Event) { - c.logger.updateState(event) -} diff --git a/vendor/k8s.io/client-go/tools/record/fake.go b/vendor/k8s.io/client-go/tools/record/fake.go deleted file mode 100644 index 6e031daa..00000000 --- a/vendor/k8s.io/client-go/tools/record/fake.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2015 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. -*/ - -package record - -import ( - "fmt" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// FakeRecorder is used as a fake during tests. It is thread safe. It is usable -// when created manually and not by NewFakeRecorder, however all events may be -// thrown away in this case. -type FakeRecorder struct { - Events chan string -} - -func (f *FakeRecorder) Event(object runtime.Object, eventtype, reason, message string) { - if f.Events != nil { - f.Events <- fmt.Sprintf("%s %s %s", eventtype, reason, message) - } -} - -func (f *FakeRecorder) Eventf(object runtime.Object, eventtype, reason, messageFmt string, args ...interface{}) { - if f.Events != nil { - f.Events <- fmt.Sprintf(eventtype+" "+reason+" "+messageFmt, args...) - } -} - -func (f *FakeRecorder) PastEventf(object runtime.Object, timestamp metav1.Time, eventtype, reason, messageFmt string, args ...interface{}) { -} - -func (f *FakeRecorder) AnnotatedEventf(object runtime.Object, annotations map[string]string, eventtype, reason, messageFmt string, args ...interface{}) { - f.Eventf(object, eventtype, reason, messageFmt, args) -} - -// NewFakeRecorder creates new fake event recorder with event channel with -// buffer of given size. -func NewFakeRecorder(bufferSize int) *FakeRecorder { - return &FakeRecorder{ - Events: make(chan string, bufferSize), - } -}