From 4e4b61cc25303de42ff0efe8aa1cf19ff1bfbf97 Mon Sep 17 00:00:00 2001 From: Jack Ottofaro Date: Thu, 12 Nov 2020 14:18:16 -0500 Subject: [PATCH 1/2] =?UTF-8?q?Module=20changes=20for=20controller-runtime?= =?UTF-8?q?=E2=80=99s=20leader=20with=20lease=20mechanism?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supported legacy leader.Become method required operator-lib dependency. --- go.mod | 1 - go.sum | 2 - .../operator-framework/operator-lib/LICENSE | 201 ------------- .../handler/enqueue_annotation.go | 180 ------------ .../handler/instrumented_enqueue_object.go | 85 ------ .../handler/internal/metrics/metrics.go | 33 --- .../operator-lib/leader/doc.go | 54 ---- .../operator-lib/leader/leader.go | 269 ------------------ vendor/modules.txt | 4 - 9 files changed, 829 deletions(-) delete mode 100644 vendor/github.com/operator-framework/operator-lib/LICENSE delete mode 100644 vendor/github.com/operator-framework/operator-lib/handler/enqueue_annotation.go delete mode 100644 vendor/github.com/operator-framework/operator-lib/handler/instrumented_enqueue_object.go delete mode 100644 vendor/github.com/operator-framework/operator-lib/handler/internal/metrics/metrics.go delete mode 100644 vendor/github.com/operator-framework/operator-lib/leader/doc.go delete mode 100644 vendor/github.com/operator-framework/operator-lib/leader/leader.go diff --git a/go.mod b/go.mod index 7ea4079fe..f1f43eeec 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,6 @@ require ( github.com/openshift/cluster-image-registry-operator v0.0.0-20200919005020-e202ec478417 github.com/openshift/custom-resource-status v0.0.0-20200602122900-c002fd1547ca github.com/openshift/library-go v0.0.0-20201109112824-093ad3cf6600 - github.com/operator-framework/operator-lib v0.1.0 github.com/stretchr/testify v1.5.1 go.uber.org/zap v1.15.0 // indirect golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect diff --git a/go.sum b/go.sum index 42f2cb89f..2c64661c1 100644 --- a/go.sum +++ b/go.sum @@ -410,8 +410,6 @@ github.com/openshift/library-go v0.0.0-20200731053141-ff55255233e3/go.mod h1:q7e github.com/openshift/library-go v0.0.0-20201109112824-093ad3cf6600 h1:BjzxCCiWcRwOq3LAKZxZWmY6wD85D5462KpG1p7zMRw= github.com/openshift/library-go v0.0.0-20201109112824-093ad3cf6600/go.mod h1:1xYaYQcQsn+AyCRsvOU+Qn5z6GGiCmcblXkT/RZLVfo= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/operator-framework/operator-lib v0.1.0 h1:7Qy6v2ZccvCeFLWEkrGnN+U+DkaeIWp0gAZaBM9T3DI= -github.com/operator-framework/operator-lib v0.1.0/go.mod h1:HLw61JTIEeq0YLeVf4dwYx/zt4DmLGZUVWI1y3Lf5Hg= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= diff --git a/vendor/github.com/operator-framework/operator-lib/LICENSE b/vendor/github.com/operator-framework/operator-lib/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/vendor/github.com/operator-framework/operator-lib/LICENSE +++ /dev/null @@ -1,201 +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: - - (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/operator-framework/operator-lib/handler/enqueue_annotation.go b/vendor/github.com/operator-framework/operator-lib/handler/enqueue_annotation.go deleted file mode 100644 index db8020a2a..000000000 --- a/vendor/github.com/operator-framework/operator-lib/handler/enqueue_annotation.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2020 The Operator-SDK 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 handler - -import ( - "fmt" - "strings" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/util/workqueue" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/event" - crtHandler "sigs.k8s.io/controller-runtime/pkg/handler" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -var log = logf.Log.WithName("event_handler") - -const ( - // NamespacedNameAnnotation is an annotation whose value encodes the name and namespace of a resource to - // reconcile when a resource containing this annotation changes. Valid values are of the form - // `/` for namespace-scoped owners and `` for cluster-scoped owners. - NamespacedNameAnnotation = "operator-sdk/primary-resource" - // TypeAnnotation is an annotation whose value encodes the group and kind of a resource to reconcil when a - // resource containing this annotation changes. Valid values are of the form `` for resource in the - // core group, and `.` for all other resources. - TypeAnnotation = "operator-sdk/primary-resource-type" -) - -// EnqueueRequestForAnnotation enqueues Request containing the Name and Namespace specified in the -// annotations of the object that is the source of the Event. The source of the event triggers reconciliation -// of the parent resource which is identified by annotations. `NamespacedNameAnnotation` and -// `TypeAnnotation` together uniquely identify an owner resource to reconcile. -// -// handler.EnqueueRequestForAnnotation can be used to trigger reconciliation of resources which are -// cross-referenced. This allows a namespace-scoped dependent to trigger reconciliation of an owner -// which is in a different namespace, and a cluster-scoped dependent can trigger the reconciliation -// of a namespace(scoped)-owner. -// -// As an example, consider the case where we would like to watch clusterroles based on which we reconcile -// namespace-scoped replicasets. With native owner references, this would not be possible since the -// cluster-scoped dependent (clusterroles) is trying to specify a namespace-scoped owner (replicasets). -// Whereas in case of annotations-based handlers, we could implement the following: -// -// if err := c.Watch(&source.Kind{ -// // Watch clusterroles -// Type: &rbacv1.ClusterRole{}}, -// -// // Enqueue ReplicaSet reconcile requests using the namespacedName annotation value in the request. -// &handler.EnqueueRequestForAnnotation{schema.GroupKind{Group:"apps", Kind:"ReplicaSet"}}); err != nil { -// entryLog.Error(err, "unable to watch ClusterRole") -// os.Exit(1) -// } -// } -// -// With this watch, the ReplicaSet reconciler would receive a request to reconcile -// "my-namespace/my-replicaset" based on a change to a ClusterRole that has the following annotations: -// -// annotations: -// operator-sdk/primary-resource:"my-namespace/my-replicaset" -// operator-sdk/primary-resource-type:"ReplicaSet.apps" -// -// Though an annotation-based watch handler removes the boundaries set by native owner reference implementation, -// the garbage collector still respects the scope restrictions. For example, -// if a parent creates a child resource across scopes not supported by owner references, it becomes the -// responsibility of the reconciler to clean up the child resource. Hence, the resource utilizing this handler -// SHOULD ALWAYS BE IMPLEMENTED WITH A FINALIZER. -type EnqueueRequestForAnnotation struct { - Type schema.GroupKind -} - -var _ crtHandler.EventHandler = &EnqueueRequestForAnnotation{} - -// Create implements EventHandler -func (e *EnqueueRequestForAnnotation) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) { - if ok, req := e.getAnnotationRequests(evt.Meta); ok { - q.Add(req) - } -} - -// Update implements EventHandler -func (e *EnqueueRequestForAnnotation) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) { - if ok, req := e.getAnnotationRequests(evt.MetaOld); ok { - q.Add(req) - } - if ok, req := e.getAnnotationRequests(evt.MetaNew); ok { - q.Add(req) - } -} - -// Delete implements EventHandler -func (e *EnqueueRequestForAnnotation) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) { - if ok, req := e.getAnnotationRequests(evt.Meta); ok { - q.Add(req) - } -} - -// Generic implements EventHandler -func (e *EnqueueRequestForAnnotation) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) { - if ok, req := e.getAnnotationRequests(evt.Meta); ok { - q.Add(req) - } -} - -// getAnnotationRequests checks if the provided object has the annotations so as to enqueue the reconcile request. -func (e *EnqueueRequestForAnnotation) getAnnotationRequests(object metav1.Object) (bool, reconcile.Request) { - if len(object.GetAnnotations()) == 0 { - return false, reconcile.Request{} - } - - if typeString, ok := object.GetAnnotations()[TypeAnnotation]; ok && typeString == e.Type.String() { - namespacedNameString, ok := object.GetAnnotations()[NamespacedNameAnnotation] - if !ok { - log.Info("Unable to find namespaced name annotation for resource", "resource", object) - } - if strings.TrimSpace(namespacedNameString) == "" { - return false, reconcile.Request{} - } - nsn := parseNamespacedName(namespacedNameString) - return true, reconcile.Request{NamespacedName: nsn} - } - return false, reconcile.Request{} -} - -// parseNamespacedName parses the provided string to extract the namespace and name into a -// types.NamespacedName. The edge case of empty string is handled prior to calling this function. -func parseNamespacedName(namespacedNameString string) types.NamespacedName { - values := strings.SplitN(namespacedNameString, "/", 2) - - switch len(values) { - case 1: - return types.NamespacedName{Name: values[0]} - default: - return types.NamespacedName{Namespace: values[0], Name: values[1]} - } -} - -// SetOwnerAnnotations helps in adding 'NamespacedNameAnnotation' and 'TypeAnnotation' to object based on -// the values obtained from owner. The object gets the annotations from owner's namespace, name, group -// and kind. In other terms, object can be said to be the dependent having annotations from the owner. -// When a watch is set on the object, the annotations help to identify the owner and trigger reconciliation. -// Annotations are ALWAYS overwritten. -func SetOwnerAnnotations(owner, object controllerutil.Object) error { - if owner.GetName() == "" { - return fmt.Errorf("%T does not have a name, cannot call SetOwnerAnnotations", owner) - } - - ownerGK := owner.GetObjectKind().GroupVersionKind().GroupKind() - - if ownerGK.Kind == "" { - return fmt.Errorf("Owner %s Kind not found, cannot call SetOwnerAnnotations", owner.GetName()) - } - - annotations := object.GetAnnotations() - if annotations == nil { - annotations = map[string]string{} - } - - annotations[NamespacedNameAnnotation] = fmt.Sprintf("%s/%s", owner.GetNamespace(), owner.GetName()) - annotations[TypeAnnotation] = ownerGK.String() - - object.SetAnnotations(annotations) - - return nil -} diff --git a/vendor/github.com/operator-framework/operator-lib/handler/instrumented_enqueue_object.go b/vendor/github.com/operator-framework/operator-lib/handler/instrumented_enqueue_object.go deleted file mode 100644 index 7bdd39360..000000000 --- a/vendor/github.com/operator-framework/operator-lib/handler/instrumented_enqueue_object.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2020 The Operator-SDK 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 handler - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/util/workqueue" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/handler" - - "github.com/operator-framework/operator-lib/handler/internal/metrics" -) - -// InstrumentedEnqueueRequestForObject wraps controller-runtime handler for -// "EnqueueRequestForObject", and sets up primary resource metrics on event -// handlers. The main objective of this handler is to set prometheus metrics -// when create/update/delete events occur. These metrics contain the following -// information on resource. -// -// resource_created_at_seconds{"name", "namespace", "group", "version", "kind"} -// -// To call the handler use: -// -// &handler.InstrumentedEnqueueRequestForObject{} -type InstrumentedEnqueueRequestForObject struct { - handler.EnqueueRequestForObject -} - -// Create implements EventHandler, and creates the metrics. -func (h InstrumentedEnqueueRequestForObject) Create(e event.CreateEvent, q workqueue.RateLimitingInterface) { - setResourceMetric(e.Meta, e.Object) - h.EnqueueRequestForObject.Create(e, q) -} - -// Update implements EventHandler, and updates the metrics. -func (h InstrumentedEnqueueRequestForObject) Update(e event.UpdateEvent, q workqueue.RateLimitingInterface) { - setResourceMetric(e.MetaOld, e.ObjectOld) - setResourceMetric(e.MetaNew, e.ObjectNew) - - h.EnqueueRequestForObject.Update(e, q) -} - -// Delete implements EventHandler, and deletes metrics. -func (h InstrumentedEnqueueRequestForObject) Delete(e event.DeleteEvent, q workqueue.RateLimitingInterface) { - deleteResourceMetric(e.Meta, e.Object) - h.EnqueueRequestForObject.Delete(e, q) -} - -func setResourceMetric(metadata metav1.Object, obj runtime.Object) { - if metadata != nil && obj != nil { - labels := getResourceLabels(metadata, obj) - m, _ := metrics.ResourceCreatedAt.GetMetricWith(labels) - m.Set(float64(metadata.GetCreationTimestamp().UTC().Unix())) - } -} - -func deleteResourceMetric(metadata metav1.Object, obj runtime.Object) { - if metadata != nil && obj != nil { - labels := getResourceLabels(metadata, obj) - _ = metrics.ResourceCreatedAt.Delete(labels) - } -} - -func getResourceLabels(metadata metav1.Object, obj runtime.Object) map[string]string { - return map[string]string{ - "name": metadata.GetName(), - "namespace": metadata.GetNamespace(), - "group": obj.GetObjectKind().GroupVersionKind().Group, - "version": obj.GetObjectKind().GroupVersionKind().Version, - "kind": obj.GetObjectKind().GroupVersionKind().Kind, - } -} diff --git a/vendor/github.com/operator-framework/operator-lib/handler/internal/metrics/metrics.go b/vendor/github.com/operator-framework/operator-lib/handler/internal/metrics/metrics.go deleted file mode 100644 index 07d19ea8a..000000000 --- a/vendor/github.com/operator-framework/operator-lib/handler/internal/metrics/metrics.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2020 The Operator-SDK 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 metrics - -import ( - "github.com/prometheus/client_golang/prometheus" - "sigs.k8s.io/controller-runtime/pkg/metrics" -) - -// ResourceCreatedAt creates new prometheus metrics for primary resource, -// with information {"name", "namespace", "group", "version", "kind"} -var ResourceCreatedAt = prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Name: "resource_created_at_seconds", - Help: "Timestamp at which a resource was created", -}, []string{"name", "namespace", "group", "version", "kind"}) - -func init() { - metrics.Registry.MustRegister( - ResourceCreatedAt, - ) -} diff --git a/vendor/github.com/operator-framework/operator-lib/leader/doc.go b/vendor/github.com/operator-framework/operator-lib/leader/doc.go deleted file mode 100644 index 3eee3e353..000000000 --- a/vendor/github.com/operator-framework/operator-lib/leader/doc.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2020 The Operator-SDK 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 leader implements Leader For Life, a simple alternative to lease-based -leader election. - -Both the Leader For Life and lease-based approaches to leader election are -built on the concept that each candidate will attempt to create a resource with -the same GVK, namespace, and name. Whichever candidate succeeds becomes the -leader. The rest receive "already exists" errors and wait for a new -opportunity. - -Leases provide a way to indirectly observe whether the leader still exists. The -leader must periodically renew its lease, usually by updating a timestamp in -its lock record. If it fails to do so, it is presumed dead, and a new election -takes place. If the leader is in fact still alive but unreachable, it is -expected to gracefully step down. A variety of factors can cause a leader to -fail at updating its lease, but continue acting as the leader before succeeding -at stepping down. - -In the "leader for life" approach, a specific Pod is the leader. Once -established (by creating a lock record), the Pod is the leader until it is -destroyed. There is no possibility for multiple pods to think they are the -leader at the same time. The leader does not need to renew a lease, consider -stepping down, or do anything related to election activity once it becomes the -leader. - -The lock record in this case is a ConfigMap whose OwnerReference is set to the -Pod that is the leader. When the leader is destroyed, the ConfigMap gets -garbage-collected, enabling a different candidate Pod to become the leader. - -Leader for Life requires that all candidate Pods be in the same Namespace. It -uses the downwards API to determine the pod name, as hostname is not reliable. -You should run it configured with: - -env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name -*/ -package leader diff --git a/vendor/github.com/operator-framework/operator-lib/leader/leader.go b/vendor/github.com/operator-framework/operator-lib/leader/leader.go deleted file mode 100644 index 89e9c8657..000000000 --- a/vendor/github.com/operator-framework/operator-lib/leader/leader.go +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright 2020 The Operator-SDK 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 leader - -import ( - "context" - "fmt" - "io/ioutil" - "os" - "strings" - "time" - - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/wait" - crclient "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/config" - logf "sigs.k8s.io/controller-runtime/pkg/log" -) - -// ErrNoNamespace indicates that a namespace could not be found for the current -// environment -var ErrNoNamespace = fmt.Errorf("namespace not found for current environment") - -// podNameEnvVar is the constant for env variable POD_NAME -// which is the name of the current pod. -const podNameEnvVar = "POD_NAME" - -var readNamespace = func() ([]byte, error) { - return ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace") -} - -var log = logf.Log.WithName("leader") - -// maxBackoffInterval defines the maximum amount of time to wait between -// attempts to become the leader. -const maxBackoffInterval = time.Second * 16 - -type Option func(*Config) error - -type Config struct { - Client crclient.Client -} - -func (c *Config) setDefaults() error { - if c.Client == nil { - config, err := config.GetConfig() - if err != nil { - return err - } - - client, err := crclient.New(config, crclient.Options{}) - if err != nil { - return err - } - c.Client = client - } - return nil -} - -func WithClient(cl crclient.Client) Option { - return func(c *Config) error { - c.Client = cl - return nil - } -} - -// Become ensures that the current pod is the leader within its namespace. If -// run outside a cluster, it will skip leader election and return nil. It -// continuously tries to create a ConfigMap with the provided name and the -// current pod set as the owner reference. Only one can exist at a time with -// the same name, so the pod that successfully creates the ConfigMap is the -// leader. Upon termination of that pod, the garbage collector will delete the -// ConfigMap, enabling a different pod to become the leader. -func Become(ctx context.Context, lockName string, opts ...Option) error { - log.Info("Trying to become the leader.") - - config := Config{} - - for _, opt := range opts { - if err := opt(&config); err != nil { - return err - } - } - - if err := config.setDefaults(); err != nil { - return err - } - - ns, err := getOperatorNamespace() - if err != nil { - return err - } - - owner, err := myOwnerRef(ctx, config.Client, ns) - if err != nil { - return err - } - - // check for existing lock from this pod, in case we got restarted - existing := &corev1.ConfigMap{} - key := crclient.ObjectKey{Namespace: ns, Name: lockName} - err = config.Client.Get(ctx, key, existing) - - switch { - case err == nil: - for _, existingOwner := range existing.GetOwnerReferences() { - if existingOwner.Name == owner.Name { - log.Info("Found existing lock with my name. I was likely restarted.") - log.Info("Continuing as the leader.") - return nil - } - log.Info("Found existing lock", "LockOwner", existingOwner.Name) - } - case apierrors.IsNotFound(err): - log.Info("No pre-existing lock was found.") - default: - log.Error(err, "Unknown error trying to get ConfigMap") - return err - } - - cm := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: lockName, - Namespace: ns, - OwnerReferences: []metav1.OwnerReference{*owner}, - }, - } - - // try to create a lock - backoff := time.Second - for { - err := config.Client.Create(ctx, cm) - switch { - case err == nil: - log.Info("Became the leader.") - return nil - case apierrors.IsAlreadyExists(err): - // refresh the lock so we use current leader - key := crclient.ObjectKey{Namespace: ns, Name: lockName} - if err := config.Client.Get(ctx, key, existing); err != nil { - log.Info("Leader lock configmap not found.") - continue // configmap got lost ... just wait a bit - } - - existingOwners := existing.GetOwnerReferences() - switch { - case len(existingOwners) != 1: - log.Info("Leader lock configmap must have exactly one owner reference.", "ConfigMap", existing) - case existingOwners[0].Kind != "Pod": - log.Info("Leader lock configmap owner reference must be a pod.", "OwnerReference", existingOwners[0]) - default: - leaderPod := &corev1.Pod{} - key = crclient.ObjectKey{Namespace: ns, Name: existingOwners[0].Name} - err = config.Client.Get(ctx, key, leaderPod) - switch { - case apierrors.IsNotFound(err): - log.Info("Leader pod has been deleted, waiting for garbage collection to remove the lock.") - case err != nil: - return err - case isPodEvicted(*leaderPod) && leaderPod.GetDeletionTimestamp() == nil: - log.Info("Operator pod with leader lock has been evicted.", "leader", leaderPod.Name) - log.Info("Deleting evicted leader.") - // Pod may not delete immediately, continue with backoff - err := config.Client.Delete(ctx, leaderPod) - if err != nil { - log.Error(err, "Leader pod could not be deleted.") - } - - default: - log.Info("Not the leader. Waiting.") - } - } - - select { - case <-time.After(wait.Jitter(backoff, .2)): - if backoff < maxBackoffInterval { - backoff *= 2 - } - continue - case <-ctx.Done(): - return ctx.Err() - } - default: - log.Error(err, "Unknown error creating ConfigMap") - return err - } - } -} - -// myOwnerRef returns an OwnerReference that corresponds to the pod in which -// this code is currently running. -// It expects the environment variable POD_NAME to be set by the downwards API -func myOwnerRef(ctx context.Context, client crclient.Client, ns string) (*metav1.OwnerReference, error) { - myPod, err := getPod(ctx, client, ns) - if err != nil { - return nil, err - } - - owner := &metav1.OwnerReference{ - APIVersion: "v1", - Kind: "Pod", - Name: myPod.ObjectMeta.Name, - UID: myPod.ObjectMeta.UID, - } - return owner, nil -} - -func isPodEvicted(pod corev1.Pod) bool { - podFailed := pod.Status.Phase == corev1.PodFailed - podEvicted := pod.Status.Reason == "Evicted" - return podFailed && podEvicted -} - -// getOperatorNamespace returns the namespace the operator should be running in. -func getOperatorNamespace() (string, error) { - nsBytes, err := readNamespace() - if err != nil { - if os.IsNotExist(err) { - return "", ErrNoNamespace - } - return "", err - } - ns := strings.TrimSpace(string(nsBytes)) - log.V(1).Info("Found namespace", "Namespace", ns) - return ns, nil -} - -// getPod returns a Pod object that corresponds to the pod in which the code -// is currently running. -// It expects the environment variable POD_NAME to be set by the downwards API. -func getPod(ctx context.Context, client crclient.Client, ns string) (*corev1.Pod, error) { - podName := os.Getenv(podNameEnvVar) - if podName == "" { - return nil, fmt.Errorf("required env %s not set, please configure downward API", podNameEnvVar) - } - - log.V(1).Info("Found podname", "Pod.Name", podName) - - pod := &corev1.Pod{} - key := crclient.ObjectKey{Namespace: ns, Name: podName} - err := client.Get(ctx, key, pod) - if err != nil { - log.Error(err, "Failed to get Pod", "Pod.Namespace", ns, "Pod.Name", podName) - return nil, err - } - - // .Get() clears the APIVersion and Kind, - // so we need to set them before returning the object. - pod.TypeMeta.APIVersion = "v1" - pod.TypeMeta.Kind = "Pod" - - log.V(1).Info("Found Pod", "Pod.Namespace", ns, "Pod.Name", pod.Name) - - return pod, nil -} diff --git a/vendor/modules.txt b/vendor/modules.txt index e462260ec..2b462298b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -76,10 +76,6 @@ github.com/openshift/cluster-image-registry-operator/pkg/version github.com/openshift/custom-resource-status/conditions/v1 # github.com/openshift/library-go v0.0.0-20201109112824-093ad3cf6600 github.com/openshift/library-go/pkg/route/routeapihelpers -# github.com/operator-framework/operator-lib v0.1.0 -github.com/operator-framework/operator-lib/handler -github.com/operator-framework/operator-lib/handler/internal/metrics -github.com/operator-framework/operator-lib/leader # github.com/pkg/errors v0.9.1 github.com/pkg/errors # github.com/pmezard/go-difflib v1.0.0 From 5caf9dc5a3731f3e6de92e482760265bc0fa1d3f Mon Sep 17 00:00:00 2001 From: Jack Ottofaro Date: Thu, 12 Nov 2020 14:27:06 -0500 Subject: [PATCH 2/2] Migrate controller to function SetupWithManager --- controllers/updateservice_controller.go | 110 +++++------------------- main.go | 108 ++++++++--------------- 2 files changed, 55 insertions(+), 163 deletions(-) diff --git a/controllers/updateservice_controller.go b/controllers/updateservice_controller.go index 5bb860ae8..dea6e1dd9 100755 --- a/controllers/updateservice_controller.go +++ b/controllers/updateservice_controller.go @@ -7,7 +7,6 @@ import ( "github.com/go-logr/logr" conditionsv1 "github.com/openshift/custom-resource-status/conditions/v1" - ophandler "github.com/operator-framework/operator-lib/handler" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" policyv1beta1 "k8s.io/api/policy/v1beta1" @@ -16,12 +15,9 @@ import ( "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/handler" logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" @@ -34,85 +30,6 @@ import ( var log = logf.Log.WithName("controller_updateservice") -// Options holds settings for the reconciler -type Options struct { - // OperandImage is the full reference to a container image for the operand. - OperandImage string -} - -// Add creates a new UpdateService Controller and adds it to the Manager. The Manager will set fields on the Controller -// and Start it when the Manager is Started. -func Add(mgr manager.Manager, options Options) error { - return add(mgr, newReconciler(mgr, options)) -} - -// newReconciler returns a new reconcile.Reconciler -func newReconciler(mgr manager.Manager, options Options) reconcile.Reconciler { - return &UpdateServiceReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - operandImage: options.OperandImage, - } -} - -// add adds a new Controller to mgr with r as the reconcile.Reconciler -func add(mgr manager.Manager, r reconcile.Reconciler) error { - // Create a new controller - c, err := controller.New("updateservice-controller", mgr, controller.Options{Reconciler: r}) - if err != nil { - return err - } - - // Watch for changes to set metrics - err = c.Watch(&source.Kind{Type: &cv1.UpdateService{}}, &ophandler.InstrumentedEnqueueRequestForObject{}) - if err != nil { - return err - } - - // Watch for changes to primary resource UpdateService - err = c.Watch(&source.Kind{Type: &cv1.UpdateService{}}, &handler.EnqueueRequestForObject{}) - if err != nil { - return err - } - - // Watch for changes to secondary resources - for _, obj := range []runtime.Object{ - &appsv1.Deployment{}, - &corev1.ConfigMap{}, - &corev1.Service{}, - &policyv1beta1.PodDisruptionBudget{}, - &routev1.Route{}, - } { - err = c.Watch(&source.Kind{Type: obj}, &handler.EnqueueRequestForOwner{ - IsController: true, - OwnerType: &cv1.UpdateService{}, - }) - if err != nil { - return err - } - } - - // Watch for all Image changes, only Reconcile when image found is named defaults.ImageConfigName and is at cluster level - err = c.Watch(&source.Kind{Type: &apicfgv1.Image{}}, - &handler.EnqueueRequestsFromMapFunc{ToRequests: &mapper{mgr.GetClient()}}, - predicate.GenerationChangedPredicate{}) - if err != nil { - log.Error(err, "Error watching ImageConfig API") - return err - } - - //Watch for all ConfigMap changes, only Reconcile when name == Image.Spec.AdditionalTrustedCA.Name - err = c.Watch(&source.Kind{Type: &corev1.ConfigMap{}}, - &handler.EnqueueRequestsFromMapFunc{ToRequests: &mapper{mgr.GetClient()}}, - ) - if err != nil { - log.Error(err, "Error watching ConfigMap API") - return err - } - - return nil -} - // blank assignment to verify that ReconcileUpdateService implements reconcile.Reconciler var _ reconcile.Reconciler = &UpdateServiceReconciler{} @@ -120,7 +37,8 @@ var _ reconcile.Reconciler = &UpdateServiceReconciler{} type UpdateServiceReconciler struct { Client client.Client Scheme *runtime.Scheme - operandImage string + Log logr.Logger + OperandImage string } // +kubebuilder:rbac:groups="",resources=pods,verbs=get @@ -182,7 +100,7 @@ func (r *UpdateServiceReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro // 'newKubeResources' creates all the kube resources we need and holds // them in 'resources' as the canonical reference for those resources // during reconciliation. - resources, err := newKubeResources(instanceCopy, r.operandImage, ps, cm) + resources, err := newKubeResources(instanceCopy, r.OperandImage, ps, cm) if err != nil { reqLogger.Error(err, "Failed to render resources") return ctrl.Result{}, err @@ -664,9 +582,23 @@ func (r *UpdateServiceReconciler) ensureSecret(ctx context.Context, reqLogger lo return nil } -/* func (r *UpdateServiceReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&cv1.UpdateService{}). + mapped := &mapper{mgr.GetClient()} + + return ctrl.NewControllerManagedBy(mgr). + For(&cv1.UpdateService{}). + Owns(&appsv1.Deployment{}). + Owns(&corev1.ConfigMap{}). + Owns(&corev1.Service{}). + Owns(&policyv1beta1.PodDisruptionBudget{}). + Owns(&routev1.Route{}). + Watches( + &source.Kind{Type: &apicfgv1.Image{}}, + &handler.EnqueueRequestsFromMapFunc{ToRequests: mapped}, + ). + Watches( + &source.Kind{Type: &corev1.ConfigMap{}}, + &handler.EnqueueRequestsFromMapFunc{ToRequests: mapped}, + ). + Complete(r) } -*/ diff --git a/main.go b/main.go index 18eb71c1f..9c78b32ca 100644 --- a/main.go +++ b/main.go @@ -1,32 +1,29 @@ package main import ( - "context" "errors" "flag" "fmt" "os" "runtime" - "strings" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + apiruntime "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" _ "k8s.io/client-go/plugin/pkg/client/auth" - "github.com/operator-framework/operator-lib/leader" - "sigs.k8s.io/controller-runtime/pkg/cache" - "sigs.k8s.io/controller-runtime/pkg/client/config" - "sigs.k8s.io/controller-runtime/pkg/manager" - configv1 "github.com/openshift/api/config/v1" routev1 "github.com/openshift/api/route/v1" updateservicev1 "github.com/openshift/cincinnati-operator/api/v1" - uscontroller "github.com/openshift/cincinnati-operator/controllers" + "github.com/openshift/cincinnati-operator/controllers" "github.com/openshift/cincinnati-operator/version" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/log/zap" + // +kubebuilder:scaffold:imports ) // Change below variables to serve metrics on different host or port. @@ -37,9 +34,18 @@ var ( ) var ( - log = ctrl.Log.WithName("cmd") + scheme = apiruntime.NewScheme() + log = ctrl.Log.WithName("cmd") ) +func init() { + utilruntime.Must(configv1.AddToScheme(scheme)) + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(routev1.AddToScheme(scheme)) + utilruntime.Must(updateservicev1.AddToScheme(scheme)) + // +kubebuilder:scaffold:scheme +} + func printVersion() { log.Info(fmt.Sprintf("Operator Version: %s", version.Version)) log.Info(fmt.Sprintf("Go Version: %s", runtime.Version())) @@ -73,85 +79,39 @@ func main() { printVersion() - namespace, err := getWatchNamespace() + _, err := getWatchNamespace() if err != nil { log.Error(err, "unable to get WatchNamespace; unable to start manager") os.Exit(1) } - log.Info(fmt.Sprintf("Namespace: %s", namespace)) - - // Get a config to talk to the apiserver - cfg, err := config.GetConfig() - if err != nil { - log.Error(err, "") - os.Exit(1) - } - - ctx := context.TODO() - // Become the leader before proceeding - err = leader.Become(ctx, "updateservice-operator-lock") - if err != nil { - log.Error(err, "Could not become leader") - os.Exit(1) - } - - // Set default manager options - options := manager.Options{ - Namespace: namespace, - MetricsBindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort), - } - - // Add support for MultiNamespace set in WATCH_NAMESPACE (e.g ns1,ns2) - // Note that this is not intended to be used for excluding namespaces, this is better done via a Predicate - // Also note that you may face performance issues when using this with a high number of namespaces. - // More Info: https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/cache#MultiNamespacedCacheBuilder - if strings.Contains(namespace, ",") { - options.Namespace = "" - options.NewCache = cache.MultiNamespacedCacheBuilder(strings.Split(namespace, ",")) - } - // Create a new manager to provide shared dependencies and start components - mgr, err := manager.New(cfg, options) + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + MetricsBindAddress: metricsAddr, + Port: 9443, + LeaderElection: enableLeaderElection, + LeaderElectionID: "2b19ccee.openshift.io", + }) if err != nil { - log.Error(err, "") - os.Exit(1) - } - - log.Info("Registering Components.") - - // Setup Scheme for all resources - if err := updateservicev1.AddToScheme(mgr.GetScheme()); err != nil { - log.Error(err, "") - os.Exit(1) - } - - if err := configv1.Install(mgr.GetScheme()); err != nil { - log.Error(err, "") + log.Error(err, "unable to start manager") os.Exit(1) } - if err := routev1.Install(mgr.GetScheme()); err != nil { - log.Error(err, "") - os.Exit(1) - } - - opts := uscontroller.Options{ - OperandImage: os.Getenv("OPERAND_IMAGE"), - } - if opts.OperandImage == "" { + operandImage := os.Getenv("OPERAND_IMAGE") + if operandImage == "" { log.Error(errors.New("Must set envvar OPERAND_IMAGE"), "") os.Exit(1) } - // Usually this is done with an init() function in the package, but since we - // need to provide input, we're doing it here instead. - uscontroller.AddToManagerFuncs = append(uscontroller.AddToManagerFuncs, func(mgr manager.Manager) error { - return uscontroller.Add(mgr, opts) - }) - // Setup UpdateService controllers - if err := uscontroller.AddToManager(mgr); err != nil { - log.Error(err, "") + if err = (&controllers.UpdateServiceReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("UpdateService"), + Scheme: mgr.GetScheme(), + OperandImage: operandImage, + }).SetupWithManager(mgr); err != nil { + log.Error(err, "unable to create controller", "controller", "UpdateService") os.Exit(1) } + // +kubebuilder:scaffold:builder log.Info("Starting the Cmd.")