From 4f3c2036774fb3a64da14f7c9ca863923db0d7fd Mon Sep 17 00:00:00 2001 From: Aditya Bhatia Date: Fri, 20 Jan 2023 17:03:58 +0100 Subject: [PATCH 1/8] feat: helm reference --- PROJECT | 15 +- api/v1alpha1/samplehelm_types.go | 96 ++++++ api/v1alpha1/zz_generated.deepcopy.go | 97 +++++++ .../operator.kyma-project.io_samplehelms.yaml | 132 +++++++++ config/crd/kustomization.yaml | 3 + .../patches/cainjection_in_samplehelms.yaml | 7 + .../crd/patches/webhook_in_samplehelms.yaml | 16 + config/rbac/role.yaml | 26 ++ config/rbac/samplehelm_editor_role.yaml | 31 ++ config/rbac/samplehelm_viewer_role.yaml | 27 ++ ...r.kyma-project.io_v1alpha1_samplehelm.yaml | 12 + controllers/common_utils.go | 17 ++ controllers/rest_getter_proxy.go | 58 ++++ .../sample_controller_rendered_resources.go | 14 - controllers/sample_local_helm_controller.go | 274 ++++++++++++++++++ controllers/suite_test.go | 4 + go.mod | 13 +- go.sum | 27 +- main.go | 9 + 19 files changed, 845 insertions(+), 33 deletions(-) create mode 100644 api/v1alpha1/samplehelm_types.go create mode 100644 config/crd/bases/operator.kyma-project.io_samplehelms.yaml create mode 100644 config/crd/patches/cainjection_in_samplehelms.yaml create mode 100644 config/crd/patches/webhook_in_samplehelms.yaml create mode 100644 config/rbac/samplehelm_editor_role.yaml create mode 100644 config/rbac/samplehelm_viewer_role.yaml create mode 100644 config/samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml create mode 100644 controllers/common_utils.go create mode 100644 controllers/rest_getter_proxy.go create mode 100644 controllers/sample_local_helm_controller.go diff --git a/PROJECT b/PROJECT index 78a5c27..2021390 100644 --- a/PROJECT +++ b/PROJECT @@ -1,3 +1,7 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html domain: kyma-project.io layout: - go.kubebuilder.io/v3 @@ -11,8 +15,17 @@ resources: namespaced: true controller: true domain: kyma-project.io - group: component + group: operator.kyma-project.io kind: Sample path: github.com/kyma-project/template-operator/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: kyma-project.io + group: operator.kyma-project.io + kind: SampleHelm + path: github.com/kyma-project/template-operator/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/api/v1alpha1/samplehelm_types.go b/api/v1alpha1/samplehelm_types.go new file mode 100644 index 0000000..1b6ed1e --- /dev/null +++ b/api/v1alpha1/samplehelm_types.go @@ -0,0 +1,96 @@ +/* +Copyright 2022. + +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 v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// SampleHelmSpec defines the desired state of SampleHelm +type SampleHelmSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // ChartPath represents the local path to the Helm chart + ChartPath string `json:"chartPath,omitempty"` +} + +// SampleHelmStatus defines the observed state of SampleHelm +type SampleHelmStatus struct { + Status `json:",inline"` + + // Conditions contain a set of conditionals to determine the State of Status. + // If all Conditions are met, State is expected to be in StateReady. + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // add other fields to status subresource here +} + +func (s *SampleHelmStatus) WithState(state State) *SampleHelmStatus { + s.State = state + return s +} + +func (s *SampleHelmStatus) WithInstallConditionStatus(status metav1.ConditionStatus, objGeneration int64) *SampleHelmStatus { + if s.Conditions == nil { + s.Conditions = make([]metav1.Condition, 0, 1) + } + + condition := meta.FindStatusCondition(s.Conditions, ConditionTypeInstallation) + + if condition == nil { + condition = &metav1.Condition{ + Type: ConditionTypeInstallation, + Reason: ConditionReasonReady, + Message: "installation is ready and resources can be used", + } + } + + condition.Status = status + condition.ObservedGeneration = objGeneration + meta.SetStatusCondition(&s.Conditions, *condition) + return s +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// SampleHelm is the Schema for the samplehelms API +type SampleHelm struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec SampleHelmSpec `json:"spec,omitempty"` + Status SampleHelmStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// SampleHelmList contains a list of SampleHelm +type SampleHelmList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []SampleHelm `json:"items"` +} + +func init() { + SchemeBuilder.Register(&SampleHelm{}, &SampleHelmList{}) +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 5c9f1fe..9ac0c09 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -53,6 +53,103 @@ func (in *Sample) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SampleHelm) DeepCopyInto(out *SampleHelm) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SampleHelm. +func (in *SampleHelm) DeepCopy() *SampleHelm { + if in == nil { + return nil + } + out := new(SampleHelm) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SampleHelm) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SampleHelmList) DeepCopyInto(out *SampleHelmList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]SampleHelm, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SampleHelmList. +func (in *SampleHelmList) DeepCopy() *SampleHelmList { + if in == nil { + return nil + } + out := new(SampleHelmList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SampleHelmList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SampleHelmSpec) DeepCopyInto(out *SampleHelmSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SampleHelmSpec. +func (in *SampleHelmSpec) DeepCopy() *SampleHelmSpec { + if in == nil { + return nil + } + out := new(SampleHelmSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SampleHelmStatus) DeepCopyInto(out *SampleHelmStatus) { + *out = *in + out.Status = in.Status + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SampleHelmStatus. +func (in *SampleHelmStatus) DeepCopy() *SampleHelmStatus { + if in == nil { + return nil + } + out := new(SampleHelmStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SampleList) DeepCopyInto(out *SampleList) { *out = *in diff --git a/config/crd/bases/operator.kyma-project.io_samplehelms.yaml b/config/crd/bases/operator.kyma-project.io_samplehelms.yaml new file mode 100644 index 0000000..5f47fd2 --- /dev/null +++ b/config/crd/bases/operator.kyma-project.io_samplehelms.yaml @@ -0,0 +1,132 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: samplehelms.operator.kyma-project.io +spec: + group: operator.kyma-project.io + names: + kind: SampleHelm + listKind: SampleHelmList + plural: samplehelms + singular: samplehelm + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: SampleHelm is the Schema for the samplehelms API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: SampleHelmSpec defines the desired state of SampleHelm + properties: + chartPath: + description: ChartPath represents the local path to the Helm chart + type: string + type: object + status: + description: SampleHelmStatus defines the observed state of SampleHelm + properties: + conditions: + description: Conditions contain a set of conditionals to determine + the State of Status. If all Conditions are met, State is expected + to be in StateReady. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + state: + description: State signifies current state of Module CR. Value can + be one of ("Ready", "Processing", "Error", "Deleting"). + enum: + - Processing + - Deleting + - Ready + - Error + type: string + required: + - state + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 4ac43f2..b7e4f3a 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -6,17 +6,20 @@ kind: Component # It should be run by config/default resources: - bases/operator.kyma-project.io_samples.yaml +- bases/operator.kyma-project.io.kyma-project.io_samplehelms.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD #- patches/webhook_in_samples.yaml +#- patches/webhook_in_samplehelms.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD #- patches/cainjection_in_samples.yaml +#- patches/cainjection_in_samplehelms.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_samplehelms.yaml b/config/crd/patches/cainjection_in_samplehelms.yaml new file mode 100644 index 0000000..0b18807 --- /dev/null +++ b/config/crd/patches/cainjection_in_samplehelms.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: samplehelms.operator.kyma-project.io.kyma-project.io diff --git a/config/crd/patches/webhook_in_samplehelms.yaml b/config/crd/patches/webhook_in_samplehelms.yaml new file mode 100644 index 0000000..50eec45 --- /dev/null +++ b/config/crd/patches/webhook_in_samplehelms.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: samplehelms.operator.kyma-project.io.kyma-project.io +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index d930480..b0cbc14 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -59,3 +59,29 @@ rules: - get - patch - update +- apiGroups: + - operator.kyma-project.io.kyma-project.io + resources: + - samplehelms + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - operator.kyma-project.io.kyma-project.io + resources: + - samplehelms/finalizers + verbs: + - update +- apiGroups: + - operator.kyma-project.io.kyma-project.io + resources: + - samplehelms/status + verbs: + - get + - patch + - update diff --git a/config/rbac/samplehelm_editor_role.yaml b/config/rbac/samplehelm_editor_role.yaml new file mode 100644 index 0000000..720c662 --- /dev/null +++ b/config/rbac/samplehelm_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit samplehelms. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: samplehelm-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: template-operator + app.kubernetes.io/part-of: template-operator + app.kubernetes.io/managed-by: kustomize + name: samplehelm-editor-role +rules: +- apiGroups: + - operator.kyma-project.io.kyma-project.io + resources: + - samplehelms + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - operator.kyma-project.io.kyma-project.io + resources: + - samplehelms/status + verbs: + - get diff --git a/config/rbac/samplehelm_viewer_role.yaml b/config/rbac/samplehelm_viewer_role.yaml new file mode 100644 index 0000000..c69b987 --- /dev/null +++ b/config/rbac/samplehelm_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view samplehelms. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: samplehelm-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: template-operator + app.kubernetes.io/part-of: template-operator + app.kubernetes.io/managed-by: kustomize + name: samplehelm-viewer-role +rules: +- apiGroups: + - operator.kyma-project.io.kyma-project.io + resources: + - samplehelms + verbs: + - get + - list + - watch +- apiGroups: + - operator.kyma-project.io.kyma-project.io + resources: + - samplehelms/status + verbs: + - get diff --git a/config/samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml b/config/samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml new file mode 100644 index 0000000..a95181b --- /dev/null +++ b/config/samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml @@ -0,0 +1,12 @@ +apiVersion: operator.kyma-project.io.kyma-project.io/v1alpha1 +kind: SampleHelm +metadata: + labels: + app.kubernetes.io/name: samplehelm + app.kubernetes.io/instance: samplehelm-sample + app.kubernetes.io/part-of: template-operator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: template-operator + name: samplehelm-sample +spec: + # TODO(user): Add fields here diff --git a/controllers/common_utils.go b/controllers/common_utils.go new file mode 100644 index 0000000..a7bbaef --- /dev/null +++ b/controllers/common_utils.go @@ -0,0 +1,17 @@ +package controllers + +import "time" + +type RateLimiter struct { + Burst int + Frequency int + BaseDelay time.Duration + FailureMaxDelay time.Duration +} + +const ( + requeueInterval = time.Second * 3 + finalizer = "sample.kyma-project.io/finalizer" + debugLogLevel = 2 + fieldOwner = "sample.kyma-project.io/owner" +) diff --git a/controllers/rest_getter_proxy.go b/controllers/rest_getter_proxy.go new file mode 100644 index 0000000..2088d18 --- /dev/null +++ b/controllers/rest_getter_proxy.go @@ -0,0 +1,58 @@ +package controllers + +import ( + _ "helm.sh/helm/v3/pkg/action" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/client-go/discovery" + memory "k8s.io/client-go/discovery/cached" + "k8s.io/client-go/rest" + "k8s.io/client-go/restmapper" + "k8s.io/client-go/tools/clientcmd" +) + +type RESTClientGetter struct { + config *rest.Config +} + +func NewRESTClientGetter(config *rest.Config) *RESTClientGetter { + return &RESTClientGetter{ + config: config, + } +} + +func (r *RESTClientGetter) ToRESTConfig() (*rest.Config, error) { + return r.config, nil +} + +func (r *RESTClientGetter) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) { + config, err := r.ToRESTConfig() + if err != nil { + return nil, err + } + + // The more groups you have, the more discovery requests you need to make. + // given 25 groups (our groups + a few custom conf) with one-ish version each, discovery needs to make 50 requests + // double it just so we don't end up here again for a while. This config is only used for discovery. + config.Burst = 100 + + discoveryClient, _ := discovery.NewDiscoveryClientForConfig(config) + return memory.NewMemCacheClient(discoveryClient), nil +} + +func (r *RESTClientGetter) ToRESTMapper() (meta.RESTMapper, error) { + discoveryClient, err := r.ToDiscoveryClient() + if err != nil { + return nil, err + } + + mapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient) + expander := restmapper.NewShortcutExpander(mapper, discoveryClient) + return expander, nil +} + +func (r *RESTClientGetter) ToRawKubeConfigLoader() clientcmd.ClientConfig { + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig + overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmd.ClusterDefaults} + return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides) +} diff --git a/controllers/sample_controller_rendered_resources.go b/controllers/sample_controller_rendered_resources.go index 25e2333..e7e5507 100644 --- a/controllers/sample_controller_rendered_resources.go +++ b/controllers/sample_controller_rendered_resources.go @@ -52,13 +52,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/ratelimiter" ) -const ( - requeueInterval = time.Second * 3 - finalizer = "sample.kyma-project.io/finalizer" - debugLogLevel = 2 - fieldOwner = "sample.kyma-project.io/owner" -) - // SampleReconciler reconciles a Sample object type SampleReconciler struct { client.Client @@ -68,13 +61,6 @@ type SampleReconciler struct { record.EventRecorder } -type RateLimiter struct { - Burst int - Frequency int - BaseDelay time.Duration - FailureMaxDelay time.Duration -} - type ManifestResources struct { Items []*unstructured.Unstructured Blobs [][]byte diff --git a/controllers/sample_local_helm_controller.go b/controllers/sample_local_helm_controller.go new file mode 100644 index 0000000..f4c2ba5 --- /dev/null +++ b/controllers/sample_local_helm_controller.go @@ -0,0 +1,274 @@ +/* +Copyright 2022. + +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 controllers + +import ( + "bufio" + "bytes" + "context" + "errors" + "fmt" + "io" + "strings" + + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/cli" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + yamlUtil "k8s.io/apimachinery/pkg/util/yaml" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/yaml" + + "helm.sh/helm/v3/pkg/chart/loader" + + "github.com/kyma-project/template-operator/api/v1alpha1" +) + +// SampleHelmReconciler reconciles a SampleHelm object +type SampleHelmReconciler struct { + client.Client + Scheme *runtime.Scheme + // EventRecorder for creating k8s events + record.EventRecorder + + Config *rest.Config +} + +type ManifestResources struct { + Items []*unstructured.Unstructured +} + +//+kubebuilder:rbac:groups=operator.kyma-project.io.kyma-project.io,resources=samplehelms,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=operator.kyma-project.io.kyma-project.io,resources=samplehelms/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=operator.kyma-project.io.kyma-project.io,resources=samplehelms/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the SampleHelm object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.14.1/pkg/reconcile +func (r *SampleHelmReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + + objectInstance := v1alpha1.SampleHelm{} + + if err := r.Client.Get(ctx, req.NamespacedName, &objectInstance); err != nil { + // we'll ignore not-found errors, since they can't be fixed by an immediate + // requeue (we'll need to wait for a new notification), and we can get them + // on deleted requests. + logger.Info(req.NamespacedName.String() + " got deleted!") + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + status := objectInstance.Status + + // check if deletionTimestamp is set, retry until it gets deleted + // set state to Deleting if not set for an object with deletion timestamp + if !objectInstance.GetDeletionTimestamp().IsZero() && + status.State != v1alpha1.StateDeleting { + // if the status is not yet set to deleting, also update the status + return ctrl.Result{}, r.setStatusForObjectInstance(ctx, &objectInstance, status.WithState(v1alpha1.StateDeleting)) + } + + // add finalizer if not present + if controllerutil.AddFinalizer(&objectInstance, finalizer) { + return ctrl.Result{}, r.ssa(ctx, &objectInstance) + } + + switch status.State { + case "": + return ctrl.Result{}, r.HandleInitialState(ctx, &objectInstance) + case v1alpha1.StateProcessing: + return ctrl.Result{Requeue: true}, r.HandleProcessingState(ctx, &objectInstance) + case v1alpha1.StateDeleting: + return ctrl.Result{Requeue: true}, r.HandleDeletingState(ctx, &objectInstance) + case v1alpha1.StateError: + return ctrl.Result{Requeue: true}, r.HandleErrorState(ctx, &objectInstance) + case v1alpha1.StateReady: + return ctrl.Result{RequeueAfter: requeueInterval}, r.HandleReadyState(ctx, &objectInstance) + } + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *SampleHelmReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&v1alpha1.SampleHelm{}). + Complete(r) +} + +func (r *SampleHelmReconciler) setStatusForObjectInstance(ctx context.Context, objectInstance *v1alpha1.SampleHelm, + status *v1alpha1.SampleHelmStatus, +) error { + objectInstance.Status = *status + + if err := r.ssaStatus(ctx, objectInstance); err != nil { + r.Event(objectInstance, "Warning", "ErrorUpdatingStatus", fmt.Sprintf("updating state to %v", string(status.State))) + return fmt.Errorf("error while updating status %s to: %w", status.State, err) + } + + r.Event(objectInstance, "Normal", "StatusUpdated", fmt.Sprintf("updating state to %v", string(status.State))) + return nil +} + +// ssaStatus patches status using SSA on the passed object +func (r *SampleHelmReconciler) ssaStatus(ctx context.Context, obj client.Object) error { + obj.SetManagedFields(nil) + obj.SetResourceVersion("") + return r.Status().Patch(ctx, obj, client.Apply, + &client.SubResourcePatchOptions{PatchOptions: client.PatchOptions{FieldManager: fieldOwner}}) +} + +// ssaStatus patches the object using SSA +func (r *SampleHelmReconciler) ssa(ctx context.Context, obj client.Object) error { + obj.SetManagedFields(nil) + obj.SetResourceVersion("") + return r.Patch(ctx, obj, client.Apply, client.ForceOwnership, client.FieldOwner(fieldOwner)) +} + +func (r *SampleHelmReconciler) Render(ctx context.Context, obj *v1alpha1.SampleHelm) (*ManifestResources, error) { + //status := obj.Status + logger := log.FromContext(ctx) + + actionConfig := new(action.Configuration) + settings := cli.New() + if err := actionConfig.Init(NewRESTClientGetter(r.Config), settings.Namespace(), "secrets", func(format string, v ...interface{}) { + format = fmt.Sprintf("[debug] %s\n", format) + debugLevel := 2 + logger.V(debugLevel).Info(fmt.Sprintf(format, v...)) + }); err != nil { + logger.Error(err, "") + } + + actionClient := action.NewInstall(actionConfig) + + // Helm flags + r.SetDefaultClientConfig(actionClient, "sample-release-name") + + // Helm values + valuesAsMap := map[string]interface{}{ + "label": "custom-label-from-controller", + } + + chrt, err := loader.Load(obj.Spec.ChartPath) + if err != nil { + return nil, err + } + + release, err := actionClient.RunWithContext(ctx, chrt, valuesAsMap) + if err != nil { + return nil, err + } + + return parseManifestStringToObjects(release.Manifest) +} + +func (r *SampleHelmReconciler) SetDefaultClientConfig(actionClient *action.Install, releaseName string) { + actionClient.DryRun = true + actionClient.Atomic = false + actionClient.Wait = false + actionClient.WaitForJobs = false + actionClient.Replace = true // Skip the name check + actionClient.IncludeCRDs = true // include CRDs in the templated output + actionClient.ClientOnly = true + actionClient.ReleaseName = releaseName + actionClient.Namespace = v1.NamespaceDefault + + // default versioning if unspecified + if actionClient.Version == "" && actionClient.Devel { + actionClient.Version = ">0.0.0-0" + } +} + +func parseManifestStringToObjects(manifest string) (*ManifestResources, error) { + objects := &ManifestResources{} + reader := yamlUtil.NewYAMLReader(bufio.NewReader(strings.NewReader(manifest))) + for { + rawBytes, err := reader.Read() + if err != nil { + if errors.Is(err, io.EOF) { + return objects, nil + } + + return nil, fmt.Errorf("invalid YAML doc: %w", err) + } + + rawBytes = bytes.TrimSpace(rawBytes) + unstructuredObj := unstructured.Unstructured{} + if err := yaml.Unmarshal(rawBytes, &unstructuredObj); err != nil { + return nil, err + } + + if len(rawBytes) == 0 || bytes.Equal(rawBytes, []byte("null")) || len(unstructuredObj.Object) == 0 { + continue + } + + objects.Items = append(objects.Items, &unstructuredObj) + } +} + +func (r *SampleHelmReconciler) HandleInitialState(ctx context.Context, v *v1alpha1.SampleHelm) error { + +} + +func (r *SampleHelmReconciler) HandleProcessingState(ctx context.Context, obj *v1alpha1.SampleHelm) error { + resources, err := r.Render(ctx, obj) + if err != nil { + return err + } + + installErrs := make([]error, 0) + + // instead of looping a concurrent mechanism can also be implemented + for _, resource := range resources.Items { + err = r.ssa(ctx, resource) + if err != nil { + installErrs = append(installErrs, err) + } + } + + if len(installErrs) != 0 { + buf := &bytes.Buffer{} + for _, err := range installErrs { + _, _ = fmt.Fprintf(buf, "%v\n", err.Error()) + } + return fmt.Errorf(buf.String()) + } +} + +func (r *SampleHelmReconciler) HandleDeletingState(ctx context.Context, v *v1alpha1.SampleHelm) error { + +} + +func (r *SampleHelmReconciler) HandleErrorState(ctx context.Context, v *v1alpha1.SampleHelm) error { + +} + +func (r *SampleHelmReconciler) HandleReadyState(ctx context.Context, v *v1alpha1.SampleHelm) error { + +} diff --git a/controllers/suite_test.go b/controllers/suite_test.go index e7d900b..5d89a5f 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -36,6 +36,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" componentv1alpha1 "github.com/kyma-project/template-operator/api/v1alpha1" + operatorkymaprojectiov1alpha1 "github.com/kyma-project/template-operator/api/v1alpha1" //+kubebuilder:scaffold:imports ) @@ -89,6 +90,9 @@ var _ = BeforeSuite(func() { err = componentv1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) + err = operatorkymaprojectiov1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + //+kubebuilder:scaffold:scheme k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) diff --git a/go.mod b/go.mod index 5c36459..e07a94e 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/onsi/ginkgo/v2 v2.6.0 github.com/onsi/gomega v1.24.1 golang.org/x/time v0.3.0 + helm.sh/helm/v3 v3.11.0 k8s.io/api v0.26.0 k8s.io/apimachinery v0.26.0 k8s.io/client-go v0.26.0 @@ -15,11 +16,11 @@ require ( ) require ( + github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect - github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-logr/zapr v1.2.3 // indirect @@ -38,7 +39,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect @@ -52,11 +53,11 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 // indirect + golang.org/x/net v0.5.0 // indirect golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 // indirect - golang.org/x/sys v0.3.0 // indirect - golang.org/x/term v0.3.0 // indirect - golang.org/x/text v0.5.0 // indirect + golang.org/x/sys v0.4.0 // indirect + golang.org/x/term v0.4.0 // indirect + golang.org/x/text v0.6.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.1 // indirect diff --git a/go.sum b/go.sum index a9b6e40..6439fa5 100644 --- a/go.sum +++ b/go.sum @@ -33,6 +33,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -74,7 +76,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= @@ -210,8 +211,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= -github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -279,7 +280,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= @@ -374,8 +375,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 h1:Frnccbp+ok2GkUS2tC84yAq/U9Vg+0sIO7aRL3T4Xnc= -golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -437,12 +438,12 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -451,8 +452,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -615,6 +616,8 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +helm.sh/helm/v3 v3.11.0 h1:F+peaCQYbycY1FIqIQ6dAortHd/VzV5FkhMciv4Kf+c= +helm.sh/helm/v3 v3.11.0/go.mod h1:z/Bu/BylToGno/6dtNGuSmjRqxKq5gaH+FU0BPO+AQ8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go index 368d85f..adb0994 100644 --- a/main.go +++ b/main.go @@ -33,6 +33,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" componentv1alpha1 "github.com/kyma-project/template-operator/api/v1alpha1" + operatorkymaprojectiov1alpha1 "github.com/kyma-project/template-operator/api/v1alpha1" "github.com/kyma-project/template-operator/controllers" //+kubebuilder:scaffold:imports ) @@ -66,6 +67,7 @@ func init() { //nolint:gochecknoinits utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(componentv1alpha1.AddToScheme(scheme)) + utilruntime.Must(operatorkymaprojectiov1alpha1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } @@ -107,6 +109,13 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "Sample") os.Exit(1) } + if err = (&controllers.SampleHelmReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "SampleHelm") + os.Exit(1) + } //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { From f386f59e9fed50cced7eb8af34c8116ff7d2170f Mon Sep 17 00:00:00 2001 From: Aditya Bhatia Date: Mon, 23 Jan 2023 16:51:41 +0100 Subject: [PATCH 2/8] tests + controller improvements --- ...r.kyma-project.io_v1alpha1_samplehelm.yaml | 2 +- controllers/common_utils.go | 42 +- .../sample_controller_rendered_resources.go | 56 +-- controllers/sample_local_helm_controller.go | 148 ++++--- .../sample_local_helm_controller_test.go | 107 +++++ controllers/suite_test.go | 24 +- controllers/test/busybox/Chart.yaml | 2 +- .../test/busybox/templates/deployment.yaml | 1 + .../test/busybox/templates/service.yaml | 1 + go.mod | 72 +++- go.sum | 404 ++++++++++++++++++ 11 files changed, 752 insertions(+), 107 deletions(-) create mode 100644 controllers/sample_local_helm_controller_test.go diff --git a/config/samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml b/config/samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml index a95181b..01cc04f 100644 --- a/config/samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml +++ b/config/samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml @@ -9,4 +9,4 @@ metadata: app.kubernetes.io/created-by: template-operator name: samplehelm-sample spec: - # TODO(user): Add fields here + chartPath: "./test/busybox" diff --git a/controllers/common_utils.go b/controllers/common_utils.go index a7bbaef..1bb1604 100644 --- a/controllers/common_utils.go +++ b/controllers/common_utils.go @@ -1,6 +1,18 @@ package controllers -import "time" +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "strings" + "time" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + yamlUtil "k8s.io/apimachinery/pkg/util/yaml" + "sigs.k8s.io/yaml" +) type RateLimiter struct { Burst int @@ -15,3 +27,31 @@ const ( debugLogLevel = 2 fieldOwner = "sample.kyma-project.io/owner" ) + +// parseManifestStringToObjects parses the string of resources into a list of unstructured resources. +func parseManifestStringToObjects(manifest string) (*ManifestResources, error) { + objects := &ManifestResources{} + reader := yamlUtil.NewYAMLReader(bufio.NewReader(strings.NewReader(manifest))) + for { + rawBytes, err := reader.Read() + if err != nil { + if errors.Is(err, io.EOF) { + return objects, nil + } + + return nil, fmt.Errorf("invalid YAML doc: %w", err) + } + + rawBytes = bytes.TrimSpace(rawBytes) + unstructuredObj := unstructured.Unstructured{} + if err := yaml.Unmarshal(rawBytes, &unstructuredObj); err != nil { + objects.Blobs = append(objects.Blobs, append(bytes.TrimPrefix(rawBytes, []byte("---\n")), '\n')) + } + + if len(rawBytes) == 0 || bytes.Equal(rawBytes, []byte("null")) || len(unstructuredObj.Object) == 0 { + continue + } + + objects.Items = append(objects.Items, &unstructuredObj) + } +} diff --git a/controllers/sample_controller_rendered_resources.go b/controllers/sample_controller_rendered_resources.go index e7e5507..31fee9b 100644 --- a/controllers/sample_controller_rendered_resources.go +++ b/controllers/sample_controller_rendered_resources.go @@ -17,16 +17,11 @@ limitations under the License. package controllers import ( - "bufio" - "bytes" "context" - "errors" "fmt" - "io" "io/fs" "os" "path/filepath" - "strings" "time" "github.com/go-logr/logr" @@ -36,7 +31,6 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/sets" - yamlUtil "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/client-go/rest" "k8s.io/client-go/tools/record" "k8s.io/client-go/util/workqueue" @@ -44,7 +38,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/yaml" "github.com/kyma-project/template-operator/api/v1alpha1" @@ -76,7 +69,7 @@ type ManifestResources struct { //+kubebuilder:rbac:groups="*",resources="*",verbs="*" // SetupWithManager sets up the controller with the Manager. -func (r *SampleReconciler) SetupWithManager(mgr ctrl.Manager, rateLimiter RateLimiter, chartPath string) error { +func (r *SampleReconciler) SetupWithManager(mgr ctrl.Manager, rateLimiter RateLimiter) error { r.Config = mgr.GetConfig() return ctrl.NewControllerManagedBy(mgr). @@ -118,7 +111,7 @@ func (r *SampleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } // check if deletionTimestamp is set, retry until it gets deleted - status := getStatusFromObjectInstance(&objectInstance) + status := getStatusFromSample(&objectInstance) // set state to Deleting if not set for an object with deletion timestamp if !objectInstance.GetDeletionTimestamp().IsZero() && @@ -150,7 +143,7 @@ func (r *SampleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr // HandleInitialState bootstraps state handling for the reconciled resource. func (r *SampleReconciler) HandleInitialState(ctx context.Context, objectInstance *v1alpha1.Sample) error { - status := getStatusFromObjectInstance(objectInstance) + status := getStatusFromSample(objectInstance) return r.setStatusForObjectInstance(ctx, objectInstance, status. WithState(v1alpha1.StateProcessing). @@ -160,7 +153,7 @@ func (r *SampleReconciler) HandleInitialState(ctx context.Context, objectInstanc // HandleProcessingState processes the reconciled resource by processing the underlying resources. // Based on the processing either a success or failure state is set on the reconciled resource. func (r *SampleReconciler) HandleProcessingState(ctx context.Context, objectInstance *v1alpha1.Sample) error { - status := getStatusFromObjectInstance(objectInstance) + status := getStatusFromSample(objectInstance) if err := r.processResources(ctx, objectInstance); err != nil { r.Event(objectInstance, "Warning", "ResourcesInstall", err.Error()) return r.setStatusForObjectInstance(ctx, objectInstance, status. @@ -173,8 +166,9 @@ func (r *SampleReconciler) HandleProcessingState(ctx context.Context, objectInst WithInstallConditionStatus(metav1.ConditionTrue, objectInstance.GetGeneration())) } +// HandleErrorState handles error recovery for the reconciled resource. func (r *SampleReconciler) HandleErrorState(ctx context.Context, objectInstance *v1alpha1.Sample) error { - status := getStatusFromObjectInstance(objectInstance) + status := getStatusFromSample(objectInstance) if err := r.processResources(ctx, objectInstance); err != nil { return err } @@ -190,7 +184,7 @@ func (r *SampleReconciler) HandleDeletingState(ctx context.Context, objectInstan r.Event(objectInstance, "Normal", "Deleting", "resource deleting") logger := log.FromContext(ctx) - status := getStatusFromObjectInstance(objectInstance) + status := getStatusFromSample(objectInstance) resourceObjs, err := getResourcesFromLocalPath(objectInstance.Spec.ResourceFilePath, logger) if err != nil && controllerutil.RemoveFinalizer(objectInstance, finalizer) { @@ -203,7 +197,7 @@ func (r *SampleReconciler) HandleDeletingState(ctx context.Context, objectInstan // so please make sure the types are available on the target cluster for _, obj := range resourceObjs.Items { if err = r.Client.Delete(ctx, obj); err != nil && !errors2.IsNotFound(err) { - logger.Error(err, "error during installation of resources") + logger.Error(err, "error during uninstallation of resources") r.Event(objectInstance, "Warning", "ResourcesDelete", "deleting resources error") return r.setStatusForObjectInstance(ctx, objectInstance, status. WithState(v1alpha1.StateError). @@ -220,7 +214,7 @@ func (r *SampleReconciler) HandleDeletingState(ctx context.Context, objectInstan // HandleReadyState checks for the consistency of reconciled resource, by verifying the underlying resources. func (r *SampleReconciler) HandleReadyState(ctx context.Context, objectInstance *v1alpha1.Sample) error { - status := getStatusFromObjectInstance(objectInstance) + status := getStatusFromSample(objectInstance) if err := r.processResources(ctx, objectInstance); err != nil { r.Event(objectInstance, "Warning", "ResourcesInstall", err.Error()) return r.setStatusForObjectInstance(ctx, objectInstance, status. @@ -266,7 +260,7 @@ func (r *SampleReconciler) processResources(ctx context.Context, objectInstance return nil } -func getStatusFromObjectInstance(objectInstance *v1alpha1.Sample) v1alpha1.SampleStatus { +func getStatusFromSample(objectInstance *v1alpha1.Sample) v1alpha1.SampleStatus { return objectInstance.Status } @@ -310,34 +304,6 @@ func getResourcesFromLocalPath(dirPath string, logger logr.Logger) (*ManifestRes return parseManifestStringToObjects(string(fileBytes)) } -// parseManifestStringToObjects parses the string of resources into a list of unstructured resources. -func parseManifestStringToObjects(manifest string) (*ManifestResources, error) { - objects := &ManifestResources{} - reader := yamlUtil.NewYAMLReader(bufio.NewReader(strings.NewReader(manifest))) - for { - rawBytes, err := reader.Read() - if err != nil { - if errors.Is(err, io.EOF) { - return objects, nil - } - - return nil, fmt.Errorf("invalid YAML doc: %w", err) - } - - rawBytes = bytes.TrimSpace(rawBytes) - unstructuredObj := unstructured.Unstructured{} - if err := yaml.Unmarshal(rawBytes, &unstructuredObj); err != nil { - objects.Blobs = append(objects.Blobs, append(bytes.TrimPrefix(rawBytes, []byte("---\n")), '\n')) - } - - if len(rawBytes) == 0 || bytes.Equal(rawBytes, []byte("null")) || len(unstructuredObj.Object) == 0 { - continue - } - - objects.Items = append(objects.Items, &unstructuredObj) - } -} - // ssaStatus patches status using SSA on the passed object func (r *SampleReconciler) ssaStatus(ctx context.Context, obj client.Object) error { obj.SetManagedFields(nil) @@ -346,7 +312,7 @@ func (r *SampleReconciler) ssaStatus(ctx context.Context, obj client.Object) err &client.SubResourcePatchOptions{PatchOptions: client.PatchOptions{FieldManager: fieldOwner}}) } -// ssaStatus patches the object using SSA +// ssa patches the object using SSA func (r *SampleReconciler) ssa(ctx context.Context, obj client.Object) error { obj.SetManagedFields(nil) obj.SetResourceVersion("") diff --git a/controllers/sample_local_helm_controller.go b/controllers/sample_local_helm_controller.go index f4c2ba5..5ec2cf4 100644 --- a/controllers/sample_local_helm_controller.go +++ b/controllers/sample_local_helm_controller.go @@ -17,29 +17,23 @@ limitations under the License. package controllers import ( - "bufio" "bytes" "context" - "errors" "fmt" - "io" - "strings" "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/cli" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + errors2 "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - yamlUtil "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/client-go/rest" "k8s.io/client-go/tools/record" 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/log" - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/pkg/chart/loader" "github.com/kyma-project/template-operator/api/v1alpha1" ) @@ -54,10 +48,6 @@ type SampleHelmReconciler struct { Config *rest.Config } -type ManifestResources struct { - Items []*unstructured.Unstructured -} - //+kubebuilder:rbac:groups=operator.kyma-project.io.kyma-project.io,resources=samplehelms,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=operator.kyma-project.io.kyma-project.io,resources=samplehelms/status,verbs=get;update;patch //+kubebuilder:rbac:groups=operator.kyma-project.io.kyma-project.io,resources=samplehelms/finalizers,verbs=update @@ -116,9 +106,17 @@ func (r *SampleHelmReconciler) Reconcile(ctx context.Context, req ctrl.Request) } // SetupWithManager sets up the controller with the Manager. -func (r *SampleHelmReconciler) SetupWithManager(mgr ctrl.Manager) error { +func (r *SampleHelmReconciler) SetupWithManager(mgr ctrl.Manager, rateLimiter RateLimiter) error { return ctrl.NewControllerManagedBy(mgr). For(&v1alpha1.SampleHelm{}). + WithOptions(controller.Options{ + RateLimiter: TemplateRateLimiter( + rateLimiter.BaseDelay, + rateLimiter.FailureMaxDelay, + rateLimiter.Frequency, + rateLimiter.Burst, + ), + }). Complete(r) } @@ -144,7 +142,7 @@ func (r *SampleHelmReconciler) ssaStatus(ctx context.Context, obj client.Object) &client.SubResourcePatchOptions{PatchOptions: client.PatchOptions{FieldManager: fieldOwner}}) } -// ssaStatus patches the object using SSA +// ssa patches the object using SSA func (r *SampleHelmReconciler) ssa(ctx context.Context, obj client.Object) error { obj.SetManagedFields(nil) obj.SetResourceVersion("") @@ -197,7 +195,7 @@ func (r *SampleHelmReconciler) SetDefaultClientConfig(actionClient *action.Insta actionClient.IncludeCRDs = true // include CRDs in the templated output actionClient.ClientOnly = true actionClient.ReleaseName = releaseName - actionClient.Namespace = v1.NamespaceDefault + actionClient.Namespace = metav1.NamespaceDefault // default versioning if unspecified if actionClient.Version == "" && actionClient.Devel { @@ -205,39 +203,97 @@ func (r *SampleHelmReconciler) SetDefaultClientConfig(actionClient *action.Insta } } -func parseManifestStringToObjects(manifest string) (*ManifestResources, error) { - objects := &ManifestResources{} - reader := yamlUtil.NewYAMLReader(bufio.NewReader(strings.NewReader(manifest))) - for { - rawBytes, err := reader.Read() - if err != nil { - if errors.Is(err, io.EOF) { - return objects, nil - } +func (r *SampleHelmReconciler) HandleInitialState(ctx context.Context, objectInstance *v1alpha1.SampleHelm) error { + status := objectInstance.Status - return nil, fmt.Errorf("invalid YAML doc: %w", err) - } + return r.setStatusForObjectInstance(ctx, objectInstance, status. + WithState(v1alpha1.StateProcessing). + WithInstallConditionStatus(metav1.ConditionUnknown, objectInstance.GetGeneration())) +} + +func (r *SampleHelmReconciler) HandleProcessingState(ctx context.Context, objectInstance *v1alpha1.SampleHelm) error { + status := objectInstance.Status + if err := r.processResources(ctx, objectInstance); err != nil { + r.Event(objectInstance, "Warning", "ResourcesInstall", err.Error()) + return r.setStatusForObjectInstance(ctx, objectInstance, status. + WithState(v1alpha1.StateError). + WithInstallConditionStatus(metav1.ConditionFalse, objectInstance.GetGeneration())) + } + // set eventual state to Ready - if no errors were found + return r.setStatusForObjectInstance(ctx, objectInstance, status. + WithState(v1alpha1.StateReady). + WithInstallConditionStatus(metav1.ConditionTrue, objectInstance.GetGeneration())) +} + +func (r *SampleHelmReconciler) HandleErrorState(ctx context.Context, objectInstance *v1alpha1.SampleHelm) error { + status := objectInstance.Status + if err := r.processResources(ctx, objectInstance); err != nil { + return err + } + // set eventual state to Ready - if no errors were found + return r.setStatusForObjectInstance(ctx, objectInstance, status. + WithState(v1alpha1.StateReady). + WithInstallConditionStatus(metav1.ConditionTrue, objectInstance.GetGeneration())) +} + +// HandleDeletingState processed the deletion on the reconciled resource. +// Once the deletion if processed the relevant finalizers (if applied) are removed. +func (r *SampleHelmReconciler) HandleDeletingState(ctx context.Context, objectInstance *v1alpha1.SampleHelm) error { + r.Event(objectInstance, "Normal", "Deleting", "resource deleting") + logger := log.FromContext(ctx) + + status := objectInstance.Status + + resources, err := r.Render(ctx, objectInstance) + if err != nil && controllerutil.RemoveFinalizer(objectInstance, finalizer) { + // if error is encountered simply remove the finalizer and delete the reconciled resource + return r.Client.Update(ctx, objectInstance) + } + r.Event(objectInstance, "Normal", "ResourcesDelete", "deleting resources") - rawBytes = bytes.TrimSpace(rawBytes) - unstructuredObj := unstructured.Unstructured{} - if err := yaml.Unmarshal(rawBytes, &unstructuredObj); err != nil { - return nil, err + installErrs := make([]error, 0) + + // instead of looping a concurrent mechanism can also be implemented + for _, resource := range resources.Items { + if err = r.Client.Delete(ctx, resource); err != nil && !errors2.IsNotFound(err) { + installErrs = append(installErrs, err) } + } - if len(rawBytes) == 0 || bytes.Equal(rawBytes, []byte("null")) || len(unstructuredObj.Object) == 0 { - continue + if len(installErrs) != 0 { + buf := &bytes.Buffer{} + for _, err = range installErrs { + _, _ = fmt.Fprintf(buf, "%v\n", err.Error()) } - objects.Items = append(objects.Items, &unstructuredObj) + logger.Error(fmt.Errorf(buf.String()), "error during uninstallation of resources") + r.Event(objectInstance, "Warning", "ResourcesDelete", "deleting resources error") + return r.setStatusForObjectInstance(ctx, objectInstance, status. + WithState(v1alpha1.StateError). + WithInstallConditionStatus(metav1.ConditionFalse, objectInstance.GetGeneration())) } -} -func (r *SampleHelmReconciler) HandleInitialState(ctx context.Context, v *v1alpha1.SampleHelm) error { + // if resources are ready to be deleted, remove finalizer + if controllerutil.RemoveFinalizer(objectInstance, finalizer) { + return r.Client.Update(ctx, objectInstance) + } + return nil +} +// HandleReadyState checks for the consistency of reconciled resource, by verifying the underlying resources. +func (r *SampleHelmReconciler) HandleReadyState(ctx context.Context, objectInstance *v1alpha1.SampleHelm) error { + status := objectInstance.Status + if err := r.processResources(ctx, objectInstance); err != nil { + r.Event(objectInstance, "Warning", "ResourcesInstall", err.Error()) + return r.setStatusForObjectInstance(ctx, objectInstance, status. + WithState(v1alpha1.StateError). + WithInstallConditionStatus(metav1.ConditionFalse, objectInstance.GetGeneration())) + } + return nil } -func (r *SampleHelmReconciler) HandleProcessingState(ctx context.Context, obj *v1alpha1.SampleHelm) error { - resources, err := r.Render(ctx, obj) +func (r *SampleHelmReconciler) processResources(ctx context.Context, objectInstance *v1alpha1.SampleHelm) error { + resources, err := r.Render(ctx, objectInstance) if err != nil { return err } @@ -254,21 +310,11 @@ func (r *SampleHelmReconciler) HandleProcessingState(ctx context.Context, obj *v if len(installErrs) != 0 { buf := &bytes.Buffer{} - for _, err := range installErrs { + for _, err = range installErrs { _, _ = fmt.Fprintf(buf, "%v\n", err.Error()) } return fmt.Errorf(buf.String()) } -} - -func (r *SampleHelmReconciler) HandleDeletingState(ctx context.Context, v *v1alpha1.SampleHelm) error { - -} - -func (r *SampleHelmReconciler) HandleErrorState(ctx context.Context, v *v1alpha1.SampleHelm) error { - -} - -func (r *SampleHelmReconciler) HandleReadyState(ctx context.Context, v *v1alpha1.SampleHelm) error { + return err } diff --git a/controllers/sample_local_helm_controller_test.go b/controllers/sample_local_helm_controller_test.go new file mode 100644 index 0000000..0ade2c4 --- /dev/null +++ b/controllers/sample_local_helm_controller_test.go @@ -0,0 +1,107 @@ +package controllers_test + +import ( + "time" + + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/client-go/kubernetes" + + "github.com/kyma-project/template-operator/api/v1alpha1" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func testHelmFn(sampleCR *v1alpha1.SampleHelm, desiredState v1alpha1.State, desiredConditionStatus metav1.ConditionStatus, + resourceCheck func(g Gomega) bool) { + // create SampleCR + Expect(k8sClient.Create(ctx, sampleCR)).To(Succeed()) + + // check if SampleCR is in the desired State + sampleCRKey := client.ObjectKeyFromObject(sampleCR) + Eventually(getCRStatus(sampleCRKey)). + WithTimeout(30 * time.Second). + WithPolling(500 * time.Millisecond). + Should(Equal(CRStatus{State: desiredState, InstallConditionStatus: desiredConditionStatus, Err: nil})) + + // check if deployed resources are up and running + Eventually(resourceCheck). + WithTimeout(30 * time.Second). + WithPolling(500 * time.Millisecond). + Should(BeTrue()) + + // clean up SampleCR + Expect(k8sClient.Delete(ctx, sampleCR)).To(Succeed()) + + // check installed resources are deleted + Eventually(checkDeleted(sampleCRKey)). + WithTimeout(30 * time.Second). + WithPolling(500 * time.Millisecond). + Should(BeTrue()) +} + +func createSampleHelmCR(sampleName string, path string) *v1alpha1.SampleHelm { + return &v1alpha1.SampleHelm{ + ObjectMeta: metav1.ObjectMeta{ + Name: sampleName, + Namespace: metav1.NamespaceDefault, + }, + Spec: v1alpha1.SampleHelmSpec{ChartPath: path}, + } +} + +func getHelmCRStatus(sampleObjKey client.ObjectKey) func(g Gomega) CRStatus { + return func(g Gomega) CRStatus { + sampleCR := &v1alpha1.Sample{} + err := k8sClient.Get(ctx, sampleObjKey, sampleCR) + if err != nil { + return CRStatus{State: v1alpha1.StateError, Err: err} + } + g.Expect(err).NotTo(HaveOccurred()) + condition := meta.FindStatusCondition(sampleCR.Status.Conditions, v1alpha1.ConditionTypeInstallation) + g.Expect(condition).ShouldNot(BeNil()) + return CRStatus{ + State: sampleCR.Status.State, + InstallConditionStatus: condition.Status, + Err: nil, + } + } +} + +func checkHelmCRDeleted(sampleObjKey client.ObjectKey) func(g Gomega) bool { + return func(g Gomega) bool { + clientSet, err := kubernetes.NewForConfig(reconciler.Config) + g.Expect(err).ToNot(HaveOccurred()) + + // check if Pod resource is deleted + _, err = clientSet.CoreV1().Pods(podNs).Get(ctx, podName, metav1.GetOptions{}) + if errors.IsNotFound(err) { + sampleCR := v1alpha1.Sample{} + // check if reconciled resource is also deleted + err = k8sClient.Get(ctx, sampleObjKey, &sampleCR) + return errors.IsNotFound(err) + } + return false + } +} + +var _ = Describe("Sample Helm CR scenarios", Ordered, func() { + DescribeTable("should set SampleHelmCR to appropriate states", + testHelmFn, + Entry("when SampleHelmCR is created with the correct resource path", + createSampleHelmCR(sampleName, "./test/busybox"), + v1alpha1.StateReady, + metav1.ConditionTrue, + getPod(podNs, podName), + ), + Entry("when SampleHelmCR is created with an incorrect resource path", + createSampleHelmCR(sampleName, "invalid/path"), + v1alpha1.StateError, + metav1.ConditionFalse, + func(g Gomega) bool { return true }, + ), + ) +}) diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 5d89a5f..dfc1cb3 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -44,12 +44,13 @@ import ( // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. var ( - k8sClient client.Client //nolint:gochecknoglobals - k8sManager manager.Manager //nolint:gochecknoglobals - testEnv *envtest.Environment //nolint:gochecknoglobals - ctx context.Context //nolint:gochecknoglobals - cancel context.CancelFunc //nolint:gochecknoglobals - reconciler *controllers.SampleReconciler //nolint:gochecknoglobals + k8sClient client.Client //nolint:gochecknoglobals + k8sManager manager.Manager //nolint:gochecknoglobals + testEnv *envtest.Environment //nolint:gochecknoglobals + ctx context.Context //nolint:gochecknoglobals + cancel context.CancelFunc //nolint:gochecknoglobals + reconciler *controllers.SampleReconciler //nolint:gochecknoglobals + reconcilerHelm *controllers.SampleHelmReconciler //nolint:gochecknoglobals ) const ( @@ -111,7 +112,16 @@ var _ = BeforeSuite(func() { EventRecorder: k8sManager.GetEventRecorderFor("tests"), } - err = reconciler.SetupWithManager(k8sManager, rateLimiter, testChartPath) + reconcilerHelm = &controllers.SampleHelmReconciler{ + Client: k8sManager.GetClient(), + Scheme: scheme.Scheme, + EventRecorder: k8sManager.GetEventRecorderFor("tests"), + Config: k8sManager.GetConfig(), + } + + err = reconciler.SetupWithManager(k8sManager, rateLimiter) + Expect(err).ToNot(HaveOccurred()) + err = reconcilerHelm.SetupWithManager(k8sManager, rateLimiter) Expect(err).ToNot(HaveOccurred()) go func() { diff --git a/controllers/test/busybox/Chart.yaml b/controllers/test/busybox/Chart.yaml index 8704631..0634f64 100644 --- a/controllers/test/busybox/Chart.yaml +++ b/controllers/test/busybox/Chart.yaml @@ -11,5 +11,5 @@ keywords: maintainers: - email: john.felten@gmail.com name: John Felten -name: busybox +name: busybox-helm version: 0.1.0 diff --git a/controllers/test/busybox/templates/deployment.yaml b/controllers/test/busybox/templates/deployment.yaml index 7c8ab68..d49437d 100644 --- a/controllers/test/busybox/templates/deployment.yaml +++ b/controllers/test/busybox/templates/deployment.yaml @@ -2,6 +2,7 @@ apiVersion: v1 kind: Pod metadata: name: {{ .Chart.Name }}-pod + namespace: default labels: chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" spec: diff --git a/controllers/test/busybox/templates/service.yaml b/controllers/test/busybox/templates/service.yaml index 202cc91..001bc6c 100644 --- a/controllers/test/busybox/templates/service.yaml +++ b/controllers/test/busybox/templates/service.yaml @@ -2,6 +2,7 @@ apiVersion: v1 kind: Service metadata: name: {{ .Chart.Name }}-pod + namespace: default labels: chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" spec: diff --git a/go.mod b/go.mod index e07a94e..47349fb 100644 --- a/go.mod +++ b/go.mod @@ -16,59 +16,129 @@ require ( ) require ( + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/BurntSushi/toml v1.2.1 // indirect + github.com/MakeNowJust/heredoc v1.0.0 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect + github.com/Masterminds/sprig/v3 v3.2.3 // indirect + github.com/Masterminds/squirrel v1.5.3 // indirect + github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/chai2010/gettext-go v1.0.2 // indirect + github.com/containerd/containerd v1.6.15 // indirect + github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/docker/cli v20.10.21+incompatible // indirect + github.com/docker/distribution v2.8.1+incompatible // indirect + github.com/docker/docker v20.10.21+incompatible // indirect + github.com/docker/docker-credential-helpers v0.7.0 // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-metrics v0.0.1 // indirect + github.com/docker/go-units v0.4.0 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect + github.com/fatih/color v1.7.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-errors/errors v1.0.1 // indirect + github.com/go-gorp/gorp/v3 v3.0.2 // indirect github.com/go-logr/zapr v1.2.3 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/swag v0.22.3 // indirect + github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/google/btree v1.0.1 // indirect github.com/google/gnostic v0.6.9 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/gosuri/uitable v0.0.4 // indirect + github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.13 // indirect + github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.11.13 // indirect github.com/kr/pretty v0.3.0 // indirect + github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect + github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/lib/pq v1.10.7 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.0.9 // indirect + github.com/mattn/go-isatty v0.0.3 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/locker v1.0.1 // indirect + github.com/moby/spdystream v0.2.0 // indirect + github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc2 // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect - github.com/rogpeppe/go-internal v1.8.0 // indirect + github.com/rubenv/sql-migrate v1.2.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/shopspring/decimal v1.2.0 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect + github.com/spf13/cast v1.3.1 // indirect + github.com/spf13/cobra v1.6.1 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + github.com/xlab/treeprint v1.1.0 // indirect + go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.24.0 // indirect + golang.org/x/crypto v0.5.0 // indirect golang.org/x/net v0.5.0 // indirect golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 // indirect + golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.4.0 // indirect golang.org/x/term v0.4.0 // indirect golang.org/x/text v0.6.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect + google.golang.org/grpc v1.49.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.26.0 // indirect + k8s.io/apiserver v0.26.0 // indirect + k8s.io/cli-runtime v0.26.0 // indirect k8s.io/component-base v0.26.0 // indirect k8s.io/klog/v2 v2.80.1 // indirect k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect + k8s.io/kubectl v0.26.0 // indirect k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect + oras.land/oras-go v1.2.2 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/kustomize/api v0.12.1 // indirect + sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect ) diff --git a/go.sum b/go.sum index 6439fa5..896f10d 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,11 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -21,6 +26,7 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -31,60 +37,141 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc= +github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= +github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= +github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= +github.com/containerd/containerd v1.6.15 h1:4wWexxzLNHNE46aIETc6ge4TofO550v+BlLoANrbses= +github.com/containerd/containerd v1.6.15/go.mod h1:U2NnBPIhzJDm59xF7xB2MMHnKtggpZ+phKg8o2TKj2c= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= +github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= +github.com/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SHndOuGsfwyhU= +github.com/docker/cli v20.10.21+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= +github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog= +github.com/docker/docker v20.10.21+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= +github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= +github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gorp/gorp/v3 v3.0.2 h1:ULqJXIekoqMx29FI5ekXXFoH1dT2Vc8UhnRzBg+Emz4= +github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -107,10 +194,25 @@ github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXym github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= +github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs= +github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0= +github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY= +github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY= +github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -124,6 +226,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -139,10 +242,14 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -152,8 +259,11 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -161,6 +271,7 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -168,51 +279,159 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= +github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= +github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.11.13 h1:eSvu8Tmq6j2psUJqJrLcWH6K3w5Dwc+qipbaA6eVEN4= +github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= +github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= +github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= +github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= +github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw= +github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.4/go.mod h1:vTLESy5mRhKOs9KDp0/RATawxP1UqBmdrpVRMnpcvKQ= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -220,23 +439,42 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc= github.com/onsi/ginkgo/v2 v2.6.0/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc= github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= +github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1 h1:oL4IBbcqwhhNWh31bjOX8C/OCy0zs9906d/VUru+bqg= +github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= @@ -249,6 +487,7 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= @@ -256,6 +495,7 @@ github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8 github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= @@ -266,34 +506,82 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rubenv/sql-migrate v1.2.0 h1:fOXMPLMd41sK7Tg75SXDec15k3zg5WNV6SjuDRiNfcU= +github.com/rubenv/sql-migrate v1.2.0/go.mod h1:Z5uVnq7vrIrPmHbVFfR4YLHRZquxeHpckCnRq0P/K9Y= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= +github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= +github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= +github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= +go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= @@ -302,15 +590,25 @@ go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= +golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -333,6 +631,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -341,9 +641,15 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -370,11 +676,19 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -382,6 +696,13 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 h1:dtndE8FcEta75/4kHF3AbpuWzV6f1LjnLrM4pe2SZrw= @@ -396,8 +717,14 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -408,7 +735,11 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -427,21 +758,37 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -449,9 +796,11 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -465,6 +814,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -475,6 +825,7 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -501,7 +852,16 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -524,6 +884,12 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -562,7 +928,20 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -575,9 +954,18 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -591,6 +979,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -598,9 +987,11 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -616,6 +1007,7 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= helm.sh/helm/v3 v3.11.0 h1:F+peaCQYbycY1FIqIQ6dAortHd/VzV5FkhMciv4Kf+c= helm.sh/helm/v3 v3.11.0/go.mod h1:z/Bu/BylToGno/6dtNGuSmjRqxKq5gaH+FU0BPO+AQ8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -631,6 +1023,10 @@ k8s.io/apiextensions-apiserver v0.26.0 h1:Gy93Xo1eg2ZIkNX/8vy5xviVSxwQulsnUdQ00n k8s.io/apiextensions-apiserver v0.26.0/go.mod h1:7ez0LTiyW5nq3vADtK6C3kMESxadD51Bh6uz3JOlqWQ= k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg= k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= +k8s.io/apiserver v0.26.0 h1:q+LqIK5EZwdznGZb8bq0+a+vCqdeEEe4Ux3zsOjbc4o= +k8s.io/apiserver v0.26.0/go.mod h1:aWhlLD+mU+xRo+zhkvP/gFNbShI4wBDHS33o0+JGI84= +k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw= +k8s.io/cli-runtime v0.26.0/go.mod h1:o+4KmwHzO/UK0wepE1qpRk6l3o60/txUZ1fEXWGIKTY= k8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8= k8s.io/client-go v0.26.0/go.mod h1:I2Sh57A79EQsDmn7F7ASpmru1cceh3ocVT9KlX2jEZg= k8s.io/component-base v0.26.0 h1:0IkChOCohtDHttmKuz+EP3j3+qKmV55rM9gIFTXA7Vs= @@ -639,8 +1035,12 @@ k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= +k8s.io/kubectl v0.26.0 h1:xmrzoKR9CyNdzxBmXV7jW9Ln8WMrwRK6hGbbf69o4T0= +k8s.io/kubectl v0.26.0/go.mod h1:eInP0b+U9XUJWSYeU9XZnTA+cVYuWyl3iYPGtru0qhQ= k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +oras.land/oras-go v1.2.2 h1:0E9tOHUfrNH7TCDk5KU0jVBEzCqbfdyuVfGmJ7ZeRPE= +oras.land/oras-go v1.2.2/go.mod h1:Apa81sKoZPpP7CDciE006tSZ0x3Q3+dOoBcMZ/aNxvw= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= @@ -648,6 +1048,10 @@ sigs.k8s.io/controller-runtime v0.14.1 h1:vThDes9pzg0Y+UbCPY3Wj34CGIYPgdmspPm2GI sigs.k8s.io/controller-runtime v0.14.1/go.mod h1:GaRkrY8a7UZF0kqFFbUKG7n9ICiTY5T55P1RiE3UZlU= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= +sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= +sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= +sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= From cbed636bfd1ff51905e36e862350ead53dd2f5e7 Mon Sep 17 00:00:00 2001 From: Aditya Bhatia Date: Tue, 24 Jan 2023 12:15:05 +0100 Subject: [PATCH 3/8] fix tests and improve docs --- README.md | 12 +- controllers/common_utils.go | 53 ++++ .../sample_controller_rendered_resources.go | 14 - controllers/sample_local_helm_controller.go | 258 ++++++++++-------- .../sample_local_helm_controller_test.go | 15 +- controllers/test/busybox/Chart.yaml | 3 - .../test/busybox/templates/deployment.yaml | 1 - .../test/busybox/templates/service.yaml | 1 - main.go | 4 +- 9 files changed, 211 insertions(+), 150 deletions(-) diff --git a/README.md b/README.md index 9cf18be..b799447 100644 --- a/README.md +++ b/README.md @@ -165,10 +165,11 @@ Further reading: [Kustomize built-in commonLabels](https://github.com/kubernetes Include the `State` values in your `Status` sub-resource, either through inline reference or direct inclusion. These values have literal meaning behind them, so use them appropriately. 2. Optionally, you can add additional fields to your `Status` sub-resource. -For instance, `Conditions` are added to `SampleCR` in the [API definition](api/v1alpha1/sample_types.go), along with the required `State` values using inline reference. +3. For instance, `Conditions` are added to `SampleCR` in the [API definition](api/v1alpha1/sample_types.go) and `SampleHelmCR` in the [API definition](api/v1alpha1/samplehelm_types.go). +This also includes the required `State` values, using an inline reference.
- Reference implementation + Reference implementation SampleCR ```go package v1alpha1 @@ -201,14 +202,17 @@ _Warning_: This sample implementation is only for reference. You could copy part 1. Implement `State` handling to represent the corresponding state of the reconciled resource, by following [kubebuilder](https://book.kubebuilder.io/) guidelines to implement controllers. -2. Optionally, you could refer to the `SampleCR` [controller implementation](controllers/sample_controller_rendered_resources_test.go) for setting appropriate `State` and `Conditions` values to your `Status` sub-resource, such as: +2. You could refer either to `SampleCR` [controller implementation](controllers/sample_controller_rendered_resources.go) or `SampleHelmCR` [controller implementation](controllers/sample_local_helm_controller.go) for setting appropriate `State` and `Conditions` values to your `Status` sub-resource. + + `SampleCR` is reconciled to install / uninstall a list of rendered resources from a YAML file on the file system. Whereas `SampleHelmCR` is reconciled to install / uninstall (using SSA, see next point) rendered resources from a local Helm Chart. The latter uses the Helm library purely to render resources. + ````go r.setStatusForObjectInstance(ctx, objectInstance, status. WithState(v1alpha1.StateReady). WithInstallConditionStatus(metav1.ConditionTrue, objectInstance.GetGeneration())) ```` -3. This [reference implementation](controllers/sample_controller_rendered_resources_test.go) also covers reading resources from a concatenated YAML file and installing them on the cluster using [Server-side apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/). +3. The reference controller implementations listed above use [Server-side apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/) instead of conventional methods to process resources on the target cluster. Parts of this logic could be leveraged to implement your own controller logic. Checkout functions `getResourcesFromLocalPath()`, `ssa()` and `ssaStatus()` for implementation details. ### Local testing diff --git a/controllers/common_utils.go b/controllers/common_utils.go index 1bb1604..7e34a2c 100644 --- a/controllers/common_utils.go +++ b/controllers/common_utils.go @@ -9,8 +9,13 @@ import ( "strings" "time" + "golang.org/x/time/rate" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" yamlUtil "k8s.io/apimachinery/pkg/util/yaml" + "k8s.io/client-go/util/workqueue" + "sigs.k8s.io/controller-runtime/pkg/ratelimiter" "sigs.k8s.io/yaml" ) @@ -55,3 +60,51 @@ func parseManifestStringToObjects(manifest string) (*ManifestResources, error) { objects.Items = append(objects.Items, &unstructuredObj) } } + +func getNsResource(nsName string) *v1.Namespace { + return &v1.Namespace{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: nsName, + Labels: map[string]string{ + "name": nsName, + }, + }, + } +} + +// TemplateRateLimiter implements a rate limiter for a client-go.workqueue. It has +// both an overall (token bucket) and per-item (exponential) rate limiting. +func TemplateRateLimiter(failureBaseDelay time.Duration, failureMaxDelay time.Duration, + frequency int, burst int, +) ratelimiter.RateLimiter { + return workqueue.NewMaxOfRateLimiter( + workqueue.NewItemExponentialFailureRateLimiter(failureBaseDelay, failureMaxDelay), + &workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(frequency), burst)}) +} + +type errorGrp struct { + Errors []error +} + +func NewErrorGrp() *errorGrp { + return &errorGrp{} +} + +func (e *errorGrp) Add(err error) { + e.Errors = append(e.Errors, err) +} + +func (e *errorGrp) Error() error { + if len(e.Errors) == 0 { + return nil + } + buf := &bytes.Buffer{} + for _, err := range e.Errors { + _, _ = fmt.Fprintf(buf, "%v\n", err.Error()) + } + return fmt.Errorf(buf.String()) +} diff --git a/controllers/sample_controller_rendered_resources.go b/controllers/sample_controller_rendered_resources.go index 31fee9b..9ce22fd 100644 --- a/controllers/sample_controller_rendered_resources.go +++ b/controllers/sample_controller_rendered_resources.go @@ -22,10 +22,8 @@ import ( "io/fs" "os" "path/filepath" - "time" "github.com/go-logr/logr" - "golang.org/x/time/rate" errors2 "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -33,7 +31,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/rest" "k8s.io/client-go/tools/record" - "k8s.io/client-go/util/workqueue" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -42,7 +39,6 @@ import ( "github.com/kyma-project/template-operator/api/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/ratelimiter" ) // SampleReconciler reconciles a Sample object @@ -85,16 +81,6 @@ func (r *SampleReconciler) SetupWithManager(mgr ctrl.Manager, rateLimiter RateLi Complete(r) } -// TemplateRateLimiter implements a rate limiter for a client-go.workqueue. It has -// both an overall (token bucket) and per-item (exponential) rate limiting. -func TemplateRateLimiter(failureBaseDelay time.Duration, failureMaxDelay time.Duration, - frequency int, burst int, -) ratelimiter.RateLimiter { - return workqueue.NewMaxOfRateLimiter( - workqueue.NewItemExponentialFailureRateLimiter(failureBaseDelay, failureMaxDelay), - &workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(frequency), burst)}) -} - // Reconcile is the entry point from the controller-runtime framework. // It performs a reconciliation based on the passed ctrl.Request object. func (r *SampleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { diff --git a/controllers/sample_local_helm_controller.go b/controllers/sample_local_helm_controller.go index 5ec2cf4..0b3c8b4 100644 --- a/controllers/sample_local_helm_controller.go +++ b/controllers/sample_local_helm_controller.go @@ -23,9 +23,10 @@ import ( "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/kube" errors2 "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" "k8s.io/client-go/tools/record" @@ -38,6 +39,8 @@ import ( "github.com/kyma-project/template-operator/api/v1alpha1" ) +var CustomNs = "helm-custom-ns" + // SampleHelmReconciler reconciles a SampleHelm object type SampleHelmReconciler struct { client.Client @@ -105,104 +108,7 @@ func (r *SampleHelmReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, nil } -// SetupWithManager sets up the controller with the Manager. -func (r *SampleHelmReconciler) SetupWithManager(mgr ctrl.Manager, rateLimiter RateLimiter) error { - return ctrl.NewControllerManagedBy(mgr). - For(&v1alpha1.SampleHelm{}). - WithOptions(controller.Options{ - RateLimiter: TemplateRateLimiter( - rateLimiter.BaseDelay, - rateLimiter.FailureMaxDelay, - rateLimiter.Frequency, - rateLimiter.Burst, - ), - }). - Complete(r) -} - -func (r *SampleHelmReconciler) setStatusForObjectInstance(ctx context.Context, objectInstance *v1alpha1.SampleHelm, - status *v1alpha1.SampleHelmStatus, -) error { - objectInstance.Status = *status - - if err := r.ssaStatus(ctx, objectInstance); err != nil { - r.Event(objectInstance, "Warning", "ErrorUpdatingStatus", fmt.Sprintf("updating state to %v", string(status.State))) - return fmt.Errorf("error while updating status %s to: %w", status.State, err) - } - - r.Event(objectInstance, "Normal", "StatusUpdated", fmt.Sprintf("updating state to %v", string(status.State))) - return nil -} - -// ssaStatus patches status using SSA on the passed object -func (r *SampleHelmReconciler) ssaStatus(ctx context.Context, obj client.Object) error { - obj.SetManagedFields(nil) - obj.SetResourceVersion("") - return r.Status().Patch(ctx, obj, client.Apply, - &client.SubResourcePatchOptions{PatchOptions: client.PatchOptions{FieldManager: fieldOwner}}) -} - -// ssa patches the object using SSA -func (r *SampleHelmReconciler) ssa(ctx context.Context, obj client.Object) error { - obj.SetManagedFields(nil) - obj.SetResourceVersion("") - return r.Patch(ctx, obj, client.Apply, client.ForceOwnership, client.FieldOwner(fieldOwner)) -} - -func (r *SampleHelmReconciler) Render(ctx context.Context, obj *v1alpha1.SampleHelm) (*ManifestResources, error) { - //status := obj.Status - logger := log.FromContext(ctx) - - actionConfig := new(action.Configuration) - settings := cli.New() - if err := actionConfig.Init(NewRESTClientGetter(r.Config), settings.Namespace(), "secrets", func(format string, v ...interface{}) { - format = fmt.Sprintf("[debug] %s\n", format) - debugLevel := 2 - logger.V(debugLevel).Info(fmt.Sprintf(format, v...)) - }); err != nil { - logger.Error(err, "") - } - - actionClient := action.NewInstall(actionConfig) - - // Helm flags - r.SetDefaultClientConfig(actionClient, "sample-release-name") - - // Helm values - valuesAsMap := map[string]interface{}{ - "label": "custom-label-from-controller", - } - - chrt, err := loader.Load(obj.Spec.ChartPath) - if err != nil { - return nil, err - } - - release, err := actionClient.RunWithContext(ctx, chrt, valuesAsMap) - if err != nil { - return nil, err - } - - return parseManifestStringToObjects(release.Manifest) -} - -func (r *SampleHelmReconciler) SetDefaultClientConfig(actionClient *action.Install, releaseName string) { - actionClient.DryRun = true - actionClient.Atomic = false - actionClient.Wait = false - actionClient.WaitForJobs = false - actionClient.Replace = true // Skip the name check - actionClient.IncludeCRDs = true // include CRDs in the templated output - actionClient.ClientOnly = true - actionClient.ReleaseName = releaseName - actionClient.Namespace = metav1.NamespaceDefault - - // default versioning if unspecified - if actionClient.Version == "" && actionClient.Devel { - actionClient.Version = ">0.0.0-0" - } -} - +// HandleInitialState bootstraps state handling for the reconciled resource. func (r *SampleHelmReconciler) HandleInitialState(ctx context.Context, objectInstance *v1alpha1.SampleHelm) error { status := objectInstance.Status @@ -211,6 +117,8 @@ func (r *SampleHelmReconciler) HandleInitialState(ctx context.Context, objectIns WithInstallConditionStatus(metav1.ConditionUnknown, objectInstance.GetGeneration())) } +// HandleProcessingState processes the reconciled resource by processing the underlying resources. +// Based on the processing either a success or failure state is set on the reconciled resource. func (r *SampleHelmReconciler) HandleProcessingState(ctx context.Context, objectInstance *v1alpha1.SampleHelm) error { status := objectInstance.Status if err := r.processResources(ctx, objectInstance); err != nil { @@ -225,6 +133,7 @@ func (r *SampleHelmReconciler) HandleProcessingState(ctx context.Context, object WithInstallConditionStatus(metav1.ConditionTrue, objectInstance.GetGeneration())) } +// HandleErrorState handles error recovery for the reconciled resource. func (r *SampleHelmReconciler) HandleErrorState(ctx context.Context, objectInstance *v1alpha1.SampleHelm) error { status := objectInstance.Status if err := r.processResources(ctx, objectInstance); err != nil { @@ -244,29 +153,24 @@ func (r *SampleHelmReconciler) HandleDeletingState(ctx context.Context, objectIn status := objectInstance.Status - resources, err := r.Render(ctx, objectInstance) + resources, err := r.render(ctx, objectInstance) if err != nil && controllerutil.RemoveFinalizer(objectInstance, finalizer) { // if error is encountered simply remove the finalizer and delete the reconciled resource return r.Client.Update(ctx, objectInstance) } r.Event(objectInstance, "Normal", "ResourcesDelete", "deleting resources") - installErrs := make([]error, 0) + errGroup := NewErrorGrp() // instead of looping a concurrent mechanism can also be implemented for _, resource := range resources.Items { if err = r.Client.Delete(ctx, resource); err != nil && !errors2.IsNotFound(err) { - installErrs = append(installErrs, err) + errGroup.Add(err) } } - if len(installErrs) != 0 { - buf := &bytes.Buffer{} - for _, err = range installErrs { - _, _ = fmt.Fprintf(buf, "%v\n", err.Error()) - } - - logger.Error(fmt.Errorf(buf.String()), "error during uninstallation of resources") + if errGroup.Error() != nil { + logger.Error(errGroup.Error(), "error during uninstallation of resources") r.Event(objectInstance, "Warning", "ResourcesDelete", "deleting resources error") return r.setStatusForObjectInstance(ctx, objectInstance, status. WithState(v1alpha1.StateError). @@ -292,29 +196,143 @@ func (r *SampleHelmReconciler) HandleReadyState(ctx context.Context, objectInsta return nil } +func (r *SampleHelmReconciler) setStatusForObjectInstance(ctx context.Context, objectInstance *v1alpha1.SampleHelm, + status *v1alpha1.SampleHelmStatus, +) error { + objectInstance.Status = *status + + if err := r.ssaStatus(ctx, objectInstance); err != nil { + r.Event(objectInstance, "Warning", "ErrorUpdatingStatus", fmt.Sprintf("updating state to %v", string(status.State))) + return fmt.Errorf("error while updating status %s to: %w", status.State, err) + } + + r.Event(objectInstance, "Normal", "StatusUpdated", fmt.Sprintf("updating state to %v", string(status.State))) + return nil +} + +// ssaStatus patches status using SSA on the passed object +func (r *SampleHelmReconciler) ssaStatus(ctx context.Context, obj client.Object) error { + obj.SetManagedFields(nil) + obj.SetResourceVersion("") + return r.Status().Patch(ctx, obj, client.Apply, + &client.SubResourcePatchOptions{PatchOptions: client.PatchOptions{FieldManager: fieldOwner}}) +} + +// ssa patches the object using SSA +func (r *SampleHelmReconciler) ssa(ctx context.Context, obj client.Object) error { + obj.SetManagedFields(nil) + obj.SetResourceVersion("") + return r.Patch(ctx, obj, client.Apply, client.ForceOwnership, client.FieldOwner(fieldOwner)) +} + +func (r *SampleHelmReconciler) render(ctx context.Context, obj *v1alpha1.SampleHelm) (*ManifestResources, error) { + // create custom namespace resource + ns := getNsResource(CustomNs) + if err := r.ssa(ctx, ns); err != nil { + return nil, err + } + + logger := log.FromContext(ctx) + restClientGetter := NewRESTClientGetter(r.Config) + + actionConfig := new(action.Configuration) + + if err := actionConfig.Init(restClientGetter, CustomNs, "memory", func(format string, v ...interface{}) { + format = fmt.Sprintf("[debug] %s\n", format) + debugLevel := 2 + logger.V(debugLevel).Info(fmt.Sprintf(format, v...)) + }); err != nil { + logger.Error(err, "") + } + + // override custom namespace + kubeClient := actionConfig.KubeClient.(*kube.Client) + kubeClient.Namespace = CustomNs + + actionClient := action.NewInstall(actionConfig) + + // Helm flags + r.setClientConfig(actionClient, "sample-release-name") + + // Helm values + valuesAsMap := map[string]interface{}{ + "label": "custom-label-from-controller", + } + + // load Helm chart from local path + chrt, err := loader.Load(obj.Spec.ChartPath) + if err != nil { + return nil, err + } + + // parse Helm chart resources + release, err := actionClient.RunWithContext(ctx, chrt, valuesAsMap) + if err != nil { + return nil, err + } + + resourceList, err := kubeClient.Build(bytes.NewBufferString(release.Manifest), true) + if err != nil { + return nil, err + } + + resources := &ManifestResources{} + for _, info := range resourceList { + resources.Items = append(resources.Items, info.Object.(*unstructured.Unstructured)) + } + + return resources, nil +} + +func (r *SampleHelmReconciler) setClientConfig(actionClient *action.Install, releaseName string) { + actionClient.DryRun = true + actionClient.Atomic = false + actionClient.Wait = false + actionClient.WaitForJobs = false + actionClient.Replace = true // Skip the name check + actionClient.IncludeCRDs = true // include CRDs in the templated output + actionClient.ClientOnly = false + actionClient.ReleaseName = releaseName + actionClient.Namespace = CustomNs + actionClient.CreateNamespace = true + actionClient.IsUpgrade = true + + // default versioning if unspecified + if actionClient.Version == "" && actionClient.Devel { + actionClient.Version = ">0.0.0-0" + } +} + func (r *SampleHelmReconciler) processResources(ctx context.Context, objectInstance *v1alpha1.SampleHelm) error { - resources, err := r.Render(ctx, objectInstance) + resources, err := r.render(ctx, objectInstance) if err != nil { return err } - installErrs := make([]error, 0) + errorGrp := NewErrorGrp() // instead of looping a concurrent mechanism can also be implemented for _, resource := range resources.Items { err = r.ssa(ctx, resource) if err != nil { - installErrs = append(installErrs, err) + errorGrp.Add(err) } } - if len(installErrs) != 0 { - buf := &bytes.Buffer{} - for _, err = range installErrs { - _, _ = fmt.Fprintf(buf, "%v\n", err.Error()) - } - return fmt.Errorf(buf.String()) - } + return errorGrp.Error() +} - return err +// SetupWithManager sets up the controller with the Manager. +func (r *SampleHelmReconciler) SetupWithManager(mgr ctrl.Manager, rateLimiter RateLimiter) error { + return ctrl.NewControllerManagedBy(mgr). + For(&v1alpha1.SampleHelm{}). + WithOptions(controller.Options{ + RateLimiter: TemplateRateLimiter( + rateLimiter.BaseDelay, + rateLimiter.FailureMaxDelay, + rateLimiter.Frequency, + rateLimiter.Burst, + ), + }). + Complete(r) } diff --git a/controllers/sample_local_helm_controller_test.go b/controllers/sample_local_helm_controller_test.go index 0ade2c4..62115e9 100644 --- a/controllers/sample_local_helm_controller_test.go +++ b/controllers/sample_local_helm_controller_test.go @@ -8,6 +8,7 @@ import ( "k8s.io/client-go/kubernetes" "github.com/kyma-project/template-operator/api/v1alpha1" + "github.com/kyma-project/template-operator/controllers" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -15,6 +16,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) +var ( + helmPodName = "busybox-helm-pod" +) + func testHelmFn(sampleCR *v1alpha1.SampleHelm, desiredState v1alpha1.State, desiredConditionStatus metav1.ConditionStatus, resourceCheck func(g Gomega) bool) { // create SampleCR @@ -22,7 +27,7 @@ func testHelmFn(sampleCR *v1alpha1.SampleHelm, desiredState v1alpha1.State, desi // check if SampleCR is in the desired State sampleCRKey := client.ObjectKeyFromObject(sampleCR) - Eventually(getCRStatus(sampleCRKey)). + Eventually(getHelmCRStatus(sampleCRKey)). WithTimeout(30 * time.Second). WithPolling(500 * time.Millisecond). Should(Equal(CRStatus{State: desiredState, InstallConditionStatus: desiredConditionStatus, Err: nil})) @@ -37,7 +42,7 @@ func testHelmFn(sampleCR *v1alpha1.SampleHelm, desiredState v1alpha1.State, desi Expect(k8sClient.Delete(ctx, sampleCR)).To(Succeed()) // check installed resources are deleted - Eventually(checkDeleted(sampleCRKey)). + Eventually(checkHelmCRDeleted(sampleCRKey)). WithTimeout(30 * time.Second). WithPolling(500 * time.Millisecond). Should(BeTrue()) @@ -55,7 +60,7 @@ func createSampleHelmCR(sampleName string, path string) *v1alpha1.SampleHelm { func getHelmCRStatus(sampleObjKey client.ObjectKey) func(g Gomega) CRStatus { return func(g Gomega) CRStatus { - sampleCR := &v1alpha1.Sample{} + sampleCR := &v1alpha1.SampleHelm{} err := k8sClient.Get(ctx, sampleObjKey, sampleCR) if err != nil { return CRStatus{State: v1alpha1.StateError, Err: err} @@ -79,7 +84,7 @@ func checkHelmCRDeleted(sampleObjKey client.ObjectKey) func(g Gomega) bool { // check if Pod resource is deleted _, err = clientSet.CoreV1().Pods(podNs).Get(ctx, podName, metav1.GetOptions{}) if errors.IsNotFound(err) { - sampleCR := v1alpha1.Sample{} + sampleCR := v1alpha1.SampleHelm{} // check if reconciled resource is also deleted err = k8sClient.Get(ctx, sampleObjKey, &sampleCR) return errors.IsNotFound(err) @@ -95,7 +100,7 @@ var _ = Describe("Sample Helm CR scenarios", Ordered, func() { createSampleHelmCR(sampleName, "./test/busybox"), v1alpha1.StateReady, metav1.ConditionTrue, - getPod(podNs, podName), + getPod(controllers.CustomNs, helmPodName), ), Entry("when SampleHelmCR is created with an incorrect resource path", createSampleHelmCR(sampleName, "invalid/path"), diff --git a/controllers/test/busybox/Chart.yaml b/controllers/test/busybox/Chart.yaml index 0634f64..b5b459e 100644 --- a/controllers/test/busybox/Chart.yaml +++ b/controllers/test/busybox/Chart.yaml @@ -8,8 +8,5 @@ keywords: - sanity check - network - connectivity -maintainers: -- email: john.felten@gmail.com - name: John Felten name: busybox-helm version: 0.1.0 diff --git a/controllers/test/busybox/templates/deployment.yaml b/controllers/test/busybox/templates/deployment.yaml index d49437d..7c8ab68 100644 --- a/controllers/test/busybox/templates/deployment.yaml +++ b/controllers/test/busybox/templates/deployment.yaml @@ -2,7 +2,6 @@ apiVersion: v1 kind: Pod metadata: name: {{ .Chart.Name }}-pod - namespace: default labels: chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" spec: diff --git a/controllers/test/busybox/templates/service.yaml b/controllers/test/busybox/templates/service.yaml index 001bc6c..202cc91 100644 --- a/controllers/test/busybox/templates/service.yaml +++ b/controllers/test/busybox/templates/service.yaml @@ -2,7 +2,6 @@ apiVersion: v1 kind: Service metadata: name: {{ .Chart.Name }}-pod - namespace: default labels: chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" spec: diff --git a/main.go b/main.go index adb0994..fd934e7 100644 --- a/main.go +++ b/main.go @@ -105,14 +105,14 @@ func main() { Client: mgr.GetClient(), Scheme: mgr.GetScheme(), EventRecorder: mgr.GetEventRecorderFor(operatorName), - }).SetupWithManager(mgr, ratelimiter, flagVar.chartPath); err != nil { + }).SetupWithManager(mgr, ratelimiter); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Sample") os.Exit(1) } if err = (&controllers.SampleHelmReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), - }).SetupWithManager(mgr); err != nil { + }).SetupWithManager(mgr, ratelimiter); err != nil { setupLog.Error(err, "unable to create controller", "controller", "SampleHelm") os.Exit(1) } From ea63ab77cb54009aaa1eea7f0b4b61477073b67f Mon Sep 17 00:00:00 2001 From: Aditya Bhatia Date: Tue, 24 Jan 2023 12:16:40 +0100 Subject: [PATCH 4/8] minor addition --- README.md | 2 +- ...fault.yaml => operator.kyma-project.io_v1alpha1_sample.yaml} | 2 +- .../samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename config/samples/{default.yaml => operator.kyma-project.io_v1alpha1_sample.yaml} (75%) diff --git a/README.md b/README.md index b799447..1d742a0 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,7 @@ _Warning_: This sample implementation is only for reference. You could copy part ```` 3. The reference controller implementations listed above use [Server-side apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/) instead of conventional methods to process resources on the target cluster. - Parts of this logic could be leveraged to implement your own controller logic. Checkout functions `getResourcesFromLocalPath()`, `ssa()` and `ssaStatus()` for implementation details. + Parts of this logic could be leveraged to implement your own controller logic. Checkout functions inside these controllers for state management and other implementation details. ### Local testing * Connect to your cluster and ensure `kubectl` is pointing to the desired cluster. diff --git a/config/samples/default.yaml b/config/samples/operator.kyma-project.io_v1alpha1_sample.yaml similarity index 75% rename from config/samples/default.yaml rename to config/samples/operator.kyma-project.io_v1alpha1_sample.yaml index d9bf86d..6280f45 100644 --- a/config/samples/default.yaml +++ b/config/samples/operator.kyma-project.io_v1alpha1_sample.yaml @@ -3,4 +3,4 @@ kind: Sample metadata: name: sample-sample spec: - releaseName: "redis-release" + resourceFilePath: "your/path" diff --git a/config/samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml b/config/samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml index 01cc04f..47155c2 100644 --- a/config/samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml +++ b/config/samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml @@ -9,4 +9,4 @@ metadata: app.kubernetes.io/created-by: template-operator name: samplehelm-sample spec: - chartPath: "./test/busybox" + chartPath: "your/path" From f82a334d759e7d155966883a999d36a99ecc2ae7 Mon Sep 17 00:00:00 2001 From: Aditya Bhatia Date: Mon, 30 Jan 2023 12:41:18 +0100 Subject: [PATCH 5/8] review comments + minor improvements --- config/crd/kustomization.yaml | 2 +- config/default/kustomization.yaml | 4 ++-- controllers/sample_local_helm_controller.go | 9 ++++++--- main.go | 5 +++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index b7e4f3a..d1a1020 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -6,7 +6,7 @@ kind: Component # It should be run by config/default resources: - bases/operator.kyma-project.io_samples.yaml -- bases/operator.kyma-project.io.kyma-project.io_samplehelms.yaml + - bases/operator.kyma-project.io_samplehelms.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 2baeae1..49cb142 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -83,5 +83,5 @@ components: images: - name: controller - newName: op-kcp-registry.localhost:5000/unsigned/kyma-project.io/module/sample-operator - newTag: 0.0.1 + newName: k3d-registry.localhost:5111/kyma-project.io/template-operator + newTag: 1.2.3 diff --git a/controllers/sample_local_helm_controller.go b/controllers/sample_local_helm_controller.go index 0b3c8b4..6cff1ad 100644 --- a/controllers/sample_local_helm_controller.go +++ b/controllers/sample_local_helm_controller.go @@ -154,9 +154,12 @@ func (r *SampleHelmReconciler) HandleDeletingState(ctx context.Context, objectIn status := objectInstance.Status resources, err := r.render(ctx, objectInstance) - if err != nil && controllerutil.RemoveFinalizer(objectInstance, finalizer) { - // if error is encountered simply remove the finalizer and delete the reconciled resource - return r.Client.Update(ctx, objectInstance) + if err != nil { + logger.Error(err, "error during rendering of resources during uninstallation") + r.Event(objectInstance, "Warning", "ResourcesDelete", "rendering resources error") + return r.setStatusForObjectInstance(ctx, objectInstance, status. + WithState(v1alpha1.StateError). + WithInstallConditionStatus(metav1.ConditionFalse, objectInstance.GetGeneration())) } r.Event(objectInstance, "Normal", "ResourcesDelete", "deleting resources") diff --git a/main.go b/main.go index fd934e7..7fd7a62 100644 --- a/main.go +++ b/main.go @@ -110,8 +110,9 @@ func main() { os.Exit(1) } if err = (&controllers.SampleHelmReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + EventRecorder: mgr.GetEventRecorderFor(operatorName), }).SetupWithManager(mgr, ratelimiter); err != nil { setupLog.Error(err, "unable to create controller", "controller", "SampleHelm") os.Exit(1) From 4297b929f6cba424cacf16eb0546fdc65a7e88f7 Mon Sep 17 00:00:00 2001 From: Aditya Bhatia Date: Mon, 30 Jan 2023 13:06:17 +0100 Subject: [PATCH 6/8] fix tests --- .golangci.yaml | 53 +++++++++++++++++++++ controllers/sample_local_helm_controller.go | 12 ++--- 2 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 .golangci.yaml diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..1b25a50 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,53 @@ +linters: + enable-all: true + disable: + - exhaustivestruct + - golint + - interfacer + - maligned + - scopelint + - forcetypeassert + - godox + - wsl + - nlreturn + - exhaustruct + - gci # disable because autofix is not supported https://github.com/golangci/golangci-lint/issues/2591 + - wrapcheck + - goerr113 + - bodyclose + - contextcheck + - nilerr + - noctx + - rowserrcheck + - sqlclosecheck + - structcheck + - tparallel + - unparam + - wastedassign + - ireturn + - ifshort + - varcheck + - deadcode + - nosnakecase +linters-settings: + gci: + sections: + - standard # Captures all standard packages if they do not match another section. + - default # Contains all imports that could not be matched to another section type. + - prefix(github.com/kyma-project) + funlen: + lines: 100 + statements: 50 + cyclop: + max-complexity: 15 +issues: + max-issues-per-linter: 0 + max-same-issues: 0 + fix: true +output: + sort-results: true +run: + concurrency: 8 + timeout: 5m + skip-files: + - zz_generated.deepcopy.go \ No newline at end of file diff --git a/controllers/sample_local_helm_controller.go b/controllers/sample_local_helm_controller.go index 6cff1ad..02937b6 100644 --- a/controllers/sample_local_helm_controller.go +++ b/controllers/sample_local_helm_controller.go @@ -154,12 +154,12 @@ func (r *SampleHelmReconciler) HandleDeletingState(ctx context.Context, objectIn status := objectInstance.Status resources, err := r.render(ctx, objectInstance) - if err != nil { - logger.Error(err, "error during rendering of resources during uninstallation") - r.Event(objectInstance, "Warning", "ResourcesDelete", "rendering resources error") - return r.setStatusForObjectInstance(ctx, objectInstance, status. - WithState(v1alpha1.StateError). - WithInstallConditionStatus(metav1.ConditionFalse, objectInstance.GetGeneration())) + // if there is an error during rendering, remove the CR finalizer as it cannot proceed with resources + if err != nil && controllerutil.RemoveFinalizer(objectInstance, finalizer) { + logger.Error(err, "error during rendering of resources during uninstallation, finalizer will be removed") + r.Event(objectInstance, "Warning", "ResourcesDelete", + "chart resources not removed") + return r.Client.Update(ctx, objectInstance) } r.Event(objectInstance, "Normal", "ResourcesDelete", "deleting resources") From f82fd3b4f575ccbbde17cc231565d76496558758 Mon Sep 17 00:00:00 2001 From: Aditya Bhatia Date: Mon, 30 Jan 2023 13:28:01 +0100 Subject: [PATCH 7/8] fix custom resource group naming --- .golangci.yaml | 53 ------------------- api/v1alpha1/groupversion_info.go | 4 +- api/v1alpha1/sample_types.go | 30 +++++------ api/v1alpha1/samplehelm_types.go | 8 +-- .../operator.kyma-project.io_samplehelms.yaml | 6 +-- .../operator.kyma-project.io_samples.yaml | 2 +- .../patches/cainjection_in_samplehelms.yaml | 2 +- .../crd/patches/webhook_in_samplehelms.yaml | 2 +- config/rbac/role.yaml | 18 +++---- config/rbac/samplehelm_editor_role.yaml | 4 +- config/rbac/samplehelm_viewer_role.yaml | 4 +- ...r.kyma-project.io_v1alpha1_samplehelm.yaml | 4 +- .../sample_controller_rendered_resources.go | 6 +-- ...mple_controller_rendered_resources_test.go | 3 +- controllers/sample_local_helm_controller.go | 12 ++--- .../sample_local_helm_controller_test.go | 7 ++- main.go | 3 +- 17 files changed, 56 insertions(+), 112 deletions(-) delete mode 100644 .golangci.yaml diff --git a/.golangci.yaml b/.golangci.yaml deleted file mode 100644 index 1b25a50..0000000 --- a/.golangci.yaml +++ /dev/null @@ -1,53 +0,0 @@ -linters: - enable-all: true - disable: - - exhaustivestruct - - golint - - interfacer - - maligned - - scopelint - - forcetypeassert - - godox - - wsl - - nlreturn - - exhaustruct - - gci # disable because autofix is not supported https://github.com/golangci/golangci-lint/issues/2591 - - wrapcheck - - goerr113 - - bodyclose - - contextcheck - - nilerr - - noctx - - rowserrcheck - - sqlclosecheck - - structcheck - - tparallel - - unparam - - wastedassign - - ireturn - - ifshort - - varcheck - - deadcode - - nosnakecase -linters-settings: - gci: - sections: - - standard # Captures all standard packages if they do not match another section. - - default # Contains all imports that could not be matched to another section type. - - prefix(github.com/kyma-project) - funlen: - lines: 100 - statements: 50 - cyclop: - max-complexity: 15 -issues: - max-issues-per-linter: 0 - max-same-issues: 0 - fix: true -output: - sort-results: true -run: - concurrency: 8 - timeout: 5m - skip-files: - - zz_generated.deepcopy.go \ No newline at end of file diff --git a/api/v1alpha1/groupversion_info.go b/api/v1alpha1/groupversion_info.go index dcbb910..cb2f6b3 100644 --- a/api/v1alpha1/groupversion_info.go +++ b/api/v1alpha1/groupversion_info.go @@ -32,10 +32,10 @@ const ( type Kind string var ( - // GroupVersion is group version used to register these objects + // GroupVersion is group version used to register these objects. GroupVersion = schema.GroupVersion{Group: "operator.kyma-project.io", Version: "v1alpha1"} - // SchemeBuilder is used to add go types to the GroupVersionKind scheme + // SchemeBuilder is used to add go types to the GroupVersionKind scheme. SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} // AddToScheme adds the types in this group-version to the given scheme. diff --git a/api/v1alpha1/sample_types.go b/api/v1alpha1/sample_types.go index 81aa4a0..63b28b2 100644 --- a/api/v1alpha1/sample_types.go +++ b/api/v1alpha1/sample_types.go @@ -26,21 +26,6 @@ var ( ConditionReasonReady = "Ready" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status -//+kubebuilder:printcolumn:name="State",type=string,JSONPath=".status.state" - -// Sample is the Schema for the samples API -type Sample struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec SampleSpec `json:"spec,omitempty"` - Status SampleStatus `json:"status,omitempty"` -} - type SampleStatus struct { Status `json:",inline"` @@ -83,9 +68,22 @@ type SampleSpec struct { ResourceFilePath string `json:"resourceFilePath,omitempty"` } +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:printcolumn:name="State",type=string,JSONPath=".status.state" + +// Sample is the Schema for the samples API. +type Sample struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec SampleSpec `json:"spec,omitempty"` + Status SampleStatus `json:"status,omitempty"` +} + // +kubebuilder:object:root=true -// SampleList contains a list of Sample +// SampleList contains a list of Sample. type SampleList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` diff --git a/api/v1alpha1/samplehelm_types.go b/api/v1alpha1/samplehelm_types.go index 1b6ed1e..39bc2d3 100644 --- a/api/v1alpha1/samplehelm_types.go +++ b/api/v1alpha1/samplehelm_types.go @@ -24,7 +24,7 @@ import ( // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. -// SampleHelmSpec defines the desired state of SampleHelm +// SampleHelmSpec defines the desired state of SampleHelm. type SampleHelmSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file @@ -33,7 +33,7 @@ type SampleHelmSpec struct { ChartPath string `json:"chartPath,omitempty"` } -// SampleHelmStatus defines the observed state of SampleHelm +// SampleHelmStatus defines the observed state of SampleHelm. type SampleHelmStatus struct { Status `json:",inline"` @@ -73,7 +73,7 @@ func (s *SampleHelmStatus) WithInstallConditionStatus(status metav1.ConditionSta //+kubebuilder:object:root=true //+kubebuilder:subresource:status -// SampleHelm is the Schema for the samplehelms API +// SampleHelm is the Schema for the samplehelms API. type SampleHelm struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` @@ -84,7 +84,7 @@ type SampleHelm struct { //+kubebuilder:object:root=true -// SampleHelmList contains a list of SampleHelm +// SampleHelmList contains a list of SampleHelm. type SampleHelmList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` diff --git a/config/crd/bases/operator.kyma-project.io_samplehelms.yaml b/config/crd/bases/operator.kyma-project.io_samplehelms.yaml index 5f47fd2..b3ea0c6 100644 --- a/config/crd/bases/operator.kyma-project.io_samplehelms.yaml +++ b/config/crd/bases/operator.kyma-project.io_samplehelms.yaml @@ -18,7 +18,7 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: SampleHelm is the Schema for the samplehelms API + description: SampleHelm is the Schema for the samplehelms API. properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -33,14 +33,14 @@ spec: metadata: type: object spec: - description: SampleHelmSpec defines the desired state of SampleHelm + description: SampleHelmSpec defines the desired state of SampleHelm. properties: chartPath: description: ChartPath represents the local path to the Helm chart type: string type: object status: - description: SampleHelmStatus defines the observed state of SampleHelm + description: SampleHelmStatus defines the observed state of SampleHelm. properties: conditions: description: Conditions contain a set of conditionals to determine diff --git a/config/crd/bases/operator.kyma-project.io_samples.yaml b/config/crd/bases/operator.kyma-project.io_samples.yaml index 27857ad..a785a08 100644 --- a/config/crd/bases/operator.kyma-project.io_samples.yaml +++ b/config/crd/bases/operator.kyma-project.io_samples.yaml @@ -22,7 +22,7 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: Sample is the Schema for the samples API + description: Sample is the Schema for the samples API. properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation diff --git a/config/crd/patches/cainjection_in_samplehelms.yaml b/config/crd/patches/cainjection_in_samplehelms.yaml index 0b18807..5b2c3f2 100644 --- a/config/crd/patches/cainjection_in_samplehelms.yaml +++ b/config/crd/patches/cainjection_in_samplehelms.yaml @@ -4,4 +4,4 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: samplehelms.operator.kyma-project.io.kyma-project.io + name: samplehelms.operator.kyma-project.io diff --git a/config/crd/patches/webhook_in_samplehelms.yaml b/config/crd/patches/webhook_in_samplehelms.yaml index 50eec45..21b214f 100644 --- a/config/crd/patches/webhook_in_samplehelms.yaml +++ b/config/crd/patches/webhook_in_samplehelms.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: samplehelms.operator.kyma-project.io.kyma-project.io + name: samplehelms.operator.kyma-project.io spec: conversion: strategy: Webhook diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index b0cbc14..11a61a3 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -36,7 +36,7 @@ rules: - apiGroups: - operator.kyma-project.io resources: - - samples + - samplehelms verbs: - create - delete @@ -48,21 +48,21 @@ rules: - apiGroups: - operator.kyma-project.io resources: - - samples/finalizers + - samplehelms/finalizers verbs: - update - apiGroups: - operator.kyma-project.io resources: - - samples/status + - samplehelms/status verbs: - get - patch - update - apiGroups: - - operator.kyma-project.io.kyma-project.io + - operator.kyma-project.io resources: - - samplehelms + - samples verbs: - create - delete @@ -72,15 +72,15 @@ rules: - update - watch - apiGroups: - - operator.kyma-project.io.kyma-project.io + - operator.kyma-project.io resources: - - samplehelms/finalizers + - samples/finalizers verbs: - update - apiGroups: - - operator.kyma-project.io.kyma-project.io + - operator.kyma-project.io resources: - - samplehelms/status + - samples/status verbs: - get - patch diff --git a/config/rbac/samplehelm_editor_role.yaml b/config/rbac/samplehelm_editor_role.yaml index 720c662..f8dfb08 100644 --- a/config/rbac/samplehelm_editor_role.yaml +++ b/config/rbac/samplehelm_editor_role.yaml @@ -12,7 +12,7 @@ metadata: name: samplehelm-editor-role rules: - apiGroups: - - operator.kyma-project.io.kyma-project.io + - operator.kyma-project.io resources: - samplehelms verbs: @@ -24,7 +24,7 @@ rules: - update - watch - apiGroups: - - operator.kyma-project.io.kyma-project.io + - operator.kyma-project.io resources: - samplehelms/status verbs: diff --git a/config/rbac/samplehelm_viewer_role.yaml b/config/rbac/samplehelm_viewer_role.yaml index c69b987..40a08f4 100644 --- a/config/rbac/samplehelm_viewer_role.yaml +++ b/config/rbac/samplehelm_viewer_role.yaml @@ -12,7 +12,7 @@ metadata: name: samplehelm-viewer-role rules: - apiGroups: - - operator.kyma-project.io.kyma-project.io + - operator.kyma-project.io resources: - samplehelms verbs: @@ -20,7 +20,7 @@ rules: - list - watch - apiGroups: - - operator.kyma-project.io.kyma-project.io + - operator.kyma-project.io resources: - samplehelms/status verbs: diff --git a/config/samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml b/config/samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml index 47155c2..924f1b3 100644 --- a/config/samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml +++ b/config/samples/operator.kyma-project.io_v1alpha1_samplehelm.yaml @@ -1,4 +1,4 @@ -apiVersion: operator.kyma-project.io.kyma-project.io/v1alpha1 +apiVersion: operator.kyma-project.io/v1alpha1 kind: SampleHelm metadata: labels: @@ -9,4 +9,4 @@ metadata: app.kubernetes.io/created-by: template-operator name: samplehelm-sample spec: - chartPath: "your/path" + chartPath: "./module-chart" diff --git a/controllers/sample_controller_rendered_resources.go b/controllers/sample_controller_rendered_resources.go index 9ce22fd..506119a 100644 --- a/controllers/sample_controller_rendered_resources.go +++ b/controllers/sample_controller_rendered_resources.go @@ -41,7 +41,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller" ) -// SampleReconciler reconciles a Sample object +// SampleReconciler reconciles a Sample object. type SampleReconciler struct { client.Client Scheme *runtime.Scheme @@ -290,7 +290,7 @@ func getResourcesFromLocalPath(dirPath string, logger logr.Logger) (*ManifestRes return parseManifestStringToObjects(string(fileBytes)) } -// ssaStatus patches status using SSA on the passed object +// ssaStatus patches status using SSA on the passed object. func (r *SampleReconciler) ssaStatus(ctx context.Context, obj client.Object) error { obj.SetManagedFields(nil) obj.SetResourceVersion("") @@ -298,7 +298,7 @@ func (r *SampleReconciler) ssaStatus(ctx context.Context, obj client.Object) err &client.SubResourcePatchOptions{PatchOptions: client.PatchOptions{FieldManager: fieldOwner}}) } -// ssa patches the object using SSA +// ssa patches the object using SSA. func (r *SampleReconciler) ssa(ctx context.Context, obj client.Object) error { obj.SetManagedFields(nil) obj.SetResourceVersion("") diff --git a/controllers/sample_controller_rendered_resources_test.go b/controllers/sample_controller_rendered_resources_test.go index 292c857..8e07307 100644 --- a/controllers/sample_controller_rendered_resources_test.go +++ b/controllers/sample_controller_rendered_resources_test.go @@ -23,7 +23,8 @@ var ( ) func testFn(sampleCR *v1alpha1.Sample, desiredState v1alpha1.State, desiredConditionStatus metav1.ConditionStatus, - resourceCheck func(g Gomega) bool) { + resourceCheck func(g Gomega) bool, +) { // create SampleCR Expect(k8sClient.Create(ctx, sampleCR)).To(Succeed()) diff --git a/controllers/sample_local_helm_controller.go b/controllers/sample_local_helm_controller.go index 02937b6..a2ad158 100644 --- a/controllers/sample_local_helm_controller.go +++ b/controllers/sample_local_helm_controller.go @@ -41,7 +41,7 @@ import ( var CustomNs = "helm-custom-ns" -// SampleHelmReconciler reconciles a SampleHelm object +// SampleHelmReconciler reconciles a SampleHelm object. type SampleHelmReconciler struct { client.Client Scheme *runtime.Scheme @@ -51,9 +51,9 @@ type SampleHelmReconciler struct { Config *rest.Config } -//+kubebuilder:rbac:groups=operator.kyma-project.io.kyma-project.io,resources=samplehelms,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=operator.kyma-project.io.kyma-project.io,resources=samplehelms/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=operator.kyma-project.io.kyma-project.io,resources=samplehelms/finalizers,verbs=update +//+kubebuilder:rbac:groups=operator.kyma-project.io,resources=samplehelms,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=operator.kyma-project.io,resources=samplehelms/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=operator.kyma-project.io,resources=samplehelms/finalizers,verbs=update // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -213,7 +213,7 @@ func (r *SampleHelmReconciler) setStatusForObjectInstance(ctx context.Context, o return nil } -// ssaStatus patches status using SSA on the passed object +// ssaStatus patches status using SSA on the passed object. func (r *SampleHelmReconciler) ssaStatus(ctx context.Context, obj client.Object) error { obj.SetManagedFields(nil) obj.SetResourceVersion("") @@ -221,7 +221,7 @@ func (r *SampleHelmReconciler) ssaStatus(ctx context.Context, obj client.Object) &client.SubResourcePatchOptions{PatchOptions: client.PatchOptions{FieldManager: fieldOwner}}) } -// ssa patches the object using SSA +// ssa patches the object using SSA. func (r *SampleHelmReconciler) ssa(ctx context.Context, obj client.Object) error { obj.SetManagedFields(nil) obj.SetResourceVersion("") diff --git a/controllers/sample_local_helm_controller_test.go b/controllers/sample_local_helm_controller_test.go index 62115e9..5494b87 100644 --- a/controllers/sample_local_helm_controller_test.go +++ b/controllers/sample_local_helm_controller_test.go @@ -16,12 +16,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -var ( - helmPodName = "busybox-helm-pod" -) +var helmPodName = "busybox-helm-pod" func testHelmFn(sampleCR *v1alpha1.SampleHelm, desiredState v1alpha1.State, desiredConditionStatus metav1.ConditionStatus, - resourceCheck func(g Gomega) bool) { + resourceCheck func(g Gomega) bool, +) { // create SampleCR Expect(k8sClient.Create(ctx, sampleCR)).To(Succeed()) diff --git a/main.go b/main.go index 7fd7a62..a15c348 100644 --- a/main.go +++ b/main.go @@ -113,6 +113,7 @@ func main() { Client: mgr.GetClient(), Scheme: mgr.GetScheme(), EventRecorder: mgr.GetEventRecorderFor(operatorName), + Config: mgr.GetConfig(), }).SetupWithManager(mgr, ratelimiter); err != nil { setupLog.Error(err, "unable to create controller", "controller", "SampleHelm") os.Exit(1) @@ -150,7 +151,5 @@ func defineFlagVar() *FlagVar { "Indicates the failure base delay in seconds for rate limiter.") flag.DurationVar(&flagVar.failureMaxDelay, "failure-max-delay", failureMaxDelayDefault, "Indicates the failure max delay in seconds") - flag.StringVar(&flagVar.chartPath, "module-chart-path", chartPath, - "Represents path containing chart to be installed") return flagVar } From df13ac6ed487fc0be02cf2240f2804350852ce8a Mon Sep 17 00:00:00 2001 From: Aditya Bhatia Date: Mon, 30 Jan 2023 14:22:58 +0100 Subject: [PATCH 8/8] NS fix for helm resources --- main.go | 2 -- module-chart/templates/configmap.yaml | 2 +- module-chart/templates/deployment.yaml | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/main.go b/main.go index a15c348..0f0dcdc 100644 --- a/main.go +++ b/main.go @@ -43,7 +43,6 @@ const ( rateLimiterFrequencyDefault = 30 failureBaseDelayDefault = 1 * time.Second failureMaxDelayDefault = 1000 * time.Second - chartPath = "./module-chart" operatorName = "template-operator" ) @@ -60,7 +59,6 @@ type FlagVar struct { failureMaxDelay time.Duration rateLimiterFrequency int rateLimiterBurst int - chartPath string } func init() { //nolint:gochecknoinits diff --git a/module-chart/templates/configmap.yaml b/module-chart/templates/configmap.yaml index 60fac52..aef7821 100644 --- a/module-chart/templates/configmap.yaml +++ b/module-chart/templates/configmap.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: ConfigMap metadata: name: test-redis-config - namespace: redis + namespace: helm-custom-ns data: redis-config: | maxmemory 2mb diff --git a/module-chart/templates/deployment.yaml b/module-chart/templates/deployment.yaml index 8216785..2608d86 100644 --- a/module-chart/templates/deployment.yaml +++ b/module-chart/templates/deployment.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: {{.Values.nameOverride}} - namespace: redis + namespace: helm-custom-ns spec: selector: matchLabels: