From 9e31638988034c6721d602a3ad38bc13a43fa808 Mon Sep 17 00:00:00 2001 From: Lubron Date: Mon, 20 Dec 2021 17:47:05 -0800 Subject: [PATCH] Initial movement of the day2 code (#807) * Inital movement of the day2 code Signed-off-by: lubronzhan Fix commit Signed-off-by: lubronzhan Fix diff Signed-off-by: lubronzhan Fix md-lint Signed-off-by: lubronzhan Try add role for psb and hsc Signed-off-by: lubronzhan Fix the group name Signed-off-by: lubronzhan add namespace controller and psb controller Signed-off-by: lubronzhan Add webhook Signed-off-by: lubronzhan Add harbor-day2-webhook-configuration label to namespace Signed-off-by: lubronzhan Debug: Signed-off-by: lubronzhan Refactor Signed-off-by: lubronzhan Remove harbor sdk Signed-off-by: lubronzhan Update API to use the go-client * Refactor based on new harbor go-client Signed-off-by: lubronzhan * Revert the webhook logic to by default not include namespace: Signed-off-by: lubronzhan * Add missing namespaceSelector to the chart manifest. Update readme Signed-off-by: lubronzhan --- .github/workflows/tests.yml | 1 + PROJECT | 8 +- README.md | 2 +- .../harborserverconfiguration_types.go | 116 ++++++ .../v1beta1/pullsecretbinding_types.go | 85 ++++ .../v1beta1/zz_generated.deepcopy.go | 208 ++++++++++ .../templates/clusterrole.yaml | 70 ++++ charts/harbor-operator/templates/crds.yaml | 237 +++++++++++ .../mutatingwebhookconfiguration.yaml | 27 ++ .../validatingwebhookconfiguration.yaml | 21 + config/crd/kustomization.yaml | 2 + ...jection_in_harborserverconfigurations.yaml | 8 + .../cainjection_in_pullsecretbindings.yaml | 8 + ...webhook_in_harborserverconfigurations.yaml | 17 + .../webhook_in_pullsecretbindings.yaml | 17 + config/default/kustomization.yaml | 6 + .../webhook_namespaceselector_patch.yaml | 13 + .../mutatingwebhook_endpoint_patch.yaml | 13 + .../validatingwebhook_endpoint_patch.yaml | 6 + controllers/controller_string.go | 7 +- controllers/controllers.go | 31 +- .../harborserverconfiguration.go | 138 +++++++ .../harborserverconfiguration/resources.go | 16 + controllers/goharbor/namespace/namespace.go | 382 ++++++++++++++++++ controllers/goharbor/namespace/resources.go | 16 + .../pullsecretbinding/pullsecretbinding.go | 366 +++++++++++++++++ .../goharbor/pullsecretbinding/resources.go | 17 + docs/arch/day2-op.md | 4 +- docs/configurations/day2-config.md | 339 +++++++++++++--- go.mod | 6 +- go.sum | 266 +++++++++++- manifests/cluster/deployment.yaml | 357 ++++++++++++++++ manifests/cluster/patch/namespace.yaml | 2 +- manifests/harbor/deployment.yaml | 357 ++++++++++++++++ manifests/harbor/namespace.yaml | 2 +- .../database/api/zz_generated.deepcopy.go | 1 - .../minio.min.io/v2/zz_generated.deepcopy.go | 1 - pkg/registry/secret/object.go | 29 ++ pkg/rest/client.go | 55 +++ pkg/rest/model/harbor_server.go | 85 ++++ pkg/rest/v2/client.go | 276 +++++++++++++ pkg/rule/rule.go | 33 ++ pkg/setup/controllers.go | 10 +- pkg/setup/setup.go | 23 ++ pkg/utils/consts/annotations.go | 29 ++ pkg/utils/strings/name.go | 32 +- pkg/utils/strings/strings.go | 25 ++ webhooks/harborserverconfiguration/webhook.go | 70 ++++ webhooks/pod/containers.go | 69 ++++ webhooks/pod/containers_test.go | 158 ++++++++ webhooks/pod/mutate_image_path.go | 240 +++++++++++ 51 files changed, 4223 insertions(+), 84 deletions(-) create mode 100644 apis/goharbor.io/v1beta1/harborserverconfiguration_types.go create mode 100644 apis/goharbor.io/v1beta1/pullsecretbinding_types.go create mode 100644 config/crd/patches/cainjection_in_harborserverconfigurations.yaml create mode 100644 config/crd/patches/cainjection_in_pullsecretbindings.yaml create mode 100644 config/crd/patches/webhook_in_harborserverconfigurations.yaml create mode 100644 config/crd/patches/webhook_in_pullsecretbindings.yaml create mode 100644 config/default/webhook_namespaceselector_patch.yaml create mode 100644 controllers/goharbor/harborserverconfiguration/harborserverconfiguration.go create mode 100644 controllers/goharbor/harborserverconfiguration/resources.go create mode 100644 controllers/goharbor/namespace/namespace.go create mode 100644 controllers/goharbor/namespace/resources.go create mode 100644 controllers/goharbor/pullsecretbinding/pullsecretbinding.go create mode 100644 controllers/goharbor/pullsecretbinding/resources.go create mode 100644 pkg/registry/secret/object.go create mode 100644 pkg/rest/client.go create mode 100644 pkg/rest/model/harbor_server.go create mode 100644 pkg/rest/v2/client.go create mode 100644 pkg/rule/rule.go create mode 100644 pkg/utils/consts/annotations.go create mode 100644 pkg/utils/strings/strings.go create mode 100644 webhooks/harborserverconfiguration/webhook.go create mode 100644 webhooks/pod/containers.go create mode 100644 webhooks/pod/containers_test.go create mode 100644 webhooks/pod/mutate_image_path.go diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 555bd5be9..8b055c02d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -261,6 +261,7 @@ jobs: kubectl -n cluster-sample-ns get all -o wide kubectl get harbor -n cluster-sample-ns -o wide kubectl get harborcluster -n cluster-sample-ns -o wide + kubectl get all -n ${operatorNamespace} fi free -h df -h diff --git a/PROJECT b/PROJECT index 83c81fb1d..3ae479992 100644 --- a/PROJECT +++ b/PROJECT @@ -67,5 +67,11 @@ resources: version: v1beta1 - group: goharbor kind: HarborCluster - version: v1beta1 + version: v1beta1 +- group: goharbor + kind: HarborServerConfiguration + version: v1alpha1 +- group: goharbor + kind: PullSecretBinding + version: v1alpha1 version: "2" diff --git a/README.md b/README.md index c1d6170c9..530e9ab69 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Harbor deployment stack is controlled by a custom Harbor resource `HarborCluster * [ ] [gcs](https://cloud.google.com/storage): A driver storing objects in a Google Cloud Storage bucket. * Supports updating the deployed Harbor cluster * Remove the optional Harbor components -* More day2 operations (see [PoC project](https://github.com/szlabs/harbor-automation-4k8s)) +* More day2 operations (see [PoC project](https://github.com/goharbor/harbor-operator)) * Auto mapping Kubernetes namespaces and Harbor project * Pull secrets injections * Container image path rewriting diff --git a/apis/goharbor.io/v1beta1/harborserverconfiguration_types.go b/apis/goharbor.io/v1beta1/harborserverconfiguration_types.go new file mode 100644 index 000000000..f7fc2b446 --- /dev/null +++ b/apis/goharbor.io/v1beta1/harborserverconfiguration_types.go @@ -0,0 +1,116 @@ +package v1beta1 + +import ( + 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. + +// HarborServerConfigurationSpec defines the desired state of HarborServerConfiguration. +type HarborServerConfigurationSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // +kubebuilder:validation:Required + // +kubebuilder:validation:Pattern="(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)+([A-Za-z]|[A-Za-z][A-Za-z0-9\\-]*[A-Za-z0-9])" + ServerURL string `json:"serverURL"` + + // Indicate if the Harbor server is an insecure registry + // +kubebuilder:validation:Optional + Insecure bool `json:"insecure,omitempty"` + + // Default indicates the harbor configuration manages namespaces. + // Value in goharbor.io/harbor annotation will be considered with high priority. + // At most, one HarborServerConfiguration can be the default, multiple defaults will be rejected. + // +kubebuilder:validation:Required + Default bool `json:"default,omitempty"` + + // +kubebuilder:validation:Required + AccessCredential *AccessCredential `json:"accessCredential"` + + // The version of the Harbor server + // +kubebuilder:validation:Required + // +kubebuilder:validation:Pattern="(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?" + Version string `json:"version"` + + // Rules configures the container image rewrite rules for transparent proxy caching with Harbor. + // +kubebuilder:validation:Optional + Rules []string `json:"rules,omitempty"` + + // NamespaceSelector decides whether to apply the HSC on a namespace based + // on whether the namespace matches the selector. + // See + // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + // for more examples of label selectors. + // + // Default to the empty LabelSelector, which matches everything. + // +optional + NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty"` +} + +// AccessCredential is a namespaced credential to keep the access key and secret for the harbor server configuration. +type AccessCredential struct { + // +kubebuilder:validation:Required + // +kubebuilder:validation:Pattern="[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*" + Namespace string `json:"namespace"` + + // +kubebuilder:validation:Required + // +kubebuilder:validation:Pattern="[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*" + AccessSecretRef string `json:"accessSecretRef"` +} + +// HarborServerConfigurationStatusType defines the status type of configuration. +type HarborServerConfigurationStatusType string + +const ( + // HarborServerConfigurationStatusReady represents ready status. + HarborServerConfigurationStatusReady HarborServerConfigurationStatusType = "Success" + // HarborServerConfigurationStatusFail represents fail status. + HarborServerConfigurationStatusFail HarborServerConfigurationStatusType = "Fail" + // HarborServerConfigurationStatusUnknown represents unknown status. + HarborServerConfigurationStatusUnknown HarborServerConfigurationStatusType = "Unknown" +) + +// HarborConfigurationStatus defines the status of HarborServerConfiguration. +type HarborServerConfigurationStatus struct { + // Status represents harbor configuration status. + // +kubebuilder:validation:Optional + Status HarborServerConfigurationStatusType `json:"status,omitempty"` + // Reason represents status reason. + // +kubebuilder:validation:Optional + Reason string `json:"reason,omitempty"` + // Message provides human-readable message. + // +kubebuilder:validation:Optional + Message string `json:"message,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:storageversion +// +k8s:openapi-gen=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:categories="goharbor",shortName="hsc",scope="Cluster" +// +kubebuilder:printcolumn:name="Harbor Server",type=string,JSONPath=`.spec.serverURL`,description="The public URL to the Harbor server",priority=0 +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status`,description="The status of the Harbor server",priority=0 +// +kubebuilder:printcolumn:name="Version",type=string,JSONPath=`.spec.version`,description="The version of the Harbor server",priority=5 +// HarborServerConfiguration is the Schema for the harborserverconfigurations API. +type HarborServerConfiguration struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec HarborServerConfigurationSpec `json:"spec,omitempty"` + Status HarborServerConfigurationStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// HarborServerConfigurationList contains a list of HarborServerConfiguration. +type HarborServerConfigurationList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []HarborServerConfiguration `json:"items"` +} + +func init() { // nolint:gochecknoinits + SchemeBuilder.Register(&HarborServerConfiguration{}, &HarborServerConfigurationList{}) +} diff --git a/apis/goharbor.io/v1beta1/pullsecretbinding_types.go b/apis/goharbor.io/v1beta1/pullsecretbinding_types.go new file mode 100644 index 000000000..6301c75e8 --- /dev/null +++ b/apis/goharbor.io/v1beta1/pullsecretbinding_types.go @@ -0,0 +1,85 @@ +package v1beta1 + +import ( + 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. + +// PullSecretBindingSpec defines the desired state of PullSecretBinding. +type PullSecretBindingSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // RobotID points to the robot account id used for secret binding + // +kubebuilder:validation:Required + RobotID string `json:"robotId"` + + // ProjectID points to the project associated with the secret binding + // +kubebuilder:validation:Required + ProjectID string `json:"projectId"` + + // Indicate which harbor server configuration is referred + HarborServerConfig string `json:"harborServerConfig"` + + // Indicate which service account binds the pull secret + ServiceAccount string `json:"serviceAccount"` +} + +// PullSecretBindingStatusType defines the status type of configuration. +type PullSecretBindingStatusType string + +const ( + // PullSecretBindingStatusBinding represents ready status. + PullSecretBindingStatusBinding PullSecretBindingStatusType = "Binding" + // PullSecretBindingStatusBound represents fail status. + PullSecretBindingStatusBound PullSecretBindingStatusType = "Bound" + // PullSecretBindingStatusUnknown represents unknown status. + PullSecretBindingStatusUnknown PullSecretBindingStatusType = "Unknown" +) + +// PullSecretBindingStatus defines the observed state of PullSecretBinding. +type PullSecretBindingStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + // Indicate the status of binding: `binding`, `bound` and `unknown` + Status PullSecretBindingStatusType `json:"status"` + // Reason represents status reason. + // +kubebuilder:validation:Optional + Reason string `json:"reason,omitempty"` + // Message provides human-readable message. + // +kubebuilder:validation:Optional + Message string `json:"message,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:storageversion +// +k8s:openapi-gen=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:categories="goharbor",shortName="psb" +// +kubebuilder:printcolumn:name="Harbor Server",type=string,JSONPath=`.spec.harborServerConfig`,description="The Harbor server configuration CR reference",priority=0 +// +kubebuilder:printcolumn:name="Service Account",type=string,JSONPath=`.spec.serviceAccount`,description="The service account binding the pull secret",priority=0 +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status`,description="The status of the Harbor server",priority=0 + +// PullSecretBinding is the Schema for the pullsecretbindings API. +type PullSecretBinding struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PullSecretBindingSpec `json:"spec,omitempty"` + Status PullSecretBindingStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// PullSecretBindingList contains a list of PullSecretBinding. +type PullSecretBindingList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []PullSecretBinding `json:"items"` +} + +func init() { // nolint:gochecknoinits + SchemeBuilder.Register(&PullSecretBinding{}, &PullSecretBindingList{}) +} diff --git a/apis/goharbor.io/v1beta1/zz_generated.deepcopy.go b/apis/goharbor.io/v1beta1/zz_generated.deepcopy.go index d07b171d6..1970d1b16 100644 --- a/apis/goharbor.io/v1beta1/zz_generated.deepcopy.go +++ b/apis/goharbor.io/v1beta1/zz_generated.deepcopy.go @@ -11,6 +11,21 @@ import ( "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AccessCredential) DeepCopyInto(out *AccessCredential) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AccessCredential. +func (in *AccessCredential) DeepCopy() *AccessCredential { + if in == nil { + return nil + } + out := new(AccessCredential) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AzureSpec) DeepCopyInto(out *AzureSpec) { *out = *in @@ -1969,6 +1984,110 @@ func (in *HarborProxySpec) DeepCopy() *HarborProxySpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HarborServerConfiguration) DeepCopyInto(out *HarborServerConfiguration) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HarborServerConfiguration. +func (in *HarborServerConfiguration) DeepCopy() *HarborServerConfiguration { + if in == nil { + return nil + } + out := new(HarborServerConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HarborServerConfiguration) 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 *HarborServerConfigurationList) DeepCopyInto(out *HarborServerConfigurationList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]HarborServerConfiguration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HarborServerConfigurationList. +func (in *HarborServerConfigurationList) DeepCopy() *HarborServerConfigurationList { + if in == nil { + return nil + } + out := new(HarborServerConfigurationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HarborServerConfigurationList) 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 *HarborServerConfigurationSpec) DeepCopyInto(out *HarborServerConfigurationSpec) { + *out = *in + if in.AccessCredential != nil { + in, out := &in.AccessCredential, &out.AccessCredential + *out = new(AccessCredential) + **out = **in + } + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HarborServerConfigurationSpec. +func (in *HarborServerConfigurationSpec) DeepCopy() *HarborServerConfigurationSpec { + if in == nil { + return nil + } + out := new(HarborServerConfigurationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HarborServerConfigurationStatus) DeepCopyInto(out *HarborServerConfigurationStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HarborServerConfigurationStatus. +func (in *HarborServerConfigurationStatus) DeepCopy() *HarborServerConfigurationStatus { + if in == nil { + return nil + } + out := new(HarborServerConfigurationStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HarborSpec) DeepCopyInto(out *HarborSpec) { *out = *in @@ -3005,6 +3124,95 @@ func (in *PostgreSQLSpec) DeepCopy() *PostgreSQLSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PullSecretBinding) DeepCopyInto(out *PullSecretBinding) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PullSecretBinding. +func (in *PullSecretBinding) DeepCopy() *PullSecretBinding { + if in == nil { + return nil + } + out := new(PullSecretBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PullSecretBinding) 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 *PullSecretBindingList) DeepCopyInto(out *PullSecretBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PullSecretBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PullSecretBindingList. +func (in *PullSecretBindingList) DeepCopy() *PullSecretBindingList { + if in == nil { + return nil + } + out := new(PullSecretBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PullSecretBindingList) 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 *PullSecretBindingSpec) DeepCopyInto(out *PullSecretBindingSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PullSecretBindingSpec. +func (in *PullSecretBindingSpec) DeepCopy() *PullSecretBindingSpec { + if in == nil { + return nil + } + out := new(PullSecretBindingSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PullSecretBindingStatus) DeepCopyInto(out *PullSecretBindingStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PullSecretBindingStatus. +func (in *PullSecretBindingStatus) DeepCopy() *PullSecretBindingStatus { + if in == nil { + return nil + } + out := new(PullSecretBindingStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RedisFailoverSpec) DeepCopyInto(out *RedisFailoverSpec) { *out = *in diff --git a/charts/harbor-operator/templates/clusterrole.yaml b/charts/harbor-operator/templates/clusterrole.yaml index 145eab601..65a8e4ea9 100644 --- a/charts/harbor-operator/templates/clusterrole.yaml +++ b/charts/harbor-operator/templates/clusterrole.yaml @@ -45,6 +45,26 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - namespaces + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - namespaces/status + verbs: + - get + - patch + - update - apiGroups: - "" resources: @@ -105,6 +125,16 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - get + - list + - patch + - update + - watch - apiGroups: - "" resources: @@ -397,6 +427,26 @@ rules: - get - patch - update +- apiGroups: + - goharbor.io + resources: + - harborserverconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - goharbor.io + resources: + - harborserverconfigurations/status + verbs: + - get + - patch + - update - apiGroups: - goharbor.io resources: @@ -461,6 +511,26 @@ rules: - get - patch - update +- apiGroups: + - goharbor.io + resources: + - pullsecretbindings + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - goharbor.io + resources: + - pullsecretbindings/status + verbs: + - get + - patch + - update - apiGroups: - goharbor.io resources: diff --git a/charts/harbor-operator/templates/crds.yaml b/charts/harbor-operator/templates/crds.yaml index 03968d611..a59c8e061 100644 --- a/charts/harbor-operator/templates/crds.yaml +++ b/charts/harbor-operator/templates/crds.yaml @@ -12052,6 +12052,148 @@ status: --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: '{{.Release.Namespace}}/{{. | include "chart.fullname"}}-serving-cert' + controller-gen.kubebuilder.io/version: v0.5.0 + creationTimestamp: null + name: harborserverconfigurations.goharbor.io +spec: + group: goharbor.io + names: + categories: + - goharbor + kind: HarborServerConfiguration + listKind: HarborServerConfigurationList + plural: harborserverconfigurations + shortNames: + - hsc + singular: harborserverconfiguration + scope: Cluster + versions: + - additionalPrinterColumns: + - description: The public URL to the Harbor server + jsonPath: .spec.serverURL + name: Harbor Server + type: string + - description: The status of the Harbor server + jsonPath: .status.status + name: Status + type: string + - description: The version of the Harbor server + jsonPath: .spec.version + name: Version + priority: 5 + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: HarborServerConfiguration is the Schema for the harborserverconfigurations 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: HarborServerConfigurationSpec defines the desired state of HarborServerConfiguration. + properties: + accessCredential: + description: AccessCredential is a namespaced credential to keep the access key and secret for the harbor server configuration. + properties: + accessSecretRef: + pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*' + type: string + namespace: + pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*' + type: string + required: + - accessSecretRef + - namespace + type: object + default: + description: Default indicates the harbor configuration manages namespaces. Value in goharbor.io/harbor annotation will be considered with high priority. At most, one HarborServerConfiguration can be the default, multiple defaults will be rejected. + type: boolean + insecure: + description: Indicate if the Harbor server is an insecure registry + type: boolean + namespaceSelector: + description: "NamespaceSelector decides whether to apply the HSC on a namespace based on whether the namespace matches the selector. See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors. \n Default to the empty LabelSelector, which matches everything." + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + rules: + description: Rules configures the container image rewrite rules for transparent proxy caching with Harbor. + items: + type: string + type: array + serverURL: + pattern: (?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)+([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9]) + type: string + version: + description: The version of the Harbor server + pattern: (0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))? + type: string + required: + - accessCredential + - serverURL + - version + type: object + status: + description: HarborConfigurationStatus defines the status of HarborServerConfiguration. + properties: + message: + description: Message provides human-readable message. + type: string + reason: + description: Reason represents status reason. + type: string + status: + description: Status represents harbor configuration status. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: '{{.Release.Namespace}}/{{. | include "chart.fullname"}}-serving-cert' @@ -18149,6 +18291,101 @@ status: --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: '{{.Release.Namespace}}/{{. | include "chart.fullname"}}-serving-cert' + controller-gen.kubebuilder.io/version: v0.5.0 + creationTimestamp: null + name: pullsecretbindings.goharbor.io +spec: + group: goharbor.io + names: + categories: + - goharbor + kind: PullSecretBinding + listKind: PullSecretBindingList + plural: pullsecretbindings + shortNames: + - psb + singular: pullsecretbinding + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The Harbor server configuration CR reference + jsonPath: .spec.harborServerConfig + name: Harbor Server + type: string + - description: The service account binding the pull secret + jsonPath: .spec.serviceAccount + name: Service Account + type: string + - description: The status of the Harbor server + jsonPath: .status.status + name: Status + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: PullSecretBinding is the Schema for the pullsecretbindings 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: PullSecretBindingSpec defines the desired state of PullSecretBinding. + properties: + harborServerConfig: + description: Indicate which harbor server configuration is referred + type: string + projectId: + description: ProjectID points to the project associated with the secret binding + type: string + robotId: + description: RobotID points to the robot account id used for secret binding + type: string + serviceAccount: + description: Indicate which service account binds the pull secret + type: string + required: + - harborServerConfig + - projectId + - robotId + - serviceAccount + type: object + status: + description: PullSecretBindingStatus defines the observed state of PullSecretBinding. + properties: + message: + description: Message provides human-readable message. + type: string + reason: + description: Reason represents status reason. + type: string + status: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file Indicate the status of binding: `binding`, `bound` and `unknown`' + type: string + required: + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: '{{.Release.Namespace}}/{{. | include "chart.fullname"}}-serving-cert' diff --git a/charts/harbor-operator/templates/mutatingwebhookconfiguration.yaml b/charts/harbor-operator/templates/mutatingwebhookconfiguration.yaml index bc3a2c91b..99b7f6c1b 100644 --- a/charts/harbor-operator/templates/mutatingwebhookconfiguration.yaml +++ b/charts/harbor-operator/templates/mutatingwebhookconfiguration.yaml @@ -28,3 +28,30 @@ webhooks: resources: - harborclusters sideEffects: None +- admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: {{ include "chart.fullname" . | quote }} + namespace: {{ .Release.Namespace | quote }} + path: /mutate-image-path + port: {{ .Values.service.port }} + failurePolicy: Fail + name: mimg.kb.io + namespaceSelector: + matchExpressions: + - key: harbor-day2-webhook-configuration + operator: In + values: + - enabled + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - pods + sideEffects: NoneOnDryRun diff --git a/charts/harbor-operator/templates/validatingwebhookconfiguration.yaml b/charts/harbor-operator/templates/validatingwebhookconfiguration.yaml index 47618bda1..a1a5564da 100644 --- a/charts/harbor-operator/templates/validatingwebhookconfiguration.yaml +++ b/charts/harbor-operator/templates/validatingwebhookconfiguration.yaml @@ -94,3 +94,24 @@ webhooks: resources: - harborclusters sideEffects: None +- admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: {{ include "chart.fullname" . | quote }} + namespace: {{ .Release.Namespace | quote }} + path: /validate-hsc + port: {{ .Values.service.port }} + failurePolicy: Fail + name: hsc.goharbor.io + rules: + - apiGroups: + - goharbor.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - harborserverconfigurations + sideEffects: None diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 4db9d1003..fc9fd71dd 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -15,6 +15,8 @@ resources: - bases/goharbor.io_trivies.yaml - bases/goharbor.io_harborclusters.yaml - bases/goharbor.io_harborconfigurations.yaml + - bases/goharbor.io_harborserverconfigurations.yaml + - bases/goharbor.io_pullsecretbindings.yaml # +kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: diff --git a/config/crd/patches/cainjection_in_harborserverconfigurations.yaml b/config/crd/patches/cainjection_in_harborserverconfigurations.yaml new file mode 100644 index 000000000..bf0bb5d36 --- /dev/null +++ b/config/crd/patches/cainjection_in_harborserverconfigurations.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: harborserverconfigurations.goharbor.io diff --git a/config/crd/patches/cainjection_in_pullsecretbindings.yaml b/config/crd/patches/cainjection_in_pullsecretbindings.yaml new file mode 100644 index 000000000..778c0827d --- /dev/null +++ b/config/crd/patches/cainjection_in_pullsecretbindings.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: pullsecretbindings.goharbor.io diff --git a/config/crd/patches/webhook_in_harborserverconfigurations.yaml b/config/crd/patches/webhook_in_harborserverconfigurations.yaml new file mode 100644 index 000000000..7850fc5a8 --- /dev/null +++ b/config/crd/patches/webhook_in_harborserverconfigurations.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: harborserverconfigurations.goharbor.io +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/config/crd/patches/webhook_in_pullsecretbindings.yaml b/config/crd/patches/webhook_in_pullsecretbindings.yaml new file mode 100644 index 000000000..9efe24c4e --- /dev/null +++ b/config/crd/patches/webhook_in_pullsecretbindings.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: pullsecretbindings.goharbor.io +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 6a49a04fd..564d60b66 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -33,6 +33,12 @@ patchesStrategicMerge: # 'CERTMANAGER' needs to be enabled to use ca injection - webhookcainjection_patch.yaml +# [WEBHOOK] To add namespaceselector to weebhook. +# so that webhook won't check namespaces with specific labels +# avoid blocking controller coming up +- webhook_namespaceselector_patch.yaml + + # the following config is for teaching kustomize how to do var substitution vars: # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. diff --git a/config/default/webhook_namespaceselector_patch.yaml b/config/default/webhook_namespaceselector_patch.yaml new file mode 100644 index 000000000..3ca9ca752 --- /dev/null +++ b/config/default/webhook_namespaceselector_patch.yaml @@ -0,0 +1,13 @@ +# This patch add namespace selector to admission webhook config and +# resources under namespace with this label to trigger this webhook +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: mutating-webhook-configuration +webhooks: +- name: mimg.kb.io + namespaceSelector: + matchExpressions: + - key: harbor-day2-webhook-configuration + operator: In + values: ["enabled"] diff --git a/config/helm/webhook/mutatingwebhook_endpoint_patch.yaml b/config/helm/webhook/mutatingwebhook_endpoint_patch.yaml index c9c71516a..b158d2d88 100644 --- a/config/helm/webhook/mutatingwebhook_endpoint_patch.yaml +++ b/config/helm/webhook/mutatingwebhook_endpoint_patch.yaml @@ -10,3 +10,16 @@ webhooks: name: '{{ include "chart.fullname" . | quote }}' namespace: '{{ .Release.Namespace | quote }}' port: '{{ .Values.service.port }}' +- name: mimg.kb.io + clientConfig: + service: + name: '{{ include "chart.fullname" . | quote }}' + namespace: '{{ .Release.Namespace | quote }}' + port: '{{ .Values.service.port }}' + # This patch add namespace selector to admission webhook config and + # resources under namespace with this label to trigger this webhook + namespaceSelector: + matchExpressions: + - key: harbor-day2-webhook-configuration + operator: In + values: ["enabled"] diff --git a/config/helm/webhook/validatingwebhook_endpoint_patch.yaml b/config/helm/webhook/validatingwebhook_endpoint_patch.yaml index a4bf56cda..6aa9ae13a 100644 --- a/config/helm/webhook/validatingwebhook_endpoint_patch.yaml +++ b/config/helm/webhook/validatingwebhook_endpoint_patch.yaml @@ -28,3 +28,9 @@ webhooks: name: '{{ include "chart.fullname" . | quote }}' namespace: '{{ .Release.Namespace | quote }}' port: '{{ .Values.service.port }}' +- name: hsc.goharbor.io + clientConfig: + service: + name: '{{ include "chart.fullname" . | quote }}' + namespace: '{{ .Release.Namespace | quote }}' + port: '{{ .Values.service.port }}' diff --git a/controllers/controller_string.go b/controllers/controller_string.go index c108947e6..293fd907c 100644 --- a/controllers/controller_string.go +++ b/controllers/controller_string.go @@ -22,11 +22,14 @@ func _() { _ = x[HarborCluster-11] _ = x[HarborConfigurationCm-12] _ = x[HarborConfiguration-13] + _ = x[HarborServerConfiguration-14] + _ = x[PullSecretBinding-15] + _ = x[Namespace-16] } -const _Controller_name = "corejobserviceportalregistryregistryctlchartmuseumexporternotaryservernotarysignertrivyharborharborclusterharborconfigurationcmharborconfiguration" +const _Controller_name = "corejobserviceportalregistryregistryctlchartmuseumexporternotaryservernotarysignertrivyharborharborclusterharborconfigurationcmharborconfigurationharborserverconfigurationpullsecretbindingnamespace" -var _Controller_index = [...]uint8{0, 4, 14, 20, 28, 39, 50, 58, 70, 82, 87, 93, 106, 127, 146} +var _Controller_index = [...]uint8{0, 4, 14, 20, 28, 39, 50, 58, 70, 82, 87, 93, 106, 127, 146, 171, 188, 197} func (i Controller) String() string { if i < 0 || i >= Controller(len(_Controller_index)-1) { diff --git a/controllers/controllers.go b/controllers/controllers.go index aeaf877c1..b9eb56bf2 100644 --- a/controllers/controllers.go +++ b/controllers/controllers.go @@ -11,20 +11,23 @@ import ( type Controller int const ( - Core Controller = iota // core - JobService // jobservice - Portal // portal - Registry // registry - RegistryController // registryctl - ChartMuseum // chartmuseum - Exporter // exporter - NotaryServer // notaryserver - NotarySigner // notarysigner - Trivy // trivy - Harbor // harbor - HarborCluster // harborcluster - HarborConfigurationCm // harborconfigurationcm - HarborConfiguration // harborconfiguration + Core Controller = iota // core + JobService // jobservice + Portal // portal + Registry // registry + RegistryController // registryctl + ChartMuseum // chartmuseum + Exporter // exporter + NotaryServer // notaryserver + NotarySigner // notarysigner + Trivy // trivy + Harbor // harbor + HarborCluster // harborcluster + HarborConfigurationCm // harborconfigurationcm + HarborConfiguration // harborconfiguration + HarborServerConfiguration // harborserverconfiguration + PullSecretBinding // pullsecretbinding + Namespace // namespace ) func (c Controller) GetFQDN() string { diff --git a/controllers/goharbor/harborserverconfiguration/harborserverconfiguration.go b/controllers/goharbor/harborserverconfiguration/harborserverconfiguration.go new file mode 100644 index 000000000..fb08b8b78 --- /dev/null +++ b/controllers/goharbor/harborserverconfiguration/harborserverconfiguration.go @@ -0,0 +1,138 @@ +package harborserverconfiguration + +import ( + "context" + "fmt" + "time" + + goharborv1 "github.com/goharbor/harbor-operator/apis/goharbor.io/v1beta1" + "github.com/goharbor/harbor-operator/controllers" + commonCtrl "github.com/goharbor/harbor-operator/pkg/controller" + harborClient "github.com/goharbor/harbor-operator/pkg/rest" + v2 "github.com/goharbor/harbor-operator/pkg/rest/v2" + "github.com/ovh/configstore" + "github.com/pkg/errors" + apierr "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + defaultCycle = 5 * time.Minute +) + +// New HarborServerConfiguration reconciler. +func New(ctx context.Context, configStore *configstore.Store) (commonCtrl.Reconciler, error) { + r := &Reconciler{} + r.Controller = commonCtrl.NewController(ctx, controllers.HarborServerConfiguration, nil, configStore) + + return r, nil +} + +// Reconciler reconciles a HarborServerConfiguration object. +type Reconciler struct { + *commonCtrl.Controller + client.Client + Scheme *runtime.Scheme + Harbor *v2.Client +} + +// +kubebuilder:rbac:groups=goharbor.io,resources=harborserverconfigurations,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=goharbor.io,resources=harborserverconfigurations/status,verbs=get;update;patch +// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch +// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch + +// Reconcile the HarborServerConfiguration. +func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl.Result, err error) { + log := r.Log.WithValues("harborserverconfiguration", req.NamespacedName) + log.Info("Starting HarborServerConfiguration Reconciler") + + // Get the configuration first + hsc := &goharborv1.HarborServerConfiguration{} + if err := r.Client.Get(ctx, req.NamespacedName, hsc); err != nil { + if apierr.IsNotFound(err) { + // It could have been deleted after reconcile request coming in. + log.Info("Harbor server configuration does not exist") + + return ctrl.Result{}, nil + } + + return ctrl.Result{}, fmt.Errorf("get HarborServerConfiguraiton error: %w", err) + } + + defer func() { + if err != nil { + hsc.Status.Status = goharborv1.HarborServerConfigurationStatusFail + hsc.Status.Message = err.Error() + } else { + hsc.Status.Status = goharborv1.HarborServerConfigurationStatusReady + hsc.Status.Reason = "" + hsc.Status.Message = "" + } + + log.Info("Reconcile end", "result", res, "error", err, "updateStatusError", r.Client.Status().Update(ctx, hsc)) + }() + + // Create harbor client + harborv2, err := harborClient.CreateHarborV2Client(ctx, r.Client, hsc) + if err != nil { + log.Error(err, "failed to create harbor client") + + return ctrl.Result{}, err + } + + r.Harbor = harborv2.WithContext(ctx) + + // Check if the configuration is being deleted + if !hsc.ObjectMeta.DeletionTimestamp.IsZero() { + log.Info("Harbor server configuration is being deleted") + + return ctrl.Result{}, nil + } + + // Check server health and construct status + err = r.checkServerHealth() + if err != nil { + return ctrl.Result{}, err + } + + log.Info("Finished HarborServerConfiguration Reconciler") + // The health should be rechecked after a reasonable cycle + return ctrl.Result{ + RequeueAfter: defaultCycle, + }, nil +} + +// SetupWithManager for HarborServerConfiguration reconcile controller. +func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { + r.Client = mgr.GetClient() + r.Scheme = mgr.GetScheme() + + return ctrl.NewControllerManagedBy(mgr). + For(&goharborv1.HarborServerConfiguration{}). + Complete(r) +} + +func (r *Reconciler) checkServerHealth() error { + errStr := "" + + healthPayload, err := r.Harbor.CheckHealth() + if err != nil { + r.Log.Error(err, "check harbor server health failed.") + + return err + } + + for _, comp := range healthPayload.Components { + if len(comp.Error) > 0 { + errStr += "Component " + comp.Name + ": " + comp.Error + } + } + + if len(errStr) == 0 { + return nil + } + + return errors.Errorf(errStr) +} diff --git a/controllers/goharbor/harborserverconfiguration/resources.go b/controllers/goharbor/harborserverconfiguration/resources.go new file mode 100644 index 000000000..d0e8ef33a --- /dev/null +++ b/controllers/goharbor/harborserverconfiguration/resources.go @@ -0,0 +1,16 @@ +package harborserverconfiguration + +import ( + "context" + + goharborv1 "github.com/goharbor/harbor-operator/apis/goharbor.io/v1beta1" + "github.com/goharbor/harbor-operator/pkg/resources" +) + +func (r *Reconciler) NewEmpty(_ context.Context) resources.Resource { + return &goharborv1.HarborServerConfiguration{} +} + +func (r *Reconciler) AddResources(ctx context.Context, resource resources.Resource) error { + return nil +} diff --git a/controllers/goharbor/namespace/namespace.go b/controllers/goharbor/namespace/namespace.go new file mode 100644 index 000000000..8f6756728 --- /dev/null +++ b/controllers/goharbor/namespace/namespace.go @@ -0,0 +1,382 @@ +package namespace + +import ( + "context" + "fmt" + "strconv" + + "github.com/go-logr/logr" + v2models "github.com/goharbor/go-client/pkg/sdk/v2.0/models" + goharborv1 "github.com/goharbor/harbor-operator/apis/goharbor.io/v1beta1" + "github.com/goharbor/harbor-operator/controllers" + commonCtrl "github.com/goharbor/harbor-operator/pkg/controller" + harborClient "github.com/goharbor/harbor-operator/pkg/rest" + v2 "github.com/goharbor/harbor-operator/pkg/rest/v2" + "github.com/goharbor/harbor-operator/pkg/utils/consts" + "github.com/goharbor/harbor-operator/pkg/utils/strings" + "github.com/ovh/configstore" + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + apierr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +const ( + defaultSaName = "default" +) + +// New Namespace reconciler. +func New(ctx context.Context, configStore *configstore.Store) (commonCtrl.Reconciler, error) { + r := &Reconciler{} + r.Controller = commonCtrl.NewController(ctx, controllers.Namespace, nil, configStore) + + return r, nil +} + +// Reconciler reconciles a Namespace object. +type Reconciler struct { + *commonCtrl.Controller + client.Client + Scheme *runtime.Scheme + Harbor *v2.Client +} + +// +kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=namespaces/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=goharbor.io,resources=pullsecretbindings,verbs=get;list;watch;create;delete + +// Reconcile the Namespace. +func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { // nolint:funlen + log := r.Log.WithValues("namespace", req.NamespacedName) + + // Get the namespace object + ns := &corev1.Namespace{} + if err := r.Client.Get(ctx, req.NamespacedName, ns); err != nil { + if apierr.IsNotFound(err) { + // The resource may have been deleted after reconcile request coming in + return ctrl.Result{}, nil + } + + return ctrl.Result{}, fmt.Errorf("get namespace error: %w", err) + } + + // Check if the ns is being deleted + if !ns.ObjectMeta.DeletionTimestamp.IsZero() { + log.Info("namespace is being deleted", "name", ns.Name) + + return ctrl.Result{}, nil + } + + // Get the binding list if existing + bindings := &goharborv1.PullSecretBindingList{} + if err := r.Client.List(ctx, bindings, &client.ListOptions{Namespace: req.Name}); err != nil { + return ctrl.Result{}, fmt.Errorf("list bindings error: %w", err) + } + + // If auto is set for image rewrite rule + harborCfg, err := r.findDefaultHarborCfg(ctx, log, ns) + if err != nil { + return ctrl.Result{}, fmt.Errorf("error finding harborCfg: %w", err) + } + + if harborCfg == nil { + log.Info("no default hsc for namespace: ", req.Namespace, ", skip PSB creation") + + if err := r.removeStalePSB(ctx, log, bindings); err != nil { + log.Info("error removing stale psb") + + return ctrl.Result{}, nil + } + + return ctrl.Result{}, nil + } + + // Pull secret issuer is set and then check if the required default binding exists + // Confirm the service account name + // Use default SA if not set inside annotation + saName := defaultSaName + if setSa, ok := ns.Annotations[consts.AnnotationAccount]; ok { + saName = setSa + } + // Check if custom service account exist + sa := &corev1.ServiceAccount{} + saNamespacedName := types.NamespacedName{ + Namespace: ns.Name, + Name: saName, + } + + if err := r.Client.Get(ctx, saNamespacedName, sa); err != nil { + if apierr.IsNotFound(err) { + return ctrl.Result{}, fmt.Errorf("service account %s not found in namespace %s: %w", saName, ns.Name, err) + } + + return ctrl.Result{}, fmt.Errorf("get service account %s in namespace %s error: %w", saName, ns.Name, err) + } + + // Find PSB + for _, bd := range bindings.Items { + if bd.Spec.HarborServerConfig == harborCfg.Name && bd.Spec.ServiceAccount == saName { + // Found it and reconcile is done + // TODO: the PSB might be useless if the credentials or projects are changed + // Need to check if the PSB is still valid. + log.Info("psb exist for this namespace") + + return ctrl.Result{}, nil + } + } + + proj, projExist := ns.Annotations[consts.AnnotationProject] + + if !projExist || proj == "" { + log.Info("annotation 'project' not set, skip reconciliation") + + return ctrl.Result{}, nil + } + + if err := r.setHarborClient(ctx, log, harborCfg); err != nil { + return ctrl.Result{}, err + } + + var projName, projID, robotID string + + if projName, projID, robotID, err = r.validateHarborProjectAndRobot(log, ns); err != nil { + return ctrl.Result{}, err + } + + // PSB doesn't exist, create one + log.Info("creating pull secret binding") + + psb, err := r.createPullSecretBinding(ctx, ns, harborCfg.Name, saName, robotID, projID) + if err != nil { + return ctrl.Result{}, err + } + + log.Info("created pull secret binding", "name", psb.Name) + + // update namespace with updated annotation + if proj == "*" { + log.Info("update namespace annotations", "projectName", projName, "robotID", robotID) + ns.Annotations[consts.AnnotationProject] = projName + ns.Annotations[consts.AnnotationRobot] = robotID + + if err := r.Client.Update(ctx, ns, &client.UpdateOptions{}); err != nil { + return ctrl.Result{}, err + } + } + + return ctrl.Result{}, nil +} + +func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { + r.Client = mgr.GetClient() + r.Scheme = mgr.GetScheme() + + return ctrl.NewControllerManagedBy(mgr). + For(&corev1.Namespace{}). + Complete(r) +} + +func (r *Reconciler) getNewBindingCR(ns string, harborCfg string, sa string) *goharborv1.PullSecretBinding { + return &goharborv1.PullSecretBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: strings.RandomName("Binding"), + Namespace: ns, + }, + Spec: goharborv1.PullSecretBindingSpec{ + HarborServerConfig: harborCfg, + ServiceAccount: sa, + }, + } +} + +func (r *Reconciler) validateProject(projectName string) (string, error) { + var ( + proj *v2models.Project + err error + ) + + if proj, err = r.Harbor.GetProject(projectName); err != nil { + return "", err + } + + return fmt.Sprintf("%d", proj.ProjectID), nil +} + +func (r *Reconciler) validateRobot(proj, robot string) error { + if robot == "" { + return errors.Errorf("robot should not be empty") + } + + if proj == "" { + return errors.Errorf("proj should not be empty") + } + + robotID, err := strconv.ParseInt(robot, 10, 64) + if err != nil { + return err + } + + projectID, err := strconv.ParseInt(proj, 10, 64) + if err != nil { + return err + } + + _, err = r.Harbor.GetRobotAccount(projectID, robotID) + + return err +} + +func (r *Reconciler) createProjectAndRobot(proj string) (string, string, error) { + projID, err := r.Harbor.EnsureProject(proj) + if err != nil { + return "", "", err + } + + robot, err := r.Harbor.CreateRobotAccount(fmt.Sprintf("%d", projID)) + if err != nil { + return "", "", err + } + + return fmt.Sprintf("%d", projID), fmt.Sprintf("%d", robot.ID), nil +} + +func (r *Reconciler) findDefaultHarborCfg(ctx context.Context, log logr.Logger, ns *corev1.Namespace) (*goharborv1.HarborServerConfiguration, error) { + // check annotation first + harborCfg, yes := ns.Annotations[consts.AnnotationHarborServer] + if yes && harborCfg != "" { + hsc := &goharborv1.HarborServerConfiguration{} + + err := r.Client.Get(ctx, types.NamespacedName{Name: harborCfg}, hsc) + if err != nil { + if apierr.IsNotFound(err) { + log.Info("hsc specified in annotation doesn't exist") + + return nil, nil + } + + return nil, fmt.Errorf("error when finding hsc specified in annotation: %w", err) + } + + return hsc, nil + } + + log.Info("no default hsc found in annotation for namespace " + ns.Name) + + // then find global default hsc + hscs := &goharborv1.HarborServerConfigurationList{} + + err := r.Client.List(ctx, hscs) + if err != nil { + return nil, fmt.Errorf("error listing harborCfg: %w", err) + } + + if len(hscs.Items) > 0 { + for _, hsc := range hscs.Items { + if hsc.Spec.Default { + log.Info("found global default hsc: " + hsc.Name) + + return &hsc, nil + } + } + } + + return nil, nil +} + +func (r *Reconciler) removeStalePSB(ctx context.Context, log logr.Logger, bindings *goharborv1.PullSecretBindingList) error { + if len(bindings.Items) > 0 { + log.Info("removig stale psb in namespace") + + for i, bd := range bindings.Items { + // Remove all the existing bindings as issuer is removed + if err := r.Client.Delete(ctx, &bindings.Items[i], &client.DeleteOptions{}); err != nil { + // Retry next time + return fmt.Errorf("remove binding %s error: %w", bd.Name, err) + } + } + } + + return nil +} + +func (r *Reconciler) createPullSecretBinding(ctx context.Context, ns *corev1.Namespace, harborCfg, saName, robotID, projID string) (*goharborv1.PullSecretBinding, error) { + defaultBinding := r.getNewBindingCR(ns.Name, harborCfg, saName) + if err := controllerutil.SetControllerReference(ns, defaultBinding, r.Scheme); err != nil { + return nil, fmt.Errorf("set ctrl reference error: %w", err) + } + + defaultBinding.Spec.RobotID = robotID + defaultBinding.Spec.ProjectID = projID + + if err := r.Client.Create(ctx, defaultBinding, &client.CreateOptions{}); err != nil { + return nil, fmt.Errorf("create binding CR error: %w", err) + } + + return defaultBinding, nil +} + +func (r *Reconciler) setHarborClient(ctx context.Context, log logr.Logger, harborCfg *goharborv1.HarborServerConfiguration) error { + // Create harbor client + harborV2, err := harborClient.CreateHarborV2Client(ctx, r.Client, harborCfg) + if err != nil { + log.Error(err, "failed to create harbor client") + + return err + } + + r.Harbor = harborV2.WithContext(ctx) + + return nil +} + +func (r *Reconciler) validateHarborProjectAndRobot(log logr.Logger, ns *corev1.Namespace) (string, string, string, error) { + var ( + err error + projID string + ) + + // Validate the annotation and create PSB is needed + proj := ns.Annotations[consts.AnnotationProject] + robotID, robotExist := ns.Annotations[consts.AnnotationRobot] + + if proj == "*" { + log.Info("validate project and robot account") + // Automatically generate project and robot account based on namespace name + // TODO: should be more structure name since many clusters might share the same Harbor instance + proj = strings.RandomName(ns.Name) + projID, robotID, err = r.createProjectAndRobot(proj) + + if err != nil { + log.Error(err, "Failed creating project and robot", "project", proj, "robot", robotID) + + return "", "", "", err + } + + return proj, projID, robotID, nil + } + + projID, err = r.validateProject(proj) + if err != nil { + log.Error(err, "Harbor annotation for project is invalid", "project", proj) + + return "", "", "", fmt.Errorf("project are invalid: %w", err) + } + + if !robotExist { + return "", "", "", errors.New("robotID is not set") + } + + err = r.validateRobot(projID, robotID) + if err != nil { + log.Error(err, "annotation 'robotID' is invalid", "robotID", robotID) + + return "", "", "", fmt.Errorf("robotID is invalid: %w", err) + } + + return proj, projID, robotID, nil +} diff --git a/controllers/goharbor/namespace/resources.go b/controllers/goharbor/namespace/resources.go new file mode 100644 index 000000000..b9130b7ca --- /dev/null +++ b/controllers/goharbor/namespace/resources.go @@ -0,0 +1,16 @@ +package namespace + +import ( + "context" + + "github.com/goharbor/harbor-operator/pkg/resources" + corev1 "k8s.io/api/core/v1" +) + +func (r *Reconciler) NewEmpty(_ context.Context) resources.Resource { + return &corev1.Namespace{} +} + +func (r *Reconciler) AddResources(ctx context.Context, resource resources.Resource) error { + return nil +} diff --git a/controllers/goharbor/pullsecretbinding/pullsecretbinding.go b/controllers/goharbor/pullsecretbinding/pullsecretbinding.go new file mode 100644 index 000000000..a03757636 --- /dev/null +++ b/controllers/goharbor/pullsecretbinding/pullsecretbinding.go @@ -0,0 +1,366 @@ +package pullsecretbinding + +import ( + "context" + "fmt" + "strconv" + "time" + + "github.com/goharbor/go-client/pkg/sdk/v2.0/models" + goharborv1 "github.com/goharbor/harbor-operator/apis/goharbor.io/v1beta1" + "github.com/goharbor/harbor-operator/controllers" + commonCtrl "github.com/goharbor/harbor-operator/pkg/controller" + "github.com/goharbor/harbor-operator/pkg/registry/secret" + "github.com/goharbor/harbor-operator/pkg/rest/model" + v2 "github.com/goharbor/harbor-operator/pkg/rest/v2" + "github.com/goharbor/harbor-operator/pkg/utils/consts" + "github.com/goharbor/harbor-operator/pkg/utils/strings" + "github.com/ovh/configstore" + corev1 "k8s.io/api/core/v1" + apierr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +const ( + defaultOwner = "harbor-operator-ns" + regSecType = "kubernetes.io/dockerconfigjson" + datakey = ".dockerconfigjson" + finalizerID = "psb.finalizers.resource.goharbor.io" + defaultCycle = 5 * time.Minute +) + +// New PullSecretBinding reconciler. +func New(ctx context.Context, configStore *configstore.Store) (commonCtrl.Reconciler, error) { + r := &Reconciler{} + r.Controller = commonCtrl.NewController(ctx, controllers.PullSecretBinding, nil, configStore) + + return r, nil +} + +// Reconciler reconciles a PullSecretBinding object +type Reconciler struct { + *commonCtrl.Controller + client.Client + Scheme *runtime.Scheme + Harbor *v2.Client +} + +// +kubebuilder:rbac:groups=goharbor.io,resources=pullsecretbindings,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=goharbor.io,resources=pullsecretbindings/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=goharbor.io,resources=harborserverconfigurations,verbs=get +// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;create;update +// +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch;update;patch + +func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl.Result, ferr error) { + log := r.Log.WithValues("pullsecretbinding", req.NamespacedName) + + // Get the psb object + bd := &goharborv1.PullSecretBinding{} + if err := r.Client.Get(ctx, req.NamespacedName, bd); err != nil { + if apierr.IsNotFound(err) { + // The resource may have be deleted after reconcile request coming in + return ctrl.Result{}, nil + } + + return ctrl.Result{}, fmt.Errorf("get binding CR error: %w", err) + } + + // Check binding resources + server, sa, res, err := r.checkBindingRes(ctx, bd) + if err != nil { + return res, err + } else { + if server == nil || sa == nil { + return res, err + } + } + + // Create harbor client + harborv2, err := v2.NewWithServer(server) + if err != nil { + log.Error(err, "failed to create harbor client") + + return ctrl.Result{}, err + } + + r.Harbor = harborv2.WithContext(ctx) + + // Check if the binding is being deleted + if bd.ObjectMeta.DeletionTimestamp.IsZero() { + if !strings.ContainsString(bd.ObjectMeta.Finalizers, finalizerID) { + // Append finalizer + bd.ObjectMeta.Finalizers = append(bd.ObjectMeta.Finalizers, finalizerID) + if err := r.update(ctx, bd); err != nil { + return ctrl.Result{}, err + } + } + } else { + if strings.ContainsString(bd.ObjectMeta.Finalizers, finalizerID) { + // Execute and remove our finalizer from the finalizer list + if err := r.deleteExternalResources(bd); err != nil { + return ctrl.Result{}, err + } + + bd.ObjectMeta.Finalizers = strings.RemoveString(bd.ObjectMeta.Finalizers, finalizerID) + if err := r.Client.Update(ctx, bd, &client.UpdateOptions{}); err != nil { + return ctrl.Result{}, err + } + } + + log.Info("pull secret binding is being deleted") + return ctrl.Result{}, nil + } + + defer func() { + if ferr != nil && bd.Status.Status != "error" { + bd.Status.Status = "error" + bd.Status.Message = ferr.Error() + if err := r.Status().Update(ctx, bd, &client.UpdateOptions{}); err != nil { + log.Error(err, "defer update status error", "cause", err) + } + } + }() + + projID, robotID := parseIntID(bd.Spec.ProjectID), parseIntID(bd.Spec.RobotID) + + // Bind robot to service account + // TODO: may cause dirty robots at the harbor project side + // TODO: check secret binding by get secret and service account + _, ok := bd.Annotations[consts.AnnotationRobotSecretRef] + if !ok { + // Need to create a new one as we only have one time to get the robot token + robot, err := r.Harbor.GetRobotAccount(projID, robotID) + if err != nil { + return ctrl.Result{}, fmt.Errorf("create robot account error: %w", err) + } + + // Make registry secret + regsec, err := r.createRegSec(ctx, bd.Namespace, server.ServerURL, robot, bd) + if err != nil { + return ctrl.Result{}, fmt.Errorf("create registry secret error: %w", err) + } + // Add secret to service account + if sa.ImagePullSecrets == nil { + sa.ImagePullSecrets = make([]corev1.LocalObjectReference, 0) + } + sa.ImagePullSecrets = append(sa.ImagePullSecrets, corev1.LocalObjectReference{ + Name: regsec.Name, + }) + + // Update + if err := r.Client.Update(ctx, sa, &client.UpdateOptions{}); err != nil { + return ctrl.Result{}, fmt.Errorf("update error: %w", err) + } + + // Update binding + if err := controllerutil.SetControllerReference(bd, regsec, r.Scheme); err != nil { + r.Log.Error(err, "set controller reference", "owner", bd.ObjectMeta, "controlled", regsec.ObjectMeta) + } + setAnnotation(bd, consts.AnnotationRobotSecretRef, regsec.Name) + if err := r.update(ctx, bd); err != nil { + return ctrl.Result{}, fmt.Errorf("update error: %w", err) + } + } + + // TODO: add conditions + if bd.Status.Status != "ready" { + bd.Status.Status = "ready" + if err := r.Status().Update(ctx, bd, &client.UpdateOptions{}); err != nil { + if apierr.IsConflict(err) { + log.Error(err, "failed to update status") + } else { + return ctrl.Result{}, err + } + } + } + + // Loop + return ctrl.Result{ + RequeueAfter: defaultCycle, + }, nil +} + +func (r *Reconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { + r.Client = mgr.GetClient() + r.Scheme = mgr.GetScheme() + + return ctrl.NewControllerManagedBy(mgr). + For(&goharborv1.PullSecretBinding{}). + Complete(r) +} + +func (r *Reconciler) update(ctx context.Context, binding *goharborv1.PullSecretBinding) error { + if err := r.Client.Update(ctx, binding, &client.UpdateOptions{}); err != nil { + return err + } + + // Refresh object status to avoid problem + namespacedName := types.NamespacedName{ + Name: binding.Name, + Namespace: binding.Namespace, + } + return r.Client.Get(ctx, namespacedName, binding) +} + +func (r *Reconciler) getConfigData(ctx context.Context, hsc *goharborv1.HarborServerConfiguration) (*model.HarborServer, error) { + s := &model.HarborServer{ + ServerURL: hsc.Spec.ServerURL, + Insecure: hsc.Spec.Insecure, + } + + namespacedName := types.NamespacedName{ + Namespace: hsc.Spec.AccessCredential.Namespace, + Name: hsc.Spec.AccessCredential.AccessSecretRef, + } + sec := &corev1.Secret{} + if err := r.Client.Get(ctx, namespacedName, sec); err != nil { + return nil, fmt.Errorf("failed to get the configured secret with error: %w", err) + } + + username, password, err := model.GetCredential(sec) + if err != nil { + return nil, fmt.Errorf("get credential error: %w", err) + } + + s.Username = username + s.Password = password + + return s, nil +} + +func (r *Reconciler) checkBindingRes(ctx context.Context, psb *goharborv1.PullSecretBinding) (*model.HarborServer, *corev1.ServiceAccount, ctrl.Result, error) { + // Get server configuration + hsc, err := r.getHarborServerConfig(ctx, psb.Spec.HarborServerConfig) + if err != nil { + // Retry later + return nil, nil, ctrl.Result{}, fmt.Errorf("get server configuration error: %w", err) + } + + if hsc == nil { + // Not exist + r.Log.Info("harbor server configuration does not exists", "name", psb.Spec.HarborServerConfig) + // Do not need to reconcile again + return nil, nil, ctrl.Result{}, nil + } + + if hsc.Status.Status == goharborv1.HarborServerConfigurationStatusUnknown || hsc.Status.Status == goharborv1.HarborServerConfigurationStatusFail { + return nil, nil, ctrl.Result{}, fmt.Errorf("status of Harbor server referred in configuration %s is unexpected: %s", hsc.Name, hsc.Status.Status) + } + + // Get the specified service account + sa, err := r.getServiceAccount(ctx, psb.Namespace, psb.Spec.ServiceAccount) + if err != nil { + // Retry later + return nil, nil, ctrl.Result{}, fmt.Errorf("get service account error: %w", err) + } + + if sa == nil { + // Not exist + r.Log.Info("service account does not exist", "name", psb.Spec.ServiceAccount) + // Do not need to reconcile again + return nil, nil, ctrl.Result{}, nil + } + + hs, err := r.getConfigData(ctx, hsc) + if err != nil { + return nil, nil, ctrl.Result{}, fmt.Errorf("get config data error: %w", err) + } + + return hs, sa, ctrl.Result{}, nil +} + +func (r *Reconciler) getHarborServerConfig(ctx context.Context, name string) (*goharborv1.HarborServerConfiguration, error) { + hsc := &goharborv1.HarborServerConfiguration{} + // HarborServerConfiguration is cluster scoped resource + namespacedName := types.NamespacedName{ + Name: name, + } + if err := r.Client.Get(ctx, namespacedName, hsc); err != nil { + // Explicitly check not found error + if apierr.IsNotFound(err) { + return nil, nil + } + + return nil, err + } + + return hsc, nil +} + +func (r *Reconciler) getServiceAccount(ctx context.Context, ns, name string) (*corev1.ServiceAccount, error) { + sc := &corev1.ServiceAccount{} + namespacedName := types.NamespacedName{ + Namespace: ns, + Name: name, + } + + if err := r.Client.Get(ctx, namespacedName, sc); err != nil { + if apierr.IsNotFound(err) { + return nil, nil + } + + return nil, err + } + + return sc, nil +} + +func (r *Reconciler) createRegSec(ctx context.Context, namespace string, registry string, robot *models.Robot, psb *goharborv1.PullSecretBinding) (*corev1.Secret, error) { + auths := &secret.Object{ + Auths: map[string]*secret.Auth{}, + } + auths.Auths[registry] = &secret.Auth{ + Username: robot.Name, + Password: robot.Secret, + Email: fmt.Sprintf("%s@goharbor.io", robot.Name), + } + + encoded := auths.Encode() + + regSec := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: strings.RandomName("regsecret"), + Namespace: namespace, + Annotations: map[string]string{ + consts.AnnotationSecOwner: defaultOwner, + }, + OwnerReferences: []metav1.OwnerReference{{APIVersion: psb.APIVersion, Kind: psb.Kind, Name: psb.Name, UID: psb.UID}}, + }, + Type: regSecType, + Data: map[string][]byte{ + datakey: encoded, + }, + } + + return regSec, r.Client.Create(ctx, regSec, &client.CreateOptions{}) +} + +func (r *Reconciler) deleteExternalResources(bd *goharborv1.PullSecretBinding) error { + if pro, ok := bd.Annotations[consts.AnnotationProject]; ok { + if err := r.Harbor.DeleteProject(pro); err != nil { + // TODO: handle delete error + // Delete non-empty project will cause error? + r.Log.Error(err, "delete external resources", "finalizer", finalizerID) + } + } + + return nil +} + +func setAnnotation(obj *goharborv1.PullSecretBinding, key string, value string) { + if obj.Annotations == nil { + obj.Annotations = make(map[string]string) + } + + obj.Annotations[key] = value +} + +func parseIntID(id string) int64 { + intID, _ := strconv.ParseInt(id, 10, 64) + return intID +} diff --git a/controllers/goharbor/pullsecretbinding/resources.go b/controllers/goharbor/pullsecretbinding/resources.go new file mode 100644 index 000000000..e7d3cde0f --- /dev/null +++ b/controllers/goharbor/pullsecretbinding/resources.go @@ -0,0 +1,17 @@ +package pullsecretbinding + +import ( + "context" + + goharborv1 "github.com/goharbor/harbor-operator/apis/goharbor.io/v1beta1" + "github.com/goharbor/harbor-operator/pkg/resources" +) + +func (r *Reconciler) NewEmpty(_ context.Context) resources.Resource { + return &goharborv1.PullSecretBinding{} +} + +func (r *Reconciler) AddResources(ctx context.Context, resource resources.Resource) error { + + return nil +} diff --git a/docs/arch/day2-op.md b/docs/arch/day2-op.md index 87a1473ac..5984409b0 100644 --- a/docs/arch/day2-op.md +++ b/docs/arch/day2-op.md @@ -10,7 +10,7 @@ The day2 operations includes: * Transparent proxy cache * Apply configuration changes -For more info, check [Here](https://github.com/szlabs/harbor-automation-4k8s#harbor-automation-4k8s). +For more info, check [Here](https://github.com/goharbor/harbor-operator/docs/configurations/day2-config.md). ## Overall design @@ -64,4 +64,4 @@ Set the Harbor system settings via a configuration CRD. At this time, it is not ## References -* Learn more, check Harbor day2 operator [prototype project](https://github.com/szlabs/harbor-automation-4k8s). +* Learn more, check Harbor day2 operator [prototype project](https://github.com/goharbor/harbor-operator). diff --git a/docs/configurations/day2-config.md b/docs/configurations/day2-config.md index ea099ff70..82e3367a7 100644 --- a/docs/configurations/day2-config.md +++ b/docs/configurations/day2-config.md @@ -1,88 +1,323 @@ -# Day2 configurations +# Day2 Operation -Initially, we configured harbor by means of configmap, but currently we recommend using `harborconfiguration` crd to configure harbor, for the configmap method will be deprecated in version 1.3, those who have used and still use configmap will be automatically converted to `harborconfiguration` cr by the controller, and automatically remove old configmap. +Day2 Operation provides the following features to help users get better experiences of using [Harbor](https://github.com/goharbor/harbor) +or apply some Day2 operations to the Harbor deployed in the Kubernetes cluster: -> The harbor configuration items can be found in [harbor swagger](https://github.com/goharbor/harbor/blob/0867a6bfd6f33149f86a7ae8a740f5e1f976cafa/api/v2.0/swagger.yaml#L7990). +- [x] **Mapping k8s namespace and harbor project**: make sure there is a relevant project existing at linked Harbor side for the + specified k8s namespace pulling image from there (bind specified one or create new). +- [x] **Pulling secret auto injection**: auto create robot account in the corresponding Harbor project and bind it to the + related service account of the related k8s namespace to avoid explicitly specifying image pulling secret in the + deployment manifest yaml. +- [x] **image path auto rewriting**: rewrite the pulling path of the matched workload images (e.g: no full repository path specified) + being deployed in the specified k8s namespace to the corresponding project at the linked Harbor. +- [x] **transparent proxy cache**: rewrite the pulling path of the matched workload images to the proxy cache project of the linked Harbor. +- [ ] apply configuration changes: update the system configurations of the linked Harbor with Kubernetes way by providing a configMap. +- [ ] certificate population: populate the CA of the linked Harbor instance to the container runtimes of all the cluster workers and let workers trust it to avoid image pulling issues. +- [ ] TBD -## ConfigMap (deprecated) +## Overall Design -First you need to prepare a config map to provide your harbor configurations, apply the config map in the same namespace as harborcluster. In particular, you need to add an annotation to your config map to mark which harborcluster it is acting on. +The diagram below shows the overall design of this project: -In addition, in order to protect the password from being displayed directly in the config map, you need to define the password inside the secret, and then specify the name of the secret in the configuration. We currently offer these type of secret configurations fields: `"email_password", "ldap_search_password", "uaa_client_secret", "oidc_client_secret"`. +![overall design](./docs/assets/4k8s-automation.png) -### Example of ConfigMap +### Feature + +#### Image rewrite + +Image rewrite will rewrite the original image paths to the specified Harbor projects by following the pre-defined rewriting rules via Kubernetes admission control webhook. + +A cluster scoped Kubernetes CR named `HarborServerConfiguration` is designed to keep the Harbor server access info by providing the access +host and access key & secret (key and secret should be wrapped into a kubernetes secret) for future referring. +It has a `default` field to define whether this HSC will be applied to all namespaces. There could be only one default HSC. + +Rewriting rule is a k-v pair to specify images from which repo should be redirected to which harbor project: +`"docker.io": "harborproject1"` or `"*": "harborproject2"` + +Here we should pay attention is the key "*" means images from any repo are redirected to harbor project "harborproject2". + +Rewriting rules will be defined as a rule list like: + +```shell +rules: + - docker.io:harborproject1 + - *:harborproject2 + - quay.io:harborproject3 +``` + +**Definition location:** + +To enforce the rewrite rule to a specific namespace, you need to add a label to the namespace `harbor-day2-webhook-configuration: enabled`. + +The rewriting rules can be defined into two places, one is in the HSC spec and another is in a configMap. + +Rules in HSC spec are for the whole cluster scope. The rules will be applied to the namespaces selected by the namespace selector of HSC. + +The rules defined in the configMap are only visible to the namespace where the configMap is living. Use annotation of namespace rename to `goharbor.io/rewriting-rules` to link the rule configMap. + +**The priority:** + +Rules in configMap > rules in HSC referenced by ConfigMap > default HSC > "*" rule + +For example. images from `docker.io` will be rewritten to 'harborproject1' and images from `quay.io` will be rewritten to 'harborproject3'. The images from `gcr.io` or `ghcr.io` will both be rewritten to 'harborproject2' by following the "*" rule. + +**Assumptions:** + +- Only 1 HSC as default. (ctrl has to make sure this constraint) +- Default HSC is appliable for all namespaces as the default behavior (except its namespace selector is configured). +- HSC can have a namespace selector to specify its influencing scope. +- Namespace selector is optional. The empty selector means adapting all. + +Namespace admin can create a configMap to customize image rewriting for the specified namespace: + +**Content of configMap:** + +```yaml +hsc: myHscName ## if this ns missing the selector of the specfying HSC, log warnning and no action. +rewriting: on ## or off +rules: -| + - docker.io:harborproject1-1 + - *:harborproject2-1 + - quay.io:harborproject3-1 +``` + +Add annotation `goharbor.io/rewriting-rules=configMapName` to the namespace to enable the rewriting. + +Merging rules: rules defined in configMap has higher priority if conflicts happened. + +#### Project mapping and secret injection + +When doing project mapping and secret injection, an annotation `goharbor.io/project` MUST be added to the specified namespace ( if `goharbor.io/project` is +not set, that means the mapping/injection function is not enabled). + +A CR `PullSecretBinding` is created to keep the relationship between Kubernetes resources and Harbor resources. + +- the mapping project is recorded in annotation `annotation:goharbor.io/project` of the CR `PullSecretBinding`. +- the linked robot account is recorded in annotation `annotation:goharbor.io/robot` of the CR `PullSecretBinding`. +- make sure the linked robot account is wrapped as a Kubernetes secret and bind with the service account that is specified in the annotation `annotation:goharbor.io/service-account` of the namespace. + +- If `goharbor.io/project`=*, then check whether annotation `goharbor.io/secret-issuer` (it should be renamed to `goharbor.io/harbor`) which is pointing to an HSC (It should be provided by the cluster-admin) is set or not. If it is not set, then back off to the default HSC. If there is no default HSC, then an error should be raised. When HSC is ready, create a Harbor project with the same name of the namespace in that HSC and also create a robot account in the newly created Harbor project. After the robot account is created, the identity of the created robot account should be recorded into the annotation `goharbro.io/robot` (it should be renamed to `goharbor.io/robot-account`). +- If `goharbor.io/project=`, then the annotation `goharbor.io/robot=` MUST also be set to a valid robot account that is living in the project representing by the annotation `goharbor.io/project`. The controller has to make sure the robot specified in the annotation can be used to access the project (by accessing API with that robot account). + - If the specified project name does not exist or the robot account provided does not mismatch, then log the error (should not return an error in the reconcile process) + +Then a PSB can be created to track the relationship and bind pull secret to the service account: + +- wrap the robot account as a secret +- bind the secret to the service account which is specified by the annotation `goharbor.io/service-account` (this is optional, if it is not set, then use the default service account under the namespace) + +## Installation + +For trying this project, you can follow the guideline shown below to quickly install the operator and webhook to your cluster: +(tools like `git`,`make`,`kustomize` and `kubectl` should be installed and available in the $PATH) + +1. Clone the repository + +1. Build the image (no official image built so far) + +```shell script +make docker-build && make docker-push +``` + +1. Deploy the operator to the Kubernetes cluster + +```shell script +make deploy +``` + +1. Check the status of the operator + +```shell script +kubectl get all -n harbor-operator-ns +``` + +1. Uninstall the operator + +```shell script +kustomize build config/default | kubectl delete -f - +``` + +## Usages + +### HarborServerConfiguration CR + +Register your Harbor in a `HarborServerConfiguration` CR: ```yaml apiVersion: v1 kind: Secret metadata: - name: secret-sample - namespace: cluster-sample-ns + name: mysecret + namespace: kube-system type: Opaque data: - # the key is same with fields name. - email_password: YmFyCg== + accessKey: YWRtaW4= + accessSecret: SGFyYm9yMTIzNDU= +--- +apiVersion: goharbor.io/v1alpha1 +kind: HarborServerConfiguration +metadata: + name: harborserverconfiguration-sample +spec: + default: true ## whether it will be default global hsc + serverURL: 10.168.167.189 + accessCredential: + namespace: kube-system + accessSecretRef: mysecret + version: 2.1.0 + insecure: true + rules: ## rules to define to rewrite image path + - "docker.io,myharbor" ## , + namespaceSelector: + matchLabels: + usethisHSC: true ``` +Create it: + +```shell script +kubectl apply -f hsc.yaml +``` + +Use the following command to check the `HarborServerConfiguration` CR (short name: `hsc`): + +```shell script +kubectl get hsc +``` + +### Pulling secret injection + +Add related annotations to your namespace when enabling secret injection: + ```yaml apiVersion: v1 -kind: ConfigMap +kind: Namespace metadata: - name: test-config - # namespace same with harborcluster cr namespace. - namespace: cluster-sample-ns + name: sz-namespace1 annotations: - # required. - # if not define the anno, the config map will not work. - # the key is `goharbor.io/configuration`, and the value is your harborcluster cr name. - goharbor.io/configuration: harborcluster-sample -data: - # provide your harbor configuration by yaml format. - config.yaml: | - email_ssl: true - email_password: secret-sample # the value is the name of secret which store the email_password. + goharbor.io/harbor: harborserverconfiguration-sample + goharbor.io/service-account: default + goharbor.io/project: "*" + label: + harbor-day2-webhook-configuration: enabled ``` -## CRD-based HarborConfiguration +Create it: -We have defined a new kubernetes crd `HarborConfiguration` as harbor configuration, which can provide more validate capabilities and scalability. The configuration is similar to configmap, you just need prepare a `harborconfiguration` resource and input the sepc you needed to configurate harbor. +```shell script +kubectl apply -f namespace.yaml +``` -### Example of HarborConfiguration +After the automation is completed, a CR `PullSecretBinding` is created: + +```shell script +kubectl get psb -n sz-namespace1 + +# output +#NAME HARBOR SERVER SERVICE ACCOUNT STATUS +#binding-txushc harborserverconfiguration-sample default ready +``` + +Get the details of the psb/binding-xxx: + +```shell script +k8s get psb/binding-txushc -n sz-namespace1 -o yaml +``` + +Output details: + +```yaml +apiVersion: goharbor.io/v1alpha1 +kind: PullSecretBinding +metadata: + annotations: + goharbor.io/project: sz-namespace1-axtnd8 + goharbor.io/robot: "31" + goharbor.io/robot-secret: regsecret-sab3pq + creationTimestamp: "2020-12-02T15:21:48Z" + finalizers: + - psb.finalizers.resource.goharbor.io + generation: 1 + name: binding-txushc + namespace: sz-namespace1 + ownerReferences: + - apiVersion: v1 + blockOwnerDeletion: true + controller: true + kind: Namespace + name: sz-namespace1 + uid: 810efadd-b560-4791-8007-8decaf2fbb1c + resourceVersion: "2500851" + selfLink: /apis/goharbor.io/v1alpha1/namespaces/sz-namespace1/pullsecretbindings/binding-txushc + uid: f5b4f68a-4657-4f89-b231-0fc96c03ca00 +spec: + harborServerConfig: harborserverconfiguration-sample + serviceAccount: default +status: + conditions: [] + status: ready +``` + +The related auto-generated data is recorded in the related annotations: + +```yaml +annotations: + goharbor.io/project: sz-namespace1-axtnd8 + goharbor.io/robot: "31" + goharbor.io/robot-secret: regsecret-sab3pq +``` + +### Image path rewrite + +To enable image rewrite, set the rules section in hsc, or set annotation to refer to a configMap that contains rules and hsc ```yaml apiVersion: v1 -kind: Secret +kind: Namespace metadata: - name: secret-sample - namespace: cluster-sample-ns -type: Opaque -data: - # the key is same with fields name. - email_password: YmFyCg== + name: sz-namespace1 + annotations: + goharbor.io/harbor: harborserverconfiguration-sample + goharbor.io/service-account: default + goharbor.io/rewriting-rules: cm ``` +Corresponding ConfigMap + ```yaml -apiVersion: goharbor.io/v1beta1 -kind: HarborConfiguration +apiVersion: v1 +kind: ConfigMap metadata: - name: test-config - namespace: cluster-sample-ns -spec: - # your harbor configuration - configuration: - emailPassword: secret-sample - emailSsl: true - harborClusterRef: harborcluster-sample + name: cm + namespace: sz-namespace1 +data: + hsc: harbor2 + rewriting: "on" + rules: | # configMap doesn't support storing nested string + docker.io,highestproject + gcr.io,a + ``` -After apply your `harborconfiguration` cr to kubernetes cluster, the controller of `harborconfiguration` will apply your configuration to harbor instance, you can see the result of configuraion from cr status. +Corresponding HSC ```yaml -status: - lastApplyTime: "2021-06-04T06:07:53Z" - lastConfiguration: - configuration: - emailPassword: secret-sample - emailSsl: true - status: Success +apiVersion: goharbor.io/v1alpha1 +kind: HarborServerConfiguration +metadata: + name: harborserverconfiguration-sample +spec: + serverURL: 10.168.167.12 + accessCredential: + namespace: kube-system + accessSecretRef: mysecret + version: 2.1.0 + insecure: true + rules: ## rules to define to rewrite image path + - "docker.io,testharbor" ## , + ``` + +As mentioned before, the mutating webhook will rewrite all the images of the deploying pods which has no registry host +prefix to the flowing pattern: + +`image:tag => //image:tag` diff --git a/go.mod b/go.mod index 2f393c7a5..b6a4eb597 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,10 @@ require ( cloud.google.com/go v0.58.0 // indirect github.com/Masterminds/semver v1.5.0 github.com/Masterminds/sprig v2.22.0+incompatible + github.com/containers/image/v5 v5.16.1 github.com/go-kit/kit v0.10.0 github.com/go-logr/logr v0.4.0 + github.com/go-openapi/runtime v0.21.0 github.com/go-redis/redis v6.15.9+incompatible github.com/goharbor/go-client v0.24.3 github.com/goharbor/harbor/src v0.0.0-20211025104526-d4affc2eba6d @@ -26,13 +28,15 @@ require ( github.com/sethvargo/go-password v0.1.3 github.com/sirupsen/logrus v1.8.1 github.com/spotahome/redis-operator v1.0.0 + github.com/stretchr/testify v1.7.0 github.com/szlabs/redis-operator v1.0.1 // indirect github.com/theupdateframework/notary v0.6.1 github.com/uber/jaeger-client-go v2.24.0+incompatible github.com/uber/jaeger-lib v2.2.0+incompatible + github.com/umisama/go-regexpcache v0.0.0-20150417035358-2444a542492f github.com/zalando/postgres-operator v1.6.1 go.uber.org/zap v1.19.0 - golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 + golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.22.3 diff --git a/go.sum b/go.sum index c3542fb30..5389acb44 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,7 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774/go.mod h1:6/0dYRLLXyJjbkIPeeGyoJ/eKOSI0eU6eTlCBYibgd0= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v37.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v46.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= @@ -73,6 +74,7 @@ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbt github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ClickHouse/clickhouse-go v1.3.12/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= @@ -93,9 +95,24 @@ github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA4 github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= +github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= +github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= +github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= +github.com/Microsoft/hcsshim v0.8.18/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim v0.8.22/go.mod h1:91uVCVzvX2QD16sMCenoxxXo6L1wJnLMX2PSufFMtF0= +github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= +github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -113,7 +130,9 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/Unknwon/goconfig v0.0.0-20160216183935-5f601ca6ef4d/go.mod h1:wngxua9XCNjvHjDiTiV26DaKDT+0c63QR6H5hjVUUxw= github.com/Venafi/vcert/v4 v4.11.0/go.mod h1:OE+UZ0cj8qqVUuk0u7R4GIk4ZB6JMSf/WySqnBPNwws= +github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= @@ -123,6 +142,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy 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/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190726115642-cd293c93fd97/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= @@ -167,14 +187,17 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/bugsnag-go v1.5.2/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= @@ -194,12 +217,17 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= +github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= 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/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= +github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cfssl v0.0.0-20190510060611-9c027c93ba9e/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= @@ -216,33 +244,116 @@ github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoC github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= +github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= +github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= +github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= +github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.11/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= +github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= +github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= +github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= +github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= +github.com/containerd/containerd v1.5.4/go.mod h1:sx18RgvW6ABJ4iYUw7Q5x7bgFOAB9B6G7+yO0XBc4zw= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= +github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= +github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= +github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= +github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= +github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= +github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= +github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= +github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= +github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= +github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/stargz-snapshotter/estargz v0.9.0/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= +github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= +github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= +github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= +github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= +github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= +github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= +github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= +github.com/containers/image/v5 v5.16.1 h1:4786k48/af3dOkVf9EM+xB880ArkXalICsGC4AXC6to= +github.com/containers/image/v5 v5.16.1/go.mod h1:mCvIFdzyyP1B0NBcZ80OIuaYqFn/OpFpaOMOMn1kU2M= +github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= +github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= +github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= +github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= +github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= +github.com/containers/storage v1.37.0/go.mod h1:kqeJeS0b7DO2ZT1nVWs0XufrmPFbgV3c+Q/45RlH6r4= github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-oidc/v3 v3.0.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.3.1/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= @@ -261,6 +372,11 @@ github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= +github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= +github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= +github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= +github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -280,19 +396,26 @@ github.com/dhui/dktest v0.3.2/go.mod h1:l1/ib23a/CmxAe7yixtrYPc8Iy90Zy2udyaHINM5 github.com/digitalocean/godo v1.44.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/cli v20.10.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v0.0.0-20191216044856-a8371794149d/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v1.4.2-0.20200213202729-31a86c4ab209/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.8+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= github.com/docker/go v0.0.0-20160303222718-d30aec9fd63c h1:Ggg7IiOtghyZzn3ozi31kPHpV6qSjMgmesXaWCijYNM= github.com/docker/go v0.0.0-20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= @@ -343,6 +466,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= +github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= @@ -529,16 +653,22 @@ github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY9 github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= github.com/gocraft/work v0.5.1/go.mod h1:pc3n9Pb5FAESPPGfM0nL+7Q1xtgtRnF8rr/azzhQVlM= +github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.13.3/go.mod h1:2ouUT4kdhUBk7TAkHWD4SN0CdI0pgEQbo8FVHhbSKWg= github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= +github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -603,12 +733,14 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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.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 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -627,8 +759,9 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= +github.com/google/uuid v1.2.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/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= @@ -674,6 +807,7 @@ github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBt github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -681,8 +815,10 @@ github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9 github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= 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 v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= @@ -717,12 +853,15 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= @@ -775,12 +914,14 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= 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 h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= 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/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= @@ -791,9 +932,12 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL 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.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 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.2/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= @@ -821,6 +965,7 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9 github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= +github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -833,6 +978,7 @@ github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7 github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/markbates/pkger v0.17.1 h1:/MKEtWqtc0mZvu9OinB9UzVN9iYCwLWuyUv4Bw+PCno= @@ -852,7 +998,10 @@ github.com/mattn/go-oci8 v0.0.7/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mN github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.11/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= @@ -864,12 +1013,14 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182aff github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/pkcs11 v0.0.0-20170220202408-7283ca79f35e/go.mod h1:WCBAbTOdfhHhz7YXujeZMF7owC4tPb1naKFsgfUISjo= +github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4= github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= github.com/minio/minio-go/v6 v6.0.57 h1:ixPkbKkyD7IhnluRgQpGSpHdpvNVaW6OD5R9IAO/9Tw= github.com/minio/minio-go/v6 v6.0.57/go.mod h1:5+R/nM9Pwrh0vqF+HbYYDQ84wdUFPyXHkrdT4AIkifM= github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.1.1 h1:Bp6x9R1Wn16SIz3OfeDr0b7RnCG2OB66Y7PQyC/cvq4= @@ -893,9 +1044,12 @@ github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQ github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +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.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= @@ -904,14 +1058,16 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +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/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d h1:LznySqW8MqVeFh+pW6rOkFdld9QQ7jRydBKKM6jyPVI= github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d/go.mod h1:u3hJ0kqCQu/cPpsu3RbCOPZ0d7V3IjPjv1adNRleM9I= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/mtrmac/gpgme v0.1.2/go.mod h1:GYYHnGSuS7HK3zVS2n3y73y0okK/BeKzwnn5jgiVFNI= github.com/munnerz/crd-schema-fuzz v1.0.0/go.mod h1:4z/rcm37JxUkSsExFcLL6ZIT1SgDRdLiu7qq1evdVS0= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= @@ -945,17 +1101,20 @@ github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:v github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.4.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -964,22 +1123,37 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU= github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= 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.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= github.com/opencontainers/runc v1.0.0-rc95/go.mod h1:z+bZxa/+Tz/FmYVWkhUajJdzFeOqjc5vrqskhVyHGUM= +github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= +github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/opencontainers/selinux v1.8.5/go.mod h1:HTvjPFoGMbpQsG886e3lQwnsRWtE4TC1OF3OUvG9FAo= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w= github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= @@ -992,6 +1166,7 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc= github.com/ovh/configstore v0.3.2 h1:/kr1B27JVzW4Eiz20muZSnQ5UyizFjLy5+2CVfp/mKs= github.com/ovh/configstore v0.3.2/go.mod h1:bBc7U++7HXgf9lrtmmJb31DK3Tp+Zv8GaIn0Bjolv/o= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= @@ -1002,12 +1177,14 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/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= @@ -1023,6 +1200,7 @@ github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4 github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 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.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= @@ -1041,6 +1219,7 @@ github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7q github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 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.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= @@ -1050,7 +1229,10 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 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.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= @@ -1062,6 +1244,7 @@ github.com/r3labs/diff v1.1.0/go.mod h1:7WjXasNzi0vJetRcB/RqNl5dlIsmXcTTLmF5IoH6 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/robfig/cron v1.0.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -1079,6 +1262,7 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= @@ -1094,6 +1278,8 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= github.com/siddontang/ledisdb v0.0.0-20181029004158-becf5f38d373/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg= github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= @@ -1138,15 +1324,18 @@ github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/spotahome/redis-operator v1.0.0 h1:bPdPTcDdo1l7U5fQNpNAVb4n8+ms4wyyeuYOgGQeW5E= github.com/spotahome/redis-operator v1.0.0/go.mod h1:fg0aPOlKkHRKP98RBJ4lWw+EV6VkT7Dxf9HlQuBJuwE= github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE= +github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1155,11 +1344,15 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/szlabs/kooper v0.22.3/go.mod h1:Mv4q3PvVA2mckE1X4GtDKsB7kaR2HTahba/s+NmIEek= github.com/szlabs/redis-operator v1.0.1 h1:qPJIGhP/+qngOXcwgaJ75lYGhRFBxvZVvLBdGZupcZo= github.com/szlabs/redis-operator v1.0.1/go.mod h1:J6Qsd2bJzpGha9IrMNk4TzV0y2xfI8dVOSa0B5b4Fko= +github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tencentcloud/tencentcloud-sdk-go v1.0.62/go.mod h1:asUz5BPXxgoPGaRgZaVm1iGcUAuHyYUo1nXqKa83cvI= github.com/theupdateframework/notary v0.6.1 h1:7wshjstgS9x9F5LuB1L5mBI2xNMObWqjz+cjWoom6l0= github.com/theupdateframework/notary v0.6.1/go.mod h1:MOfgIfmox8s7/7fduvB2xyPPMJCrjRLRizA8OFwpnKY= @@ -1177,17 +1370,28 @@ github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/ github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/umisama/go-regexpcache v0.0.0-20150417035358-2444a542492f h1:haUDHoDEHXYsmhhJ9DwOcJBGtgRSCT6d5J1EcqxMFuU= +github.com/umisama/go-regexpcache v0.0.0-20150417035358-2444a542492f/go.mod h1:YTm0hcnGJEKJOLVM4x0PvO8p43r7DANkXRNiONPfWIM= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= +github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= +github.com/vbauerster/mpb/v7 v7.1.5/go.mod h1:4M8+qAoQqV60WDNktBM5k05i1iTrXE7rjKOHEVkVlec= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vmihailenco/msgpack/v5 v5.0.0-rc.2/go.mod h1:HVxBVPUK/+fZMonk4bi1islLa8V3cfnBug0+4dykPzo= github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc= +github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= @@ -1197,7 +1401,9 @@ github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhe github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190809123943-df4f5c81cb3b/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= @@ -1243,6 +1449,7 @@ go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4S go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw= go.mongodb.org/mongo-driver v1.7.3 h1:G4l/eYY9VrQAK/AUgkV0koQKzQnyddnWxrd/Etf0jIs= go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1316,7 +1523,9 @@ go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.19.0 h1:mZQZefskPPCMIBCSEH0v2/iUqqLrYtaeqwD6FUGUnFE= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1342,11 +1551,13 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1391,6 +1602,7 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/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-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1433,12 +1645,14 @@ golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 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-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/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-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= @@ -1460,6 +1674,7 @@ golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= @@ -1487,7 +1702,9 @@ golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1495,6 +1712,8 @@ golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/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-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1509,6 +1728,7 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/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= @@ -1518,6 +1738,7 @@ golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1530,17 +1751,24 @@ 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-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/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-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/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-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/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-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1551,6 +1779,7 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/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-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 h1:xrCZDmdtoloIiooiA9q0OQb9r8HejIHYoHGhGCe1pGg= golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1698,6 +1927,7 @@ google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= @@ -1707,6 +1937,7 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200128133413-58ce757ed39b/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= @@ -1740,13 +1971,16 @@ google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 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.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= @@ -1767,6 +2001,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1781,6 +2016,7 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fatih/pool.v2 v2.0.0/go.mod h1:8xVGeu1/2jr2wm5V9SPuMht2H5AEmf5aFMGSQixtjTY= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/gorethink/gorethink.v3 v3.0.5/go.mod h1:+3yIIHJUGMBK+wyPH+iN5TP+88ikFDfZdqTlK3Y9q8I= gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= gopkg.in/h2non/gentleman.v1 v1.0.4/go.mod h1:JYuHVdFzS4MKOXe0o+chKJ4hCe6tqKKw9XH9YP6WFrg= @@ -1837,7 +2073,10 @@ k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8= k8s.io/api v0.18.6/go.mod h1:eeyxr+cwCjMdLAmr2W3RyDI0VvTawSg/3RFFBEnmZGI= k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw= k8s.io/api v0.19.4/go.mod h1:SbtJ2aHCItirzdJ36YslycFNzWADYH3tgOhvBEFtZAk= +k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8= +k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= +k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= k8s.io/api v0.21.0/go.mod h1:+YbrhBBGgsxbF6o6Kj4KJPJnBmAKuXDeS3E18bgHNVU= k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8= k8s.io/api v0.22.3 h1:wOoES2GoSkUsdped2RB4zYypPqWtvprGoKCENTOOjP4= @@ -1857,7 +2096,10 @@ k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftc k8s.io/apimachinery v0.18.6/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= k8s.io/apimachinery v0.19.4/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= +k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= k8s.io/apimachinery v0.21.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apimachinery v0.22.3 h1:mrvBG5CZnEfwgpVqWcrRKvdsYECTrhAR6cApAgdsflk= @@ -1867,6 +2109,9 @@ k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw= k8s.io/apiserver v0.18.6/go.mod h1:Zt2XvTHuaZjBz6EFYzpp+X4hTmgWGy8AthNVnTdm3Wg= k8s.io/apiserver v0.19.0/go.mod h1:XvzqavYj73931x7FLtyagh8WibHpePJ1QwWrSJs2CLk= k8s.io/apiserver v0.19.4/go.mod h1:X8WRHCR1UGZDd7HpV0QDc1h/6VbbpAeAGyxSh8yzZXw= +k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= +k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= +k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= k8s.io/apiserver v0.21.0/go.mod h1:w2YSn4/WIwYuxG5zJmcqtRdtqgW/J2JRgFAqps3bBpg= k8s.io/apiserver v0.22.2/go.mod h1:vrpMmbyjWrgdyOvZTSpsusQq5iigKNWv9o9KlDAbBHI= k8s.io/apiserver v0.22.3/go.mod h1:oam7lH/F1Kto/WTamyQYrD68fS0mGUBORAFf6x/9Mxs= @@ -1878,7 +2123,10 @@ k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8= k8s.io/client-go v0.18.6/go.mod h1:/fwtGLjYMS1MaM5oi+eXhKwG+1UHidUEXRh6cNsdO0Q= k8s.io/client-go v0.19.0/go.mod h1:H9E/VT95blcFQnlyShFgnFT9ZnJOAceiUHM3MlRC+mU= k8s.io/client-go v0.19.4/go.mod h1:ZrEy7+wj9PjH5VMBCuu/BDlvtUAku0oVFk4MmnW9mWA= +k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.2/go.mod h1:kH5brqWqp7HDxUFKoEgiI4v8G1xzbe9giaCenUWJzgE= +k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= +k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= k8s.io/client-go v0.21.0/go.mod h1:nNBytTF9qPFDEhoqgEPaarobC8QPae13bElIVHzIglA= k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U= k8s.io/client-go v0.22.3 h1:6onkOSc+YNdwq5zXE0wFXicq64rrym+mXwHu/CPVGO4= @@ -1896,11 +2144,18 @@ k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1 k8s.io/component-base v0.18.6/go.mod h1:knSVsibPR5K6EW2XOjEHik6sdU5nCvKMrzMt2D4In14= k8s.io/component-base v0.19.0/go.mod h1:dKsY8BxkA+9dZIAh2aWJLL/UdASFDNtGYTCItL4LM7Y= k8s.io/component-base v0.19.4/go.mod h1:ZzuSLlsWhajIDEkKF73j64Gz/5o0AgON08FgRbEPI70= +k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= +k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= +k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= k8s.io/component-base v0.21.0/go.mod h1:qvtjz6X0USWXbgmbfXR+Agik4RZ3jv2Bgr5QnZzdPYw= k8s.io/component-base v0.22.2/go.mod h1:5Br2QhI9OTe79p+TzPe9JKNQYvEKbq9rTJDWllunGug= k8s.io/component-base v0.22.3 h1:/+hryAW03u3FpJQww+GSMsArJNUbGjH66lrgxaRynLU= k8s.io/component-base v0.22.3/go.mod h1:kuybv1miLCMoOk3ebrqF93GbQHQx6W2287FC0YEQY6s= k8s.io/component-helpers v0.21.0/go.mod h1:tezqefP7lxfvJyR+0a+6QtVrkZ/wIkyMLK4WcQ3Cj8U= +k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= +k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= @@ -1931,6 +2186,7 @@ k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kubectl v0.19.0/go.mod h1:gPCjjsmE6unJzgaUNXIFGZGafiUp5jh0If3F/x7/rRg= k8s.io/kubectl v0.21.0/go.mod h1:EU37NukZRXn1TpAkMUoy8Z/B2u6wjHDS4aInsDzVvks= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/metrics v0.19.0/go.mod h1:WykpW8B60OeAJx1imdwUgyOID2kDljr/Q+1zrPJ98Wo= k8s.io/metrics v0.21.0/go.mod h1:L3Ji9EGPP1YBbfm9sPfEXSpnj8i24bfQbAFAsW0NueQ= k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= @@ -1962,6 +2218,7 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns= @@ -1984,6 +2241,7 @@ sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= diff --git a/manifests/cluster/deployment.yaml b/manifests/cluster/deployment.yaml index 579a72030..657b439d9 100644 --- a/manifests/cluster/deployment.yaml +++ b/manifests/cluster/deployment.yaml @@ -12076,6 +12076,150 @@ status: --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.5.0 + goharbor.io/deploy-engine: Kustomization + goharbor.io/deploy-mode: cluster + goharbor.io/operator-version: v1.1.0 + creationTimestamp: null + name: harborserverconfigurations.goharbor.io +spec: + group: goharbor.io + names: + categories: + - goharbor + kind: HarborServerConfiguration + listKind: HarborServerConfigurationList + plural: harborserverconfigurations + shortNames: + - hsc + singular: harborserverconfiguration + scope: Cluster + versions: + - additionalPrinterColumns: + - description: The public URL to the Harbor server + jsonPath: .spec.serverURL + name: Harbor Server + type: string + - description: The status of the Harbor server + jsonPath: .status.status + name: Status + type: string + - description: The version of the Harbor server + jsonPath: .spec.version + name: Version + priority: 5 + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: HarborServerConfiguration is the Schema for the harborserverconfigurations 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: HarborServerConfigurationSpec defines the desired state of HarborServerConfiguration. + properties: + accessCredential: + description: AccessCredential is a namespaced credential to keep the access key and secret for the harbor server configuration. + properties: + accessSecretRef: + pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*' + type: string + namespace: + pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*' + type: string + required: + - accessSecretRef + - namespace + type: object + default: + description: Default indicates the harbor configuration manages namespaces. Value in goharbor.io/harbor annotation will be considered with high priority. At most, one HarborServerConfiguration can be the default, multiple defaults will be rejected. + type: boolean + insecure: + description: Indicate if the Harbor server is an insecure registry + type: boolean + namespaceSelector: + description: "NamespaceSelector decides whether to apply the HSC on a namespace based on whether the namespace matches the selector. See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors. \n Default to the empty LabelSelector, which matches everything." + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + rules: + description: Rules configures the container image rewrite rules for transparent proxy caching with Harbor. + items: + type: string + type: array + serverURL: + pattern: (?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)+([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9]) + type: string + version: + description: The version of the Harbor server + pattern: (0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))? + type: string + required: + - accessCredential + - serverURL + - version + type: object + status: + description: HarborConfigurationStatus defines the status of HarborServerConfiguration. + properties: + message: + description: Message provides human-readable message. + type: string + reason: + description: Reason represents status reason. + type: string + status: + description: Status represents harbor configuration status. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: harbor-operator-ns/serving-cert @@ -18185,6 +18329,103 @@ status: --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.5.0 + goharbor.io/deploy-engine: Kustomization + goharbor.io/deploy-mode: cluster + goharbor.io/operator-version: v1.1.0 + creationTimestamp: null + name: pullsecretbindings.goharbor.io +spec: + group: goharbor.io + names: + categories: + - goharbor + kind: PullSecretBinding + listKind: PullSecretBindingList + plural: pullsecretbindings + shortNames: + - psb + singular: pullsecretbinding + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The Harbor server configuration CR reference + jsonPath: .spec.harborServerConfig + name: Harbor Server + type: string + - description: The service account binding the pull secret + jsonPath: .spec.serviceAccount + name: Service Account + type: string + - description: The status of the Harbor server + jsonPath: .status.status + name: Status + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: PullSecretBinding is the Schema for the pullsecretbindings 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: PullSecretBindingSpec defines the desired state of PullSecretBinding. + properties: + harborServerConfig: + description: Indicate which harbor server configuration is referred + type: string + projectId: + description: ProjectID points to the project associated with the secret binding + type: string + robotId: + description: RobotID points to the robot account id used for secret binding + type: string + serviceAccount: + description: Indicate which service account binds the pull secret + type: string + required: + - harborServerConfig + - projectId + - robotId + - serviceAccount + type: object + status: + description: PullSecretBindingStatus defines the observed state of PullSecretBinding. + properties: + message: + description: Message provides human-readable message. + type: string + reason: + description: Reason represents status reason. + type: string + status: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file Indicate the status of binding: `binding`, `bound` and `unknown`' + type: string + required: + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: harbor-operator-ns/serving-cert @@ -35100,6 +35341,26 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - namespaces + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - namespaces/status + verbs: + - get + - patch + - update - apiGroups: - "" resources: @@ -35160,6 +35421,16 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - get + - list + - patch + - update + - watch - apiGroups: - "" resources: @@ -35452,6 +35723,26 @@ rules: - get - patch - update +- apiGroups: + - goharbor.io + resources: + - harborserverconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - goharbor.io + resources: + - harborserverconfigurations/status + verbs: + - get + - patch + - update - apiGroups: - goharbor.io resources: @@ -35516,6 +35807,26 @@ rules: - get - patch - update +- apiGroups: + - goharbor.io + resources: + - pullsecretbindings + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - goharbor.io + resources: + - pullsecretbindings/status + verbs: + - get + - patch + - update - apiGroups: - goharbor.io resources: @@ -36923,6 +37234,32 @@ metadata: goharbor.io/operator-version: v1.1.0 name: mutating-webhook-configuration webhooks: +- admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: harbor-operator-ns + path: /mutate-image-path + failurePolicy: Fail + name: mimg.kb.io + namespaceSelector: + matchExpressions: + - key: harbor-day2-webhook-configuration + operator: In + values: + - enabled + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - pods + sideEffects: NoneOnDryRun - admissionReviewVersions: - v1beta1 - v1 @@ -37039,3 +37376,23 @@ webhooks: resources: - registries sideEffects: None +- admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: harbor-operator-ns + path: /validate-hsc + failurePolicy: Fail + name: hsc.goharbor.io + rules: + - apiGroups: + - goharbor.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - harborserverconfigurations + sideEffects: None diff --git a/manifests/cluster/patch/namespace.yaml b/manifests/cluster/patch/namespace.yaml index 42ac44773..58dc7cb67 100644 --- a/manifests/cluster/patch/namespace.yaml +++ b/manifests/cluster/patch/namespace.yaml @@ -1,4 +1,4 @@ apiVersion: v1 kind: Namespace metadata: - name: harbor-operator-ns \ No newline at end of file + name: harbor-operator-ns diff --git a/manifests/harbor/deployment.yaml b/manifests/harbor/deployment.yaml index 34bc3ce51..495d7ae53 100644 --- a/manifests/harbor/deployment.yaml +++ b/manifests/harbor/deployment.yaml @@ -12076,6 +12076,150 @@ status: --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.5.0 + goharbor.io/deploy-engine: Kustomization + goharbor.io/deploy-mode: harbor + goharbor.io/operator-version: v1.1.0 + creationTimestamp: null + name: harborserverconfigurations.goharbor.io +spec: + group: goharbor.io + names: + categories: + - goharbor + kind: HarborServerConfiguration + listKind: HarborServerConfigurationList + plural: harborserverconfigurations + shortNames: + - hsc + singular: harborserverconfiguration + scope: Cluster + versions: + - additionalPrinterColumns: + - description: The public URL to the Harbor server + jsonPath: .spec.serverURL + name: Harbor Server + type: string + - description: The status of the Harbor server + jsonPath: .status.status + name: Status + type: string + - description: The version of the Harbor server + jsonPath: .spec.version + name: Version + priority: 5 + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: HarborServerConfiguration is the Schema for the harborserverconfigurations 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: HarborServerConfigurationSpec defines the desired state of HarborServerConfiguration. + properties: + accessCredential: + description: AccessCredential is a namespaced credential to keep the access key and secret for the harbor server configuration. + properties: + accessSecretRef: + pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*' + type: string + namespace: + pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*' + type: string + required: + - accessSecretRef + - namespace + type: object + default: + description: Default indicates the harbor configuration manages namespaces. Value in goharbor.io/harbor annotation will be considered with high priority. At most, one HarborServerConfiguration can be the default, multiple defaults will be rejected. + type: boolean + insecure: + description: Indicate if the Harbor server is an insecure registry + type: boolean + namespaceSelector: + description: "NamespaceSelector decides whether to apply the HSC on a namespace based on whether the namespace matches the selector. See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors. \n Default to the empty LabelSelector, which matches everything." + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + rules: + description: Rules configures the container image rewrite rules for transparent proxy caching with Harbor. + items: + type: string + type: array + serverURL: + pattern: (?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)+([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9]) + type: string + version: + description: The version of the Harbor server + pattern: (0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))? + type: string + required: + - accessCredential + - serverURL + - version + type: object + status: + description: HarborConfigurationStatus defines the status of HarborServerConfiguration. + properties: + message: + description: Message provides human-readable message. + type: string + reason: + description: Reason represents status reason. + type: string + status: + description: Status represents harbor configuration status. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: harbor-operator-ns/serving-cert @@ -18185,6 +18329,103 @@ status: --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.5.0 + goharbor.io/deploy-engine: Kustomization + goharbor.io/deploy-mode: harbor + goharbor.io/operator-version: v1.1.0 + creationTimestamp: null + name: pullsecretbindings.goharbor.io +spec: + group: goharbor.io + names: + categories: + - goharbor + kind: PullSecretBinding + listKind: PullSecretBindingList + plural: pullsecretbindings + shortNames: + - psb + singular: pullsecretbinding + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The Harbor server configuration CR reference + jsonPath: .spec.harborServerConfig + name: Harbor Server + type: string + - description: The service account binding the pull secret + jsonPath: .spec.serviceAccount + name: Service Account + type: string + - description: The status of the Harbor server + jsonPath: .status.status + name: Status + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: PullSecretBinding is the Schema for the pullsecretbindings 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: PullSecretBindingSpec defines the desired state of PullSecretBinding. + properties: + harborServerConfig: + description: Indicate which harbor server configuration is referred + type: string + projectId: + description: ProjectID points to the project associated with the secret binding + type: string + robotId: + description: RobotID points to the robot account id used for secret binding + type: string + serviceAccount: + description: Indicate which service account binds the pull secret + type: string + required: + - harborServerConfig + - projectId + - robotId + - serviceAccount + type: object + status: + description: PullSecretBindingStatus defines the observed state of PullSecretBinding. + properties: + message: + description: Message provides human-readable message. + type: string + reason: + description: Reason represents status reason. + type: string + status: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file Indicate the status of binding: `binding`, `bound` and `unknown`' + type: string + required: + - status + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: harbor-operator-ns/serving-cert @@ -26838,6 +27079,26 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - namespaces + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - namespaces/status + verbs: + - get + - patch + - update - apiGroups: - "" resources: @@ -26898,6 +27159,16 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - get + - list + - patch + - update + - watch - apiGroups: - "" resources: @@ -27190,6 +27461,26 @@ rules: - get - patch - update +- apiGroups: + - goharbor.io + resources: + - harborserverconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - goharbor.io + resources: + - harborserverconfigurations/status + verbs: + - get + - patch + - update - apiGroups: - goharbor.io resources: @@ -27254,6 +27545,26 @@ rules: - get - patch - update +- apiGroups: + - goharbor.io + resources: + - pullsecretbindings + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - goharbor.io + resources: + - pullsecretbindings/status + verbs: + - get + - patch + - update - apiGroups: - goharbor.io resources: @@ -27941,6 +28252,32 @@ metadata: goharbor.io/operator-version: v1.1.0 name: mutating-webhook-configuration webhooks: +- admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: harbor-operator-ns + path: /mutate-image-path + failurePolicy: Fail + name: mimg.kb.io + namespaceSelector: + matchExpressions: + - key: harbor-day2-webhook-configuration + operator: In + values: + - enabled + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - pods + sideEffects: NoneOnDryRun - admissionReviewVersions: - v1beta1 - v1 @@ -28057,3 +28394,23 @@ webhooks: resources: - registries sideEffects: None +- admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: webhook-service + namespace: harbor-operator-ns + path: /validate-hsc + failurePolicy: Fail + name: hsc.goharbor.io + rules: + - apiGroups: + - goharbor.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - harborserverconfigurations + sideEffects: None diff --git a/manifests/harbor/namespace.yaml b/manifests/harbor/namespace.yaml index 42ac44773..58dc7cb67 100644 --- a/manifests/harbor/namespace.yaml +++ b/manifests/harbor/namespace.yaml @@ -1,4 +1,4 @@ apiVersion: v1 kind: Namespace metadata: - name: harbor-operator-ns \ No newline at end of file + name: harbor-operator-ns diff --git a/pkg/cluster/controllers/database/api/zz_generated.deepcopy.go b/pkg/cluster/controllers/database/api/zz_generated.deepcopy.go index 63cb084de..bcd589acf 100644 --- a/pkg/cluster/controllers/database/api/zz_generated.deepcopy.go +++ b/pkg/cluster/controllers/database/api/zz_generated.deepcopy.go @@ -1,4 +1,3 @@ -//go:build !ignore_autogenerated // +build !ignore_autogenerated /* diff --git a/pkg/cluster/controllers/storage/minio/apis/minio.min.io/v2/zz_generated.deepcopy.go b/pkg/cluster/controllers/storage/minio/apis/minio.min.io/v2/zz_generated.deepcopy.go index 38efb7343..a189dacf4 100644 --- a/pkg/cluster/controllers/storage/minio/apis/minio.min.io/v2/zz_generated.deepcopy.go +++ b/pkg/cluster/controllers/storage/minio/apis/minio.min.io/v2/zz_generated.deepcopy.go @@ -1,4 +1,3 @@ -//go:build !ignore_autogenerated // +build !ignore_autogenerated // Code generated by deepcopy-gen. DO NOT EDIT. diff --git a/pkg/registry/secret/object.go b/pkg/registry/secret/object.go new file mode 100644 index 000000000..408c84620 --- /dev/null +++ b/pkg/registry/secret/object.go @@ -0,0 +1,29 @@ +package secret + +import ( + "encoding/base64" + "encoding/json" + "fmt" +) + +type Object struct { + Auths map[string]*Auth `json:"auths"` +} + +type Auth struct { + Username string `json:"username"` + Password string `json:"password"` + Email string `json:"email"` + Auth string `json:"auth"` +} + +func (o *Object) Encode() []byte { + for _, auth := range o.Auths { + au := fmt.Sprintf("%s:%s", auth.Username, auth.Password) + auth.Auth = base64.StdEncoding.EncodeToString([]byte(au)) + } + + bytes, _ := json.Marshal(o) + + return bytes +} diff --git a/pkg/rest/client.go b/pkg/rest/client.go new file mode 100644 index 000000000..de927b1d8 --- /dev/null +++ b/pkg/rest/client.go @@ -0,0 +1,55 @@ +package rest + +import ( + "context" + "fmt" + + goharborv1beta1 "github.com/goharbor/harbor-operator/apis/goharbor.io/v1beta1" + "github.com/goharbor/harbor-operator/pkg/rest/model" + v2 "github.com/goharbor/harbor-operator/pkg/rest/v2" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func CreateHarborV2Client(ctx context.Context, client client.Client, hsc *goharborv1beta1.HarborServerConfiguration) (*v2.Client, error) { + server, err := createHarborServer(ctx, client, hsc) + if err != nil { + return nil, err + } + + return v2.NewWithServer(server) +} + +// Check if the server configuration is valid. +// That is checking if the admin password secret object is valid. +func createHarborServer(ctx context.Context, client client.Client, hsc *goharborv1beta1.HarborServerConfiguration) (*model.HarborServer, error) { + // construct accessCreds from Secret + secretNSedName := types.NamespacedName{ + Namespace: hsc.Spec.AccessCredential.Namespace, + Name: hsc.Spec.AccessCredential.AccessSecretRef, + } + + username, password, err := createAccessCredsFromSecret(ctx, client, secretNSedName) + if err != nil { + return nil, err + } + + // put server config into client + return model.NewHarborServer(hsc.Spec.ServerURL, username, password, hsc.Spec.Insecure), nil +} + +func createAccessCredsFromSecret(ctx context.Context, client client.Client, secretNSedName types.NamespacedName) (string, string, error) { + accessSecret := &corev1.Secret{} + if err := client.Get(ctx, secretNSedName, accessSecret); err != nil { + // No matter what errors (including not found) occurred, the server configuration is invalid + return "", "", fmt.Errorf("get access secret error: %w", err) + } + + username, password, err := model.GetCredential(accessSecret) + if err != nil { + return "", "", fmt.Errorf("get credential error: %w", err) + } + + return username, password, nil +} diff --git a/pkg/rest/model/harbor_server.go b/pkg/rest/model/harbor_server.go new file mode 100644 index 000000000..4a20845f2 --- /dev/null +++ b/pkg/rest/model/harbor_server.go @@ -0,0 +1,85 @@ +package model + +import ( + gruntime "github.com/go-openapi/runtime" + hc "github.com/goharbor/go-client/pkg/harbor" + assistclient "github.com/goharbor/go-client/pkg/sdk/assist/client" + v2client "github.com/goharbor/go-client/pkg/sdk/v2.0/client" + legacyclient "github.com/goharbor/go-client/pkg/sdk/v2.0/legacy/client" + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" +) + +const ( + accessKey = "accessKey" + accessSecret = "accessSecret" +) + +// GetCredential put secret into AccessCred. +func GetCredential(secret *corev1.Secret) (string, string, error) { + decodedAK, ok1 := secret.Data[accessKey] + decodedAS, ok2 := secret.Data[accessSecret] + + if !(ok1 && ok2) { + return "", "", errors.New("invalid access secret") + } + + if len(decodedAK) == 0 || len(decodedAS) == 0 { + return "", "", errors.New("access key and secret can't be empty") + } + + return string(decodedAK), string(decodedAS), nil +} + +// HarborServer contains connection data. +type HarborServer struct { + ServerURL string + Username string + Password string + Insecure bool +} + +// NewHarborServer returns harbor server with inputs. +func NewHarborServer(url, username, password string, insecure bool) *HarborServer { + return &HarborServer{ + ServerURL: url, + Username: username, + Password: password, + Insecure: insecure, + } +} + +// HarborAssistClient keeps Harbor client. +type HarborAssistClient struct { + Client *assistclient.HarborAPI +} + +// HarborLegacyClient keeps Harbor client. +type HarborLegacyClient struct { + Client *legacyclient.HarborAPI +} + +// HarborClientV2 keeps Harbor client v2. +type HarborClientV2 struct { + Client *v2client.HarborAPI + Auth gruntime.ClientAuthInfoWriter +} + +// ClientV2 created based on the server data. Harbor V2 API. +func (h *HarborServer) ClientV2() (*HarborClientV2, error) { + c := &hc.ClientSetConfig{ + URL: h.ServerURL, + Username: h.Username, + Password: h.Password, + Insecure: h.Insecure, + } + + cs, err := hc.NewClientSet(c) + if err != nil { + return nil, err + } + + return &HarborClientV2{ + Client: cs.V2(), + }, nil +} diff --git a/pkg/rest/v2/client.go b/pkg/rest/v2/client.go new file mode 100644 index 000000000..7df56fb5b --- /dev/null +++ b/pkg/rest/v2/client.go @@ -0,0 +1,276 @@ +package v2 + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/go-logr/logr" + "github.com/goharbor/go-client/pkg/sdk/v2.0/client/health" + "github.com/goharbor/go-client/pkg/sdk/v2.0/client/project" + "github.com/goharbor/go-client/pkg/sdk/v2.0/client/robotv1" + "github.com/goharbor/go-client/pkg/sdk/v2.0/models" + "github.com/goharbor/harbor-operator/pkg/rest/model" + utilstring "github.com/goharbor/harbor-operator/pkg/utils/strings" + "github.com/pkg/errors" + ctrl "sigs.k8s.io/controller-runtime" +) + +// Client for talking to Harbor V2 API +// Wrap based on sdk v2. +type Client struct { + // Server info for talking to + server *model.HarborServer + // Timeout for client connection + timeout time.Duration + // Context for doing client connection + context context.Context + // Harbor API client + harborClient *model.HarborClientV2 + // Logger + log logr.Logger +} + +// New V2 client. +func New() *Client { + // Initialize with default settings + return &Client{ + timeout: 30 * time.Second, // nolint:gomnd + context: context.Background(), + log: ctrl.Log.WithName("v2").WithName("client"), + } +} + +// NewWithServer new V2 client with provided server. +func NewWithServer(s *model.HarborServer) (*Client, error) { + var err error + // Initialize with default settings + c := New() + c.server = s + c.harborClient, err = s.ClientV2() + + if err != nil { + return nil, err + } + + return c, nil +} + +func (c *Client) WithContext(ctx context.Context) *Client { + if ctx != nil { + c.context = ctx + } + + return c +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.timeout = timeout + + return c +} + +// EnsureProject ensures the specified project is on the harbor server +// If project with name is existing, then error will be nil. +func (c *Client) EnsureProject(name string) (int64, error) { + if len(name) == 0 { + return -1, errors.New("project name is empty") + } + + if c.harborClient == nil { + return -1, errors.New("nil harbor client") + } + + // Check existence first + p, err := c.GetProject(name) + if err == nil { + return int64(p.ProjectID), nil + } + + if err != nil { + if !strings.Contains(err.Error(), "no project with name") { + return 0, errors.Errorf("error when getting project %s: %s", name, err) + } + } + + fmt.Println("creating project since target project doesn't exist") + + // Create one when the project does not exist + cparams := project.NewCreateProjectParamsWithContext(c.context). + WithTimeout(c.timeout). + WithProject(&models.ProjectReq{ + ProjectName: name, + Metadata: &models.ProjectMetadata{ + Public: "false", + }, + }) + + cp, err := c.harborClient.Client.Project.CreateProject(c.context, cparams) + if err != nil { + return -1, fmt.Errorf("ensure project error: %w", err) + } + + return utilstring.ExtractID(cp.Location) +} + +// GetProject gets the project data. +func (c *Client) GetProject(name string) (*models.Project, error) { + if len(name) == 0 { + return nil, errors.New("project name is empty") + } + + if c.harborClient == nil { + return nil, errors.New("nil harbor client") + } + // Use listProject endpoint since getProject requires project id query key + params := project.NewListProjectsParamsWithContext(c.context). + WithTimeout(c.timeout). + WithName(&name) + + res, err := c.harborClient.Client.Project.ListProjects(c.context, params) + if err != nil { + return nil, fmt.Errorf("get project error: %w", err) + } + + if len(res.Payload) < 1 { + return nil, errors.Errorf("no project with name %s exists", name) + } + + return res.Payload[0], nil +} + +// DeleteProject deletes project. +func (c *Client) DeleteProject(name string) error { + if len(name) == 0 { + return errors.New("project name is empty") + } + + if c.harborClient == nil { + return errors.New("nil harbor client") + } + + // Get ID first + p, err := c.GetProject(name) + if err != nil { + return fmt.Errorf("delete project error: %w", err) + } + + params := project.NewDeleteProjectParamsWithContext(c.context). + WithTimeout(c.timeout). + WithProjectNameOrID(string(p.ProjectID)) + + if _, err = c.harborClient.Client.Project.DeleteProject(c.context, params); err != nil { + return err + } + + return nil +} + +func (c *Client) CheckHealth() (*models.OverallHealthStatus, error) { + params := health.NewGetHealthParams(). + WithTimeout(c.timeout) + + res, err := c.harborClient.Client.Health.GetHealth(c.context, params) + if err != nil { + return nil, err + } + + return res.Payload, nil +} + +func (c *Client) CreateRobotAccount(projectID string) (*models.Robot, error) { + if len(projectID) == 0 { + return nil, errors.New("empty project id") + } + + if c.harborClient == nil { + return nil, errors.New("nil harbor client") + } + + params := robotv1.NewCreateRobotV1Params(). + WithTimeout(c.timeout). + WithProjectNameOrID(projectID). + WithRobot(&models.RobotCreateV1{ + Access: []*models.Access{ + { + Action: "push", + Resource: fmt.Sprintf("/project/%s/repository", projectID), + }, + }, + Description: "automated by harbor automation operator", + ExpiresAt: -1, // never + Name: utilstring.RandomName("4k8s"), + }) + + res, err := c.harborClient.Client.Robotv1.CreateRobotV1(c.context, params) + if err != nil { + return nil, err + } + + rid, err := utilstring.ExtractID(res.Location) + if err != nil { + // ignore this error that should never happen + c.log.Error(err, "location", res.Location) + } + + return &models.Robot{ + ID: rid, + Name: res.Payload.Name, + Secret: res.Payload.Secret, + }, nil +} + +func (c *Client) DeleteRobotAccount(projectID, robotID int64) error { + if projectID <= 0 { + return errors.New("invalid project id") + } + + if robotID <= 0 { + return errors.New("invalid robot id") + } + + if c.harborClient == nil { + return errors.New("nil harbor client") + } + + params := robotv1.NewDeleteRobotV1Params(). + WithTimeout(c.timeout). + WithProjectNameOrID(fmt.Sprintf("%d", projectID)). + WithRobotID(robotID) + + if _, err := c.harborClient.Client.Robotv1.DeleteRobotV1(c.context, params); err != nil { + return err + } + + return nil +} + +func (c *Client) GetRobotAccount(projectID, robotID int64) (*models.Robot, error) { + if projectID <= 0 { + return nil, errors.New("invalid project id") + } + + if robotID <= 0 { + return nil, errors.New("invalid robot id") + } + + if c.harborClient == nil { + return nil, errors.New("nil harbor client") + } + + params := robotv1.NewGetRobotByIDV1Params(). + WithTimeout(c.timeout). + WithProjectNameOrID(fmt.Sprintf("%d", projectID)). + WithRobotID(robotID) + + res, err := c.harborClient.Client.Robotv1.GetRobotByIDV1(c.context, params) + if err != nil { + return nil, err + } + + return &models.Robot{ + ID: robotID, + Name: res.Payload.Name, + }, nil +} diff --git a/pkg/rule/rule.go b/pkg/rule/rule.go new file mode 100644 index 000000000..e0e64c03e --- /dev/null +++ b/pkg/rule/rule.go @@ -0,0 +1,33 @@ +package rule + +import "strings" + +type Rule struct { + RegistryRegex string + Project string + ServerURL string +} + +// assume rule.Rules are concatentated by ','. +func StringToRules(raw []string, server string) []Rule { + res := make([]Rule, 0) + + for _, r := range raw { + registryRegex := r[:strings.LastIndex(r, ",")] + project := r[strings.LastIndex(r, ",")+1:] + + res = append(res, Rule{ + RegistryRegex: registryRegex, + Project: project, + ServerURL: server, + }) + } + + return res +} + +// append l after h, so l will be checked first. +// there could be cases that regex in h is `gcr.io`, while in l is `gcr.io*`. +func MergeRules(l, h []Rule) []Rule { + return append(h, l...) +} diff --git a/pkg/setup/controllers.go b/pkg/setup/controllers.go index a696d10bd..ec3b2c347 100644 --- a/pkg/setup/controllers.go +++ b/pkg/setup/controllers.go @@ -13,10 +13,13 @@ import ( "github.com/goharbor/harbor-operator/controllers/goharbor/exporter" "github.com/goharbor/harbor-operator/controllers/goharbor/harbor" "github.com/goharbor/harbor-operator/controllers/goharbor/harborcluster" + "github.com/goharbor/harbor-operator/controllers/goharbor/harborserverconfiguration" "github.com/goharbor/harbor-operator/controllers/goharbor/jobservice" + "github.com/goharbor/harbor-operator/controllers/goharbor/namespace" "github.com/goharbor/harbor-operator/controllers/goharbor/notaryserver" "github.com/goharbor/harbor-operator/controllers/goharbor/notarysigner" "github.com/goharbor/harbor-operator/controllers/goharbor/portal" + "github.com/goharbor/harbor-operator/controllers/goharbor/pullsecretbinding" "github.com/goharbor/harbor-operator/controllers/goharbor/registry" "github.com/goharbor/harbor-operator/controllers/goharbor/trivy" "github.com/goharbor/harbor-operator/pkg/config" @@ -45,8 +48,11 @@ var controllersBuilder = map[controllers.Controller]func(context.Context, *confi controllers.HarborCluster: harborcluster.New, // old configmap controller is planned to be removed at v1.3, // the controller converts the cm to configuration cr. - controllers.HarborConfigurationCm: configuration.NewWithCm, - controllers.HarborConfiguration: configuration.New, + controllers.HarborConfigurationCm: configuration.NewWithCm, + controllers.HarborConfiguration: configuration.New, + controllers.HarborServerConfiguration: harborserverconfiguration.New, + controllers.PullSecretBinding: pullsecretbinding.New, + controllers.Namespace: namespace.New, } type ControllerFactory func(context.Context, string, string, *configstore.Store) (commonCtrl.Reconciler, error) diff --git a/pkg/setup/setup.go b/pkg/setup/setup.go index 56ed5aa2c..0e0d7ae4d 100644 --- a/pkg/setup/setup.go +++ b/pkg/setup/setup.go @@ -5,6 +5,8 @@ import ( "github.com/goharbor/harbor-operator/pkg/factories/application" "github.com/goharbor/harbor-operator/pkg/factories/logger" + "github.com/goharbor/harbor-operator/webhooks/harborserverconfiguration" + "github.com/goharbor/harbor-operator/webhooks/pod" "github.com/pkg/errors" "golang.org/x/sync/errgroup" kauthn "k8s.io/api/authorization/v1" @@ -12,7 +14,9 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/discovery" + logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/webhook" ) func WithManager(ctx context.Context, mgr manager.Manager) error { @@ -147,5 +151,24 @@ func WebhooksWithManager(ctx context.Context, mgr manager.Manager) error { } } + // setup separate webhooks + setupCustomWebhooks(mgr) + return nil } + +func setupCustomWebhooks(mgr manager.Manager) { + mgr.GetWebhookServer().Register("/mutate-image-path", &webhook.Admission{ + Handler: &pod.ImagePathRewriter{ + Client: mgr.GetClient(), + Log: logf.Log.WithName("webhooks").WithName("MutatingImagePath"), + }, + }) + + mgr.GetWebhookServer().Register("/validate-hsc", &webhook.Admission{ + Handler: &harborserverconfiguration.Validator{ + Client: mgr.GetClient(), + Log: logf.Log.WithName("webhooks").WithName("HarborServerConfigurationValidator"), + }, + }) +} diff --git a/pkg/utils/consts/annotations.go b/pkg/utils/consts/annotations.go new file mode 100644 index 000000000..4da6726ff --- /dev/null +++ b/pkg/utils/consts/annotations.go @@ -0,0 +1,29 @@ +package consts + +const ( + // AnnotationHarborServer is the annnotation for harbor server. + AnnotationHarborServer = "goharbor.io/harbor" + // AnnotationAccount is the annnotation for service account. + AnnotationAccount = "goharbor.io/service-account" + // AnnotationProject is the annnotation for harbor project name. + AnnotationProject = "goharbor.io/project" + // AnnotationRobot is the annotation for robot id. + AnnotationRobot = "goharbor.io/robot" + // AnnotationRobotSecretRef is the annotation for robot secret reference. + AnnotationRobotSecretRef = "goharbor.io/robot-secret" // nolint:gosec + // AnnotationSecOwner is the annotation for owner. + AnnotationSecOwner = "goharbor.io/owner" + // AnnotationImageRewriteRuleConfigMapRef is the annotation for reference to configmap that stores rules. + AnnotationImageRewriteRuleConfigMapRef = "goharbor.io/rewriting-rules" + + // ConfigMapKeyHarborServer is the key in configmap that for HSC. + ConfigMapKeyHarborServer = "hsc" + // ConfigMapKeyRules is the key in configmap that for rules. + ConfigMapKeyRules = "rules" + // ConfigMapKeyRewriting is the key in configmap that for whether turn on image rewrite. + ConfigMapKeyRewriting = "rewriting" + // ConfigMapValueRewritingOff is the key in configmap that for rewrite to turn off. + ConfigMapValueRewritingOff = "off" + // ConfigMapValueRewritingOn is the key in configmap that for rewrite to turn on. + ConfigMapValueRewritingOn = "on" +) diff --git a/pkg/utils/strings/name.go b/pkg/utils/strings/name.go index 33f875bfc..a9f302d73 100644 --- a/pkg/utils/strings/name.go +++ b/pkg/utils/strings/name.go @@ -2,10 +2,19 @@ package strings import ( "fmt" + "math/rand" + "strconv" "strings" + "time" ) -const NormalizationSeparator = "-" +const ( + charset = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + nameLen = 6 + NormalizationSeparator = "-" +) + +var seededRand *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) // nolint:gosec func NormalizeName(name string, suffixes ...string) string { if len(suffixes) > 0 { @@ -14,3 +23,24 @@ func NormalizeName(name string, suffixes ...string) string { return name } + +func stringWithCharset(length int, charset string) string { + b := make([]byte, length) + for i := range b { + b[i] = charset[seededRand.Intn(len(charset))] + } + + return string(b) +} + +// RandomName generates random names. +func RandomName(prefix string) string { + return strings.ToLower(fmt.Sprintf("%s-%s", prefix, stringWithCharset(nameLen, charset))) +} + +// ExtractID extracts ID from location of response. +func ExtractID(location string) (int64, error) { + idstr := location[strings.LastIndex(location, "/")+1:] + + return strconv.ParseInt(idstr, 10, 64) +} diff --git a/pkg/utils/strings/strings.go b/pkg/utils/strings/strings.go new file mode 100644 index 000000000..a7c249f9a --- /dev/null +++ b/pkg/utils/strings/strings.go @@ -0,0 +1,25 @@ +package strings + +// ContainsString is helper functions to check string from a slice of strings. +func ContainsString(slice []string, s string) bool { + for _, item := range slice { + if item == s { + return true + } + } + + return false +} + +// RemoveString is helper functions to remove string from a slice of strings. +func RemoveString(slice []string, s string) (result []string) { + for _, item := range slice { + if item == s { + continue + } + + result = append(result, item) + } + + return +} diff --git a/webhooks/harborserverconfiguration/webhook.go b/webhooks/harborserverconfiguration/webhook.go new file mode 100644 index 000000000..98c4fd434 --- /dev/null +++ b/webhooks/harborserverconfiguration/webhook.go @@ -0,0 +1,70 @@ +package harborserverconfiguration + +import ( + "context" + "fmt" + "net/http" + "strings" + + "github.com/go-logr/logr" + goharborv1 "github.com/goharbor/harbor-operator/apis/goharbor.io/v1beta1" + "github.com/umisama/go-regexpcache" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// +kubebuilder:webhook:path=/validate-hsc,mutating=false,failurePolicy=fail,groups="goharbor.io",resources=harborserverconfigurations,verbs=create;update,sideEffects=None,admissionReviewVersions=v1beta1,versions=v1beta1,name=hsc.goharbor.io + +type Validator struct { + Client client.Client + Log logr.Logger + decoder *admission.Decoder +} + +var ( + _ admission.Handler = (*Validator)(nil) + _ admission.DecoderInjector = (*Validator)(nil) +) + +func (h *Validator) Handle(ctx context.Context, req admission.Request) admission.Response { + hsc := &goharborv1.HarborServerConfiguration{} + + err := h.decoder.Decode(req, hsc) + if err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + + for _, rule := range hsc.Spec.Rules { + registryRegex := rule[:strings.LastIndex(rule, ",")+1] + + if _, err := regexpcache.Compile(registryRegex); err != nil { + return admission.ValidationResponse(false, fmt.Sprintf("%s can not be validated, %q is not a valid regular expression: %s", hsc.Name, registryRegex, err.Error())) + } + } + // Check for duplicate default configurations + if hsc.Spec.Default { + hscList := &goharborv1.HarborServerConfigurationList{} + if err := h.Client.List(ctx, hscList); err != nil { + return admission.Errored(http.StatusInternalServerError, fmt.Errorf("failed to list harbor server configurations: %w", err)) + } + + for _, harborConf := range hscList.Items { + if harborConf.Name != hsc.Name && harborConf.Spec.Default { + return admission.ValidationResponse(false, fmt.Sprintf("%q can not be set as default, %q is the default harbor server configuration", hsc.Name, harborConf.Name)) + } + } + } + + return admission.Allowed("") +} + +func (h *Validator) InjectDecoder(decoder *admission.Decoder) error { + h.decoder = decoder + + return nil +} + +func (h *Validator) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr).For(&goharborv1.HarborServerConfiguration{}).Complete() +} diff --git a/webhooks/pod/containers.go b/webhooks/pod/containers.go new file mode 100644 index 000000000..361910698 --- /dev/null +++ b/webhooks/pod/containers.go @@ -0,0 +1,69 @@ +package pod + +import ( + "fmt" + "strings" + + "github.com/containers/image/v5/docker/reference" + "github.com/goharbor/harbor-operator/pkg/rule" + "github.com/umisama/go-regexpcache" +) + +const BareRegistry = "docker.io" + +// RegistryFromImageRef returns the registry (and port, if set) from the image reference, +// otherwise returns the default bare registry, "docker.io". +func RegistryFromImageRef(imageReference string) (registry string, err error) { + ref, err := reference.ParseDockerRef(imageReference) + if err != nil { + return "", err + } + + return reference.Domain(ref), nil +} + +// ReplaceRegistryInImageRef returns the the image reference with the registry replaced. +func ReplaceRegistryInImageRef(imageReference, replacementRegistry string) (imageRef string, err error) { + named, err := reference.ParseDockerRef(imageReference) + if err != nil { + return "", err + } + + return strings.Replace(named.String(), reference.Domain(named), replacementRegistry, 1), nil +} + +// rewriteContainer replaces any registries matching the image rules with the given serverURL. +func rewriteContainer(imageReference string, rules []rule.Rule) (imageRef string, err error) { + registry, err := RegistryFromImageRef(imageReference) + if err != nil { + return "", err + } + + var starRule *rule.Rule + + for i, r := range rules { + if r.RegistryRegex != "*" { + regex, err := regexpcache.Compile(r.RegistryRegex) + if err != nil { + return "", err + } + + if regex.MatchString(registry) { + rewritten := fmt.Sprintf("%s/%s", r.ServerURL, r.Project) + + return ReplaceRegistryInImageRef(imageReference, rewritten) + } + } else { + starRule = &rules[i] + } + } + + // * has the lowerest priority in the rules, match this in the end. + if starRule != nil { + rewritten := fmt.Sprintf("%s/%s", starRule.ServerURL, starRule.Project) + + return ReplaceRegistryInImageRef(imageReference, rewritten) + } + + return "", nil +} diff --git a/webhooks/pod/containers_test.go b/webhooks/pod/containers_test.go new file mode 100644 index 000000000..9acfa06c1 --- /dev/null +++ b/webhooks/pod/containers_test.go @@ -0,0 +1,158 @@ +package pod_test + +import ( + "testing" + + "github.com/containers/image/v5/docker/reference" + "github.com/goharbor/harbor-operator/webhooks/pod" + "github.com/stretchr/testify/require" +) + +func Test_RegistryFromImageRef(t *testing.T) { + type testcase struct { + description string + imageRef string + expectedRegistry string + } + + tests := []testcase{ + { + description: "image reference with hostname with port and image tag set", + imageRef: "somehost:443/public/busybox:latest", + expectedRegistry: "somehost:443", + }, + { + description: "image reference with hostname with port and no image tag set", + imageRef: "somehost:443/public/busybox", + expectedRegistry: "somehost:443", + }, + { + description: "image reference with hostname with port and image sha set", + imageRef: "somehost:443/public/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa", + expectedRegistry: "somehost:443", + }, + { + description: "image reference with url and image tag set", + imageRef: "example.com/busybox:latest", + expectedRegistry: "example.com", + }, + { + description: "image reference with url and no image tag set", + imageRef: "example.com/busybox", + expectedRegistry: "example.com", + }, + { + description: "image reference with url, project and no image tag set", + imageRef: "example.com/nginxinc/nginx-unprivileged", + expectedRegistry: "example.com", + }, + { + description: "image reference with url and image sha set", + imageRef: "example.com/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa", + expectedRegistry: "example.com", + }, + { + description: "bare image reference with image tag set", + imageRef: "busybox:latest", + expectedRegistry: pod.BareRegistry, + }, + { + description: "bare image reference with project and no image tag set", + imageRef: "nginxinc/nginx-unprivileged", + expectedRegistry: pod.BareRegistry, + }, + { + description: "bare image reference with and no image tag set", + imageRef: "busybox", + expectedRegistry: pod.BareRegistry, + }, + { + description: "bare image reference with image sha set", + imageRef: "busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa", + expectedRegistry: pod.BareRegistry, + }, + } + + for _, testcase := range tests { + output, err := pod.RegistryFromImageRef(testcase.imageRef) + require.NoError(t, err, testcase.description) + require.Equal(t, testcase.expectedRegistry, output, testcase.description) + } +} + +func Test_RegistryFromImageRef_EmptyErr(t *testing.T) { + _, err := pod.RegistryFromImageRef("") + require.EqualError(t, err, reference.ErrReferenceInvalidFormat.Error()) +} + +func Test_ReplaceRegistryInImageRef(t *testing.T) { + type testcase struct { + description string + imageRef string + newRegistry string + expectedRef string + } + + tests := []testcase{ + { + description: "image reference with hostname with port and image tag set", + imageRef: "somehost:443/public/busybox:1.32.0", + newRegistry: "harbor.example.com/proxy-cache", + expectedRef: "harbor.example.com/proxy-cache/public/busybox:1.32.0", + }, + { + description: "image reference with hostname with port and no image tag set", + imageRef: "somehost:443/public/busybox", + newRegistry: "harbor.example.com/proxy-cache", + expectedRef: "harbor.example.com/proxy-cache/public/busybox:latest", + }, + { + description: "image reference with hostname with port and image sha set", + imageRef: "somehost:443/public/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa", + newRegistry: "harbor.example.com/proxy-cache", + expectedRef: "harbor.example.com/proxy-cache/public/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa", + }, + { + description: "image reference with url and image tag set", + imageRef: "example.com/busybox:1.32.0", + newRegistry: "harbor.example.com/proxy-cache", + expectedRef: "harbor.example.com/proxy-cache/busybox:1.32.0", + }, + { + description: "image reference with url and no image tag set", + imageRef: "example.com/busybox", + newRegistry: "harbor.example.com/proxy-cache", + expectedRef: "harbor.example.com/proxy-cache/busybox:latest", + }, + { + description: "image reference with url and image sha set", + imageRef: "example.com/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa", + newRegistry: "harbor.example.com/proxy-cache", + expectedRef: "harbor.example.com/proxy-cache/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa", + }, + { + description: "bare image reference with image tag set", + imageRef: "busybox:1.32.0", + newRegistry: "harbor.example.com/proxy-cache", + expectedRef: "harbor.example.com/proxy-cache/library/busybox:1.32.0", + }, + { + description: "bare image reference with and no image tag set", + imageRef: "busybox", + newRegistry: "harbor.example.com/proxy-cache", + expectedRef: "harbor.example.com/proxy-cache/library/busybox:latest", + }, + { + description: "bare image reference with image sha set", + imageRef: "busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa", + newRegistry: "harbor.example.com/proxy-cache", + expectedRef: "harbor.example.com/proxy-cache/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa", + }, + } + + for _, testcase := range tests { + output, err := pod.ReplaceRegistryInImageRef(testcase.imageRef, testcase.newRegistry) + require.NoError(t, err) + require.Equal(t, testcase.expectedRef, output, testcase.description) + } +} diff --git a/webhooks/pod/mutate_image_path.go b/webhooks/pod/mutate_image_path.go new file mode 100644 index 000000000..f7d980b66 --- /dev/null +++ b/webhooks/pod/mutate_image_path.go @@ -0,0 +1,240 @@ +package pod + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "strings" + + "github.com/go-logr/logr" + goharborv1 "github.com/goharbor/harbor-operator/apis/goharbor.io/v1beta1" + "github.com/goharbor/harbor-operator/pkg/rule" + "github.com/goharbor/harbor-operator/pkg/utils/consts" + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + apierr "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// +kubebuilder:webhook:path=/mutate-image-path,mutating=true,failurePolicy=fail,groups="",resources=pods,verbs=create;update,sideEffects=NoneOnDryRun,admissionReviewVersions=v1beta1,versions=v1,name=mimg.kb.io + +// ImagePathRewriter implements webhook logic to mutate the image path of deploying pods. +type ImagePathRewriter struct { + Client client.Client + Log logr.Logger + decoder *admission.Decoder +} + +// Handle the admission webhook for mutating the image path of deploying pods. +func (ipr *ImagePathRewriter) Handle(ctx context.Context, req admission.Request) admission.Response { // nolint:funlen,gocognit + pod := &corev1.Pod{} + + err := ipr.decoder.Decode(req, pod) + if err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + + // Get namespace of pod + podNS, err := ipr.getPodNamespace(ctx, req.Namespace) + if err != nil { + return admission.Errored(http.StatusInternalServerError, fmt.Errorf("get pod namespace object error: %w", err)) + } + + ipr.Log.Info("receive pod request", "pod", pod.Name, "namespace", podNS.Name) + + // whether to rewrite image path is dependent on rules + // the rules could be in assigned hsc or default hsc + // assigned hsc has higher priority + ipr.Log.Info("try find image rewrite rules that will be applied to this pod") + + var allRules []rule.Rule + + // check if the configmap exist + if cmName, ok := podNS.Annotations[consts.AnnotationImageRewriteRuleConfigMapRef]; ok { // nolint:nestif + cm, err := ipr.getConfigMap(ctx, cmName, podNS.Name) + if err != nil { + // The resource may have been deleted after reconcile request coming in + if apierr.IsNotFound(err) { + return admission.Errored(http.StatusBadRequest, fmt.Errorf("the ConfigMap %s/%s is not found: %w", podNS.Name, cmName, err)) + } + + return admission.Errored(http.StatusInternalServerError, fmt.Errorf("failed to get ConfigMap %s/%s:,%w", podNS.Name, cmName, err)) + } + + // skip if rewriting is off + if enable, yes := cm.Data[consts.ConfigMapKeyRewriting]; yes { + if enable == consts.ConfigMapValueRewritingOff { + return admission.Allowed("no change") + } else if enable != consts.ConfigMapValueRewritingOn { + return admission.Errored(http.StatusBadRequest, errors.Errorf("the rewriting value in configmap %s/%s '%s' is unacceptable", podNS.Name, cmName, enable)) + } + } + + // check the hsc that the configmap is referring to + if hscKey, yes := cm.Data[consts.ConfigMapKeyHarborServer]; yes { + hsc, err := ipr.getHarborServerConfig(ctx, hscKey) + if err != nil { + return admission.Errored(http.StatusInternalServerError, err) + } + + // check selector, error out if assigned HSC doesn't select current namespace + if hsc.Spec.NamespaceSelector != nil { + if match := checkNamespaceSelector(podNS.Labels, hsc.Spec.NamespaceSelector.MatchLabels); !match { + return admission.Errored(http.StatusBadRequest, errors.New("the selector specified in HSC doesn't match the current namespace")) + } + } + + // append rules of configMap to rules of hsc. + allRules = rule.MergeRules(rule.StringToRules(hsc.Spec.Rules, hsc.Spec.ServerURL), + rule.StringToRules(strings.Split(strings.TrimSpace(cm.Data[consts.ConfigMapKeyRules]), "\n"), hsc.Spec.ServerURL)) + } else if _, yes := cm.Data[consts.ConfigMapKeyRules]; yes && strings.TrimSpace(cm.Data[consts.ConfigMapKeyRules]) != "" { + return admission.Errored(http.StatusBadRequest, errors.New("rule are defined in configMap but there is no hsc associated with it")) + } + } + + defaultHSC, err := ipr.lookupDefaultHarborServerConfig(ctx) + if err != nil { + return admission.Errored(http.StatusInternalServerError, fmt.Errorf("get default hsc object error: %w", err)) + } + + if defaultHSC != nil && defaultHSC.Spec.NamespaceSelector != nil { + // check selector, if there is match, add the default rule to it. it has lowerest priority + if match := checkNamespaceSelector(podNS.Labels, defaultHSC.Spec.NamespaceSelector.MatchLabels); match { + allRules = rule.MergeRules(rule.StringToRules(defaultHSC.Spec.Rules, defaultHSC.Spec.ServerURL), allRules) + } else { + // it's ok to not match the default hsc + ipr.Log.Info("default hsc ", defaultHSC.Namespace, "/", defaultHSC.Name, " doesn't match current namespace") + } + } + + // there is no rule that will be applied to the current namespace, skip + if len(allRules) == 0 { + return admission.Allowed("no change") + } + + ipr.Log.Info("try rewrite the image path") + + return ipr.rewriteContainers(req, allRules, pod) +} + +func checkNamespaceSelector(nsLabels, hscLabelSelector map[string]string) bool { + if nsLabels != nil && hscLabelSelector != nil { + for k, v := range nsLabels { + if _, ok := hscLabelSelector[k]; ok && v == hscLabelSelector[k] { + return true + } + } + } + + return false +} + +func (ipr *ImagePathRewriter) getConfigMap(ctx context.Context, name, namespace string) (*corev1.ConfigMap, error) { + cm := &corev1.ConfigMap{} + cmNamespacedName := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + // TODO: replace with no cache client to avoid potential OOM issue + if err := ipr.Client.Get(ctx, cmNamespacedName, cm); err != nil { + return nil, err + } + + return cm, nil +} + +func (ipr *ImagePathRewriter) rewriteContainers(req admission.Request, rules []rule.Rule, pod *corev1.Pod) admission.Response { + for i, c := range pod.Spec.Containers { + rewrittenImage, err := rewriteContainer(c.Image, rules) + if err != nil { + ipr.Log.Error(err, "invalid container image format", "image", c.Image) + + continue + } + + if rewrittenImage != "" { + rewrittenContainer := c.DeepCopy() + rewrittenContainer.Image = rewrittenImage + pod.Spec.Containers[i] = *rewrittenContainer + + ipr.Log.Info("rewrite container image", "original", c.Image, "rewrite", rewrittenImage) + } + } + + for i, c := range pod.Spec.InitContainers { + rewrittenImage, err := rewriteContainer(c.Image, rules) + if err != nil { + ipr.Log.Error(err, "invalid container image format", "image", c.Image) + + continue + } + + if rewrittenImage != "" { + rewrittenContainer := c.DeepCopy() + rewrittenContainer.Image = rewrittenImage + pod.Spec.InitContainers[i] = *rewrittenContainer + + ipr.Log.Info("rewrite init image", "original", c.Image, "rewrite", rewrittenImage) + } + } + + marshaledPod, err := json.Marshal(pod) + if err != nil { + return admission.Errored(http.StatusInternalServerError, err) + } + + return admission.PatchResponseFromRaw(req.Object.Raw, marshaledPod) +} + +func (ipr *ImagePathRewriter) lookupDefaultHarborServerConfig(ctx context.Context) (*goharborv1.HarborServerConfiguration, error) { + hscList := &goharborv1.HarborServerConfigurationList{} + if err := ipr.Client.List(ctx, hscList); err != nil { + return nil, err + } + + for _, hsc := range hscList.Items { + if hsc.Spec.Default { + return &hsc, nil + } + } + + return nil, nil +} + +// A decoder will be automatically injected. +// InjectDecoder injects the decoder. +func (ipr *ImagePathRewriter) InjectDecoder(d *admission.Decoder) error { + ipr.decoder = d + + return nil +} + +func (ipr *ImagePathRewriter) getPodNamespace(ctx context.Context, ns string) (*corev1.Namespace, error) { + namespace := &corev1.Namespace{} + + nsName := types.NamespacedName{ + Namespace: "", + Name: ns, + } + if err := ipr.Client.Get(ctx, nsName, namespace); err != nil { + return nil, fmt.Errorf("get namesapce error: %w", err) + } + + return namespace, nil +} + +func (ipr *ImagePathRewriter) getHarborServerConfig(ctx context.Context, issuer string) (*goharborv1.HarborServerConfiguration, error) { + hsc := &goharborv1.HarborServerConfiguration{} + nsName := types.NamespacedName{ + Name: issuer, + } + + if err := ipr.Client.Get(ctx, nsName, hsc); err != nil { + return nil, err + } + + return hsc, nil +}