diff --git a/apis/apiextensions/v1/composition_types.go b/apis/apiextensions/v1/composition_types.go index 0574a762c..3d2a06cd7 100644 --- a/apis/apiextensions/v1/composition_types.go +++ b/apis/apiextensions/v1/composition_types.go @@ -20,6 +20,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" ) // CompositionSpec specifies desired state of a composition. @@ -42,8 +44,20 @@ type CompositionSpec struct { // WriteConnectionSecretsToNamespace specifies the namespace in which the // connection secrets of composite resource dynamically provisioned using // this composition will be created. + // This field is planned to be replaced in a future release in favor of + // PublishConnectionDetailsWithStoreConfigRef. Currently, both could be + // set independently and connection details would be published to both + // without affecting each other as long as related fields at MR level + // specified. // +optional WriteConnectionSecretsToNamespace *string `json:"writeConnectionSecretsToNamespace,omitempty"` + + // PublishConnectionDetailsWithStoreConfig specifies the secret store config + // with which the connection secrets of composite resource dynamically + // provisioned using this composition will be published. + // +optional + // +kubebuilder:default={"name": "default"} + PublishConnectionDetailsWithStoreConfigRef *xpv1.Reference `json:"publishConnectionDetailsWithStoreConfigRef,omitempty"` } // A PatchSet is a set of patches that can be reused from all resources within diff --git a/apis/apiextensions/v1/xrd_types.go b/apis/apiextensions/v1/xrd_types.go index 73a0c7cff..dcded309b 100644 --- a/apis/apiextensions/v1/xrd_types.go +++ b/apis/apiextensions/v1/xrd_types.go @@ -31,10 +31,12 @@ type CompositeResourceDefinitionSpec struct { // Group specifies the API group of the defined composite resource. // Composite resources are served under `/apis//...`. Must match the // name of the XRD (in the form `.`). + // +immutable Group string `json:"group"` // Names specifies the resource and kind names of the defined composite // resource. + // +immutable Names extv1.CustomResourceDefinitionNames `json:"names"` // ClaimNames specifies the names of an optional composite resource claim. @@ -45,6 +47,7 @@ type CompositeResourceDefinitionSpec struct { // create, update, or delete a corresponding composite resource. You may add // claim names to an existing CompositeResourceDefinition, but they cannot // be changed or removed once they have been set. + // +immutable // +optional ClaimNames *extv1.CustomResourceDefinitionNames `json:"claimNames,omitempty"` @@ -98,6 +101,16 @@ type CompositeResourceDefinitionVersion struct { // Served specifies that this version should be served via REST APIs. Served bool `json:"served"` + // The deprecated field specifies that this version is deprecated and should + // not be used. + // +optional + Deprecated *bool `json:"deprecated,omitempty"` + + // DeprecationWarning specifies the message that should be shown to the user + // when using this version. + // +optional + DeprecationWarning *string `json:"deprecationWarning,omitempty"` + // Schema describes the schema used for validation, pruning, and defaulting // of this version of the defined composite resource. Fields required by all // composite resources will be injected into this schema automatically, and diff --git a/apis/apiextensions/v1/xrd_webhooks.go b/apis/apiextensions/v1/xrd_webhooks.go new file mode 100644 index 000000000..3b3ed8e5d --- /dev/null +++ b/apis/apiextensions/v1/xrd_webhooks.go @@ -0,0 +1,78 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + + "github.com/crossplane/crossplane-runtime/pkg/errors" +) + +const ( + errUnexpectedType = "unexpected type" + + errGroupImmutable = "spec.group is immutable" + errPluralImmutable = "spec.names.plural is immutable" + errKindImmutable = "spec.names.kind is immutable" + errClaimPluralImmutable = "spec.claimNames.plural is immutable" + errClaimKindImmutable = "spec.claimNames.kind is immutable" +) + +// +kubebuilder:webhook:verbs=update,path=/validate-apiextensions-crossplane-io-v1-compositeresourcedefinition,mutating=false,failurePolicy=fail,groups=apiextensions.crossplane.io,resources=compositeresourcedefinitions,versions=v1,name=compositeresourcedefinitions.apiextensions.crossplane.io,sideEffects=None,admissionReviewVersions=v1 + +// ValidateCreate is run for creation actions. +func (in *CompositeResourceDefinition) ValidateCreate() error { + return nil +} + +// ValidateUpdate is run for update actions. +func (in *CompositeResourceDefinition) ValidateUpdate(old runtime.Object) error { + oldObj, ok := old.(*CompositeResourceDefinition) + if !ok { + return errors.New(errUnexpectedType) + } + switch { + case in.Spec.Group != oldObj.Spec.Group: + return errors.New(errGroupImmutable) + case in.Spec.Names.Plural != oldObj.Spec.Names.Plural: + return errors.New(errPluralImmutable) + case in.Spec.Names.Kind != oldObj.Spec.Names.Kind: + return errors.New(errKindImmutable) + } + if in.Spec.ClaimNames != nil && oldObj.Spec.ClaimNames != nil { + switch { + case in.Spec.ClaimNames.Plural != oldObj.Spec.ClaimNames.Plural: + return errors.New(errClaimPluralImmutable) + case in.Spec.ClaimNames.Kind != oldObj.Spec.ClaimNames.Kind: + return errors.New(errClaimKindImmutable) + } + } + return nil +} + +// ValidateDelete is run for delete actions. +func (in *CompositeResourceDefinition) ValidateDelete() error { + return nil +} + +// SetupWebhookWithManager sets up webhook with manager. +func (in *CompositeResourceDefinition) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(in). + Complete() +} diff --git a/apis/apiextensions/v1/xrd_webhooks_test.go b/apis/apiextensions/v1/xrd_webhooks_test.go new file mode 100644 index 000000000..ecde60ae4 --- /dev/null +++ b/apis/apiextensions/v1/xrd_webhooks_test.go @@ -0,0 +1,165 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/runtime" + + "github.com/crossplane/crossplane-runtime/pkg/errors" + "github.com/crossplane/crossplane-runtime/pkg/test" +) + +func TestValidateUpdate(t *testing.T) { + type args struct { + old runtime.Object + new *CompositeResourceDefinition + } + cases := map[string]struct { + args + err error + }{ + "UnexpectedType": { + args: args{ + old: &extv1.CustomResourceDefinition{}, + new: &CompositeResourceDefinition{}, + }, + err: errors.New(errUnexpectedType), + }, + "GroupChanged": { + args: args{ + old: &CompositeResourceDefinition{ + Spec: CompositeResourceDefinitionSpec{ + Group: "a", + }, + }, + new: &CompositeResourceDefinition{ + Spec: CompositeResourceDefinitionSpec{ + Group: "b", + }, + }, + }, + err: errors.New(errGroupImmutable), + }, + "PluralChanged": { + args: args{ + old: &CompositeResourceDefinition{ + Spec: CompositeResourceDefinitionSpec{ + Names: extv1.CustomResourceDefinitionNames{ + Plural: "b", + }, + }, + }, + new: &CompositeResourceDefinition{ + Spec: CompositeResourceDefinitionSpec{ + Names: extv1.CustomResourceDefinitionNames{ + Plural: "a", + }, + }, + }, + }, + err: errors.New(errPluralImmutable), + }, + "KindChanged": { + args: args{ + old: &CompositeResourceDefinition{ + Spec: CompositeResourceDefinitionSpec{ + Names: extv1.CustomResourceDefinitionNames{ + Kind: "b", + }, + }, + }, + new: &CompositeResourceDefinition{ + Spec: CompositeResourceDefinitionSpec{ + Names: extv1.CustomResourceDefinitionNames{ + Kind: "a", + }, + }, + }, + }, + err: errors.New(errKindImmutable), + }, + "ClaimPluralChanged": { + args: args{ + old: &CompositeResourceDefinition{ + Spec: CompositeResourceDefinitionSpec{ + ClaimNames: &extv1.CustomResourceDefinitionNames{ + Plural: "b", + }, + }, + }, + new: &CompositeResourceDefinition{ + Spec: CompositeResourceDefinitionSpec{ + ClaimNames: &extv1.CustomResourceDefinitionNames{ + Plural: "a", + }, + }, + }, + }, + err: errors.New(errClaimPluralImmutable), + }, + "ClaimKindChanged": { + args: args{ + old: &CompositeResourceDefinition{ + Spec: CompositeResourceDefinitionSpec{ + ClaimNames: &extv1.CustomResourceDefinitionNames{ + Kind: "b", + }, + }, + }, + new: &CompositeResourceDefinition{ + Spec: CompositeResourceDefinitionSpec{ + ClaimNames: &extv1.CustomResourceDefinitionNames{ + Kind: "a", + }, + }, + }, + }, + err: errors.New(errClaimKindImmutable), + }, + "Success": { + args: args{ + old: &CompositeResourceDefinition{ + Spec: CompositeResourceDefinitionSpec{ + Names: extv1.CustomResourceDefinitionNames{ + Kind: "a", + }, + }, + }, + new: &CompositeResourceDefinition{ + Spec: CompositeResourceDefinitionSpec{ + Names: extv1.CustomResourceDefinitionNames{ + Kind: "a", + }, + }, + }, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + err := tc.new.ValidateUpdate(tc.old) + if diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != "" { + t.Errorf("ValidateUpdate(): -want, +got:\n%s", diff) + } + }) + } +} diff --git a/apis/apiextensions/v1/zz_generated.deepcopy.go b/apis/apiextensions/v1/zz_generated.deepcopy.go index 2b453b34d..f1b0847c4 100644 --- a/apis/apiextensions/v1/zz_generated.deepcopy.go +++ b/apis/apiextensions/v1/zz_generated.deepcopy.go @@ -246,6 +246,16 @@ func (in *CompositeResourceDefinitionStatus) DeepCopy() *CompositeResourceDefini // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CompositeResourceDefinitionVersion) DeepCopyInto(out *CompositeResourceDefinitionVersion) { *out = *in + if in.Deprecated != nil { + in, out := &in.Deprecated, &out.Deprecated + *out = new(bool) + **out = **in + } + if in.DeprecationWarning != nil { + in, out := &in.DeprecationWarning, &out.DeprecationWarning + *out = new(string) + **out = **in + } if in.Schema != nil { in, out := &in.Schema, &out.Schema *out = new(CompositeResourceValidation) @@ -365,6 +375,11 @@ func (in *CompositionSpec) DeepCopyInto(out *CompositionSpec) { *out = new(string) **out = **in } + if in.PublishConnectionDetailsWithStoreConfigRef != nil { + in, out := &in.PublishConnectionDetailsWithStoreConfigRef, &out.PublishConnectionDetailsWithStoreConfigRef + *out = new(commonv1.Reference) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositionSpec. diff --git a/apis/apiextensions/v1alpha1/revision_types.go b/apis/apiextensions/v1alpha1/revision_types.go index 80a5d4a7c..bbddaf5ca 100644 --- a/apis/apiextensions/v1alpha1/revision_types.go +++ b/apis/apiextensions/v1alpha1/revision_types.go @@ -65,9 +65,21 @@ type CompositionRevisionSpec struct { // WriteConnectionSecretsToNamespace specifies the namespace in which the // connection secrets of composite resource dynamically provisioned using // this composition will be created. + // This field is planned to be removed in a future release in favor of + // PublishConnectionDetailsWithStoreConfigRef. Currently, both could be + // set independently and connection details would be published to both + // without affecting each other as long as related fields at MR level + // specified. // +optional WriteConnectionSecretsToNamespace *string `json:"writeConnectionSecretsToNamespace,omitempty"` + // PublishConnectionDetailsWithStoreConfig specifies the secret store config + // with which the connection secrets of composite resource dynamically + // provisioned using this composition will be published. + // +optional + // +kubebuilder:default={"name": "default"} + PublishConnectionDetailsWithStoreConfigRef *xpv1.Reference `json:"publishConnectionDetailsWithStoreConfigRef,omitempty"` + // Revision number. Newer revisions have larger numbers. // +immutable Revision int64 `json:"revision"` diff --git a/apis/apiextensions/v1alpha1/zz_generated.deepcopy.go b/apis/apiextensions/v1alpha1/zz_generated.deepcopy.go index fb9680973..781fbf9ea 100644 --- a/apis/apiextensions/v1alpha1/zz_generated.deepcopy.go +++ b/apis/apiextensions/v1alpha1/zz_generated.deepcopy.go @@ -22,6 +22,7 @@ limitations under the License. package v1alpha1 import ( + "github.com/crossplane/crossplane-runtime/apis/common/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -187,6 +188,11 @@ func (in *CompositionRevisionSpec) DeepCopyInto(out *CompositionRevisionSpec) { *out = new(string) **out = **in } + if in.PublishConnectionDetailsWithStoreConfigRef != nil { + in, out := &in.PublishConnectionDetailsWithStoreConfigRef, &out.PublishConnectionDetailsWithStoreConfigRef + *out = new(v1.Reference) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositionRevisionSpec. diff --git a/apis/apis.go b/apis/apis.go index 0e82b2a87..a7f723f0b 100644 --- a/apis/apis.go +++ b/apis/apis.go @@ -22,6 +22,7 @@ import ( "github.com/crossplane/crossplane/apis/apiextensions" "github.com/crossplane/crossplane/apis/pkg" + "github.com/crossplane/crossplane/apis/secrets" ) func init() { @@ -29,6 +30,7 @@ func init() { AddToSchemes = append(AddToSchemes, apiextensions.AddToScheme, pkg.AddToScheme, + secrets.AddToScheme, ) } diff --git a/apis/generate.go b/apis/generate.go index 04009a916..34df3e399 100644 --- a/apis/generate.go +++ b/apis/generate.go @@ -1,3 +1,4 @@ +//go:build generate // +build generate /* @@ -19,19 +20,23 @@ limitations under the License. // NOTE(negz): See the below link for details on what is happening here. // https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module -// Remove existing CRDs +// Remove existing manifests //go:generate rm -rf ../cluster/crds +//go:generate rm -rf ../cluster/webhookconfigurations // Generate deepcopy methodsets and CRD manifests -//go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=../hack/boilerplate.go.txt paths=./pkg/v1alpha1;./pkg/v1beta1;./pkg/v1;./apiextensions/... crd:crdVersions=v1 output:artifacts:config=../cluster/crds +//go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=../hack/boilerplate.go.txt paths=./pkg/v1alpha1;./pkg/v1beta1;./pkg/v1;./apiextensions/...;./secrets/... crd:crdVersions=v1 output:artifacts:config=../cluster/crds // NOTE(hasheddan): we generate the meta.pkg.crossplane.io types separately as // the generated CRDs are never installed, only used for API documentation. //go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=../hack/boilerplate.go.txt paths=./pkg/meta/... crd:crdVersions=v1 output:artifacts:config=../docs/api-docs/crds +// Generate webhook manifests +//go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen webhook paths=./pkg/v1alpha1;./pkg/v1beta1;./pkg/v1;./apiextensions/... output:artifacts:config=../cluster/webhookconfigurations + // Generate clientset for types. //go:generate rm -rf ../internal/client -//go:generate go run -tags generate k8s.io/code-generator/cmd/client-gen --clientset-name "versioned" --build-tag="ignore_autogenerated" --go-header-file "../hack/boilerplate.go.txt" --output-package "github.com/crossplane/crossplane/internal/client/clientset" --input-base "github.com/crossplane/crossplane/apis" --output-base "../tmp-clientgen" --input "pkg/v1alpha1,pkg/v1beta1,pkg/v1,apiextensions/v1" +//go:generate go run -tags generate k8s.io/code-generator/cmd/client-gen --clientset-name "versioned" --build-tag="ignore_autogenerated" --go-header-file "../hack/boilerplate.go.txt" --output-package "github.com/crossplane/crossplane/internal/client/clientset" --input-base "github.com/crossplane/crossplane/apis" --output-base "../tmp-clientgen" --input "pkg/v1alpha1,pkg/v1beta1,pkg/v1,apiextensions/v1,secrets/v1alpha1" //go:generate cp -r ../tmp-clientgen/github.com/crossplane/crossplane/internal/client ../internal/client //go:generate rm -rf ../tmp-clientgen diff --git a/apis/pkg/v1/interfaces.go b/apis/pkg/v1/interfaces.go index 5bd8269e9..a94914575 100644 --- a/apis/pkg/v1/interfaces.go +++ b/apis/pkg/v1/interfaces.go @@ -351,6 +351,9 @@ type PackageRevision interface { GetDependencyStatus() (found, installed, invalid int64) SetDependencyStatus(found, installed, invalid int64) + + GetWebhookTLSSecretName() *string + SetWebhookTLSSecretName(n *string) } // GetCondition of this ProviderRevision. @@ -475,6 +478,16 @@ func (p *ProviderRevision) SetSkipDependencyResolution(b *bool) { p.Spec.SkipDependencyResolution = b } +// GetWebhookTLSSecretName of this ProviderRevision. +func (p *ProviderRevision) GetWebhookTLSSecretName() *string { + return p.Spec.WebhookTLSSecretName +} + +// SetWebhookTLSSecretName of this ProviderRevision. +func (p *ProviderRevision) SetWebhookTLSSecretName(b *string) { + p.Spec.WebhookTLSSecretName = b +} + // GetCondition of this ConfigurationRevision. func (p *ConfigurationRevision) GetCondition(ct xpv1.ConditionType) xpv1.Condition { return p.Status.GetCondition(ct) @@ -597,6 +610,16 @@ func (p *ConfigurationRevision) SetSkipDependencyResolution(b *bool) { p.Spec.SkipDependencyResolution = b } +// GetWebhookTLSSecretName of this ConfigurationRevision. +func (p *ConfigurationRevision) GetWebhookTLSSecretName() *string { + return p.Spec.WebhookTLSSecretName +} + +// SetWebhookTLSSecretName of this ConfigurationRevision. +func (p *ConfigurationRevision) SetWebhookTLSSecretName(b *string) { + p.Spec.WebhookTLSSecretName = b +} + var _ PackageRevisionList = &ProviderRevisionList{} var _ PackageRevisionList = &ConfigurationRevisionList{} diff --git a/apis/pkg/v1/revision_types.go b/apis/pkg/v1/revision_types.go index 22ccab5e5..986a5637d 100644 --- a/apis/pkg/v1/revision_types.go +++ b/apis/pkg/v1/revision_types.go @@ -79,6 +79,16 @@ type PackageRevisionSpec struct { // +optional // +kubebuilder:default=false SkipDependencyResolution *bool `json:"skipDependencyResolution,omitempty"` + + // WebhookTLSSecretName is the name of the TLS Secret that will be used + // by the provider to serve a TLS-enabled webhook server. The certificate + // will be injected to webhook configurations as well as CRD conversion + // webhook strategy if needed. + // If it's not given, provider will not have a certificate mounted to its + // filesystem, webhook configurations won't be deployed and if there is a + // CRD with webhook conversion strategy, the installation will fail. + // +optional + WebhookTLSSecretName *string `json:"webhookTLSSecretName,omitempty"` } // PackageRevisionStatus represents the observed state of a PackageRevision. diff --git a/apis/pkg/v1/zz_generated.deepcopy.go b/apis/pkg/v1/zz_generated.deepcopy.go index 25f0187d2..d8da6d414 100644 --- a/apis/pkg/v1/zz_generated.deepcopy.go +++ b/apis/pkg/v1/zz_generated.deepcopy.go @@ -207,6 +207,11 @@ func (in *PackageRevisionSpec) DeepCopyInto(out *PackageRevisionSpec) { *out = new(bool) **out = **in } + if in.WebhookTLSSecretName != nil { + in, out := &in.WebhookTLSSecretName, &out.WebhookTLSSecretName + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageRevisionSpec. diff --git a/apis/secrets/secrets.go b/apis/secrets/secrets.go new file mode 100644 index 000000000..c2089d76d --- /dev/null +++ b/apis/secrets/secrets.go @@ -0,0 +1,39 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package secrets contains Kubernetes API groups for secret store types of Crossplane. +package secrets + +import ( + "k8s.io/apimachinery/pkg/runtime" + + "github.com/crossplane/crossplane/apis/secrets/v1alpha1" +) + +func init() { + // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back + AddToSchemes = append(AddToSchemes, + v1alpha1.AddToScheme, + ) +} + +// AddToSchemes may be used to add all resources defined in the project to a Scheme +var AddToSchemes runtime.SchemeBuilder + +// AddToScheme adds all Resources to the Scheme +func AddToScheme(s *runtime.Scheme) error { + return AddToSchemes.AddToScheme(s) +} diff --git a/apis/secrets/v1alpha1/doc.go b/apis/secrets/v1alpha1/doc.go new file mode 100644 index 000000000..50675aef0 --- /dev/null +++ b/apis/secrets/v1alpha1/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1alpha1 contains the Secret StoreConfig resources. +// +kubebuilder:object:generate=true +// +groupName=secrets.crossplane.io +// +versionName=v1alpha1 +package v1alpha1 diff --git a/apis/secrets/v1alpha1/register.go b/apis/secrets/v1alpha1/register.go new file mode 100644 index 000000000..96259d67f --- /dev/null +++ b/apis/secrets/v1alpha1/register.go @@ -0,0 +1,53 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "reflect" + + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +// Package type metadata. +const ( + Group = "secrets.crossplane.io" + Version = "v1alpha1" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} + + // AddToScheme adds all registered types to scheme + AddToScheme = SchemeBuilder.AddToScheme +) + +// StoreConfig type metadata. +var ( + StoreConfigKind = reflect.TypeOf(StoreConfig{}).Name() + StoreConfigGroupKind = schema.GroupKind{Group: Group, Kind: StoreConfigKind}.String() + StoreConfigKindAPIVersion = StoreConfigKind + "." + SchemeGroupVersion.String() + StoreConfigGroupVersionKind = SchemeGroupVersion.WithKind(StoreConfigKind) +) + +func init() { + SchemeBuilder.Register(&StoreConfig{}, &StoreConfigList{}) +} diff --git a/apis/secrets/v1alpha1/storeconfig_types.go b/apis/secrets/v1alpha1/storeconfig_types.go new file mode 100644 index 000000000..12442760c --- /dev/null +++ b/apis/secrets/v1alpha1/storeconfig_types.go @@ -0,0 +1,56 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" +) + +// A StoreConfigSpec defines the desired state of a StoreConfig. +type StoreConfigSpec struct { + xpv1.SecretStoreConfig `json:",inline"` +} + +// +kubebuilder:object:root=true + +// A StoreConfig configures how Crossplane controllers should store connection details. +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="TYPE",type="string",JSONPath=".spec.type" +// +kubebuilder:printcolumn:name="DEFAULT-SCOPE",type="string",JSONPath=".spec.defaultScope" +// +kubebuilder:resource:scope=Cluster,categories={crossplane,store} +type StoreConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec StoreConfigSpec `json:"spec"` +} + +// +kubebuilder:object:root=true + +// StoreConfigList contains a list of StoreConfig +type StoreConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []StoreConfig `json:"items"` +} + +// GetStoreConfig returns SecretStoreConfig +func (in *StoreConfig) GetStoreConfig() xpv1.SecretStoreConfig { + return in.Spec.SecretStoreConfig +} diff --git a/apis/secrets/v1alpha1/zz_generated.deepcopy.go b/apis/secrets/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 000000000..87d45ab6b --- /dev/null +++ b/apis/secrets/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,100 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StoreConfig) DeepCopyInto(out *StoreConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StoreConfig. +func (in *StoreConfig) DeepCopy() *StoreConfig { + if in == nil { + return nil + } + out := new(StoreConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *StoreConfig) 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 *StoreConfigList) DeepCopyInto(out *StoreConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]StoreConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StoreConfigList. +func (in *StoreConfigList) DeepCopy() *StoreConfigList { + if in == nil { + return nil + } + out := new(StoreConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *StoreConfigList) 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 *StoreConfigSpec) DeepCopyInto(out *StoreConfigSpec) { + *out = *in + in.SecretStoreConfig.DeepCopyInto(&out.SecretStoreConfig) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StoreConfigSpec. +func (in *StoreConfigSpec) DeepCopy() *StoreConfigSpec { + if in == nil { + return nil + } + out := new(StoreConfigSpec) + in.DeepCopyInto(out) + return out +} diff --git a/cluster/charts/crossplane/README.md b/cluster/charts/crossplane/README.md index a3a86389c..80418f842 100644 --- a/cluster/charts/crossplane/README.md +++ b/cluster/charts/crossplane/README.md @@ -78,7 +78,8 @@ and their default values. | `leaderElection` | Enable leader election for Crossplane Managers pod | `true` | | `nodeSelector` | Enable nodeSelector for Crossplane pod | `{}` | | `customLabels` | Custom labels to add into metadata | `{}` | -| `serviceAccount.customAnnotations` | Custom annotations to add to the sercviceaccount of Crossplane | `{}` | +| `customAnnotations` | Custom annotations to add to the Crossplane deployment and pod | `{}` | +| `serviceAccount.customAnnotations` | Custom annotations to add to the serviceaccount of Crossplane | `{}` | | `priorityClassName` | Priority class name for Crossplane and RBAC Manager (if enabled) pods | `""` | | `resourcesCrossplane.limits.cpu` | CPU resource limits for Crossplane | `100m` | | `resourcesCrossplane.limits.memory` | Memory resource limits for Crossplane | `512Mi` | @@ -113,6 +114,7 @@ and their default values. | `metrics.enabled` | Expose Crossplane and RBAC Manager metrics endpoint | `false` | | `extraEnvVarsCrossplane` | List of extra environment variables to set in the crossplane deployment. Any `.` in variable names will be replaced with `_` (example: `SAMPLE.KEY=value1` becomes `SAMPLE_KEY=value1`). | `{}` | | `extraEnvVarsRBACManager` | List of extra environment variables to set in the crossplane rbac manager deployment. Any `.` in variable names will be replaced with `_` (example: `SAMPLE.KEY=value1` becomes `SAMPLE_KEY=value1`). | `{}` | +| `webhooks.enabled` | Enable webhook functionality for Crossplane as well as packages installed by Crossplane. | `false` | ### Command Line diff --git a/cluster/charts/crossplane/templates/clusterrole.yaml b/cluster/charts/crossplane/templates/clusterrole.yaml index 1e9754ffe..a9ee42dc6 100644 --- a/cluster/charts/crossplane/templates/clusterrole.yaml +++ b/cluster/charts/crossplane/templates/clusterrole.yaml @@ -51,11 +51,13 @@ rules: - "" resources: - serviceaccounts + - services verbs: - "*" - apiGroups: - apiextensions.crossplane.io - pkg.crossplane.io + - secrets.crossplane.io resources: - "*" verbs: @@ -87,3 +89,16 @@ rules: - patch - watch - delete +- apiGroups: + - admissionregistration.k8s.io + resources: + - validatingwebhookconfigurations + - mutatingwebhookconfigurations + verbs: + - get + - list + - create + - update + - patch + - watch + - delete diff --git a/cluster/charts/crossplane/templates/deployment.yaml b/cluster/charts/crossplane/templates/deployment.yaml index 163c9e4b5..c6b20a698 100644 --- a/cluster/charts/crossplane/templates/deployment.yaml +++ b/cluster/charts/crossplane/templates/deployment.yaml @@ -6,6 +6,9 @@ metadata: app: {{ template "crossplane.name" . }} release: {{ .Release.Name }} {{- include "crossplane.labels" . | indent 4 }} + {{- with .Values.customAnnotations }} + annotations: {{ toYaml . | nindent 4 }} + {{- end }} spec: replicas: {{ .Values.replicas }} selector: @@ -16,12 +19,17 @@ spec: type: {{ .Values.deploymentStrategy }} template: metadata: - {{- if .Values.metrics.enabled }} + {{- if or .Values.metrics.enabled .Values.customAnnotations }} annotations: + {{- end }} + {{- if .Values.metrics.enabled }} prometheus.io/path: /metrics prometheus.io/port: "8080" prometheus.io/scrape: "true" {{- end }} + {{- with .Values.customAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} labels: app: {{ template "crossplane.name" . }} release: {{ .Release.Name }} @@ -52,6 +60,23 @@ spec: {{- toYaml .Values.resourcesCrossplane | nindent 12 }} securityContext: {{- toYaml .Values.securityContextCrossplane | nindent 12 }} + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- if .Values.webhooks.enabled }} + - name: "WEBHOOK_TLS_SECRET_NAME" + value: webhook-tls-secret + - name: "WEBHOOK_SERVICE_NAME" + value: {{ template "crossplane.name" . }}-webhooks + - name: "WEBHOOK_SERVICE_NAMESPACE" + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: "WEBHOOK_SERVICE_PORT" + value: "9443" + {{- end }} containers: - image: {{ .Values.image.repository }}:{{ .Values.image.tag }} args: @@ -64,11 +89,17 @@ spec: name: {{ .Chart.Name }} resources: {{- toYaml .Values.resourcesCrossplane | nindent 12 }} - {{- if .Values.metrics.enabled }} + {{- if or .Values.metrics.enabled (.Values.webhooks.enabled) }} ports: + {{- end }} + {{- if .Values.metrics.enabled }} - name: metrics containerPort: 8080 {{- end }} + {{- if .Values.webhooks.enabled }} + - name: webhooks + containerPort: 9443 + {{- end }} securityContext: {{- toYaml .Values.securityContextCrossplane | nindent 12 }} env: @@ -76,12 +107,20 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + - name: olala + value: olala - name: LEADER_ELECTION value: "{{ .Values.leaderElection }}" {{- if .Values.registryCaBundleConfig.key }} - name: CA_BUNDLE_PATH value: "/certs/{{ .Values.registryCaBundleConfig.key }}" {{- end}} + {{- if .Values.webhooks.enabled }} + - name: "WEBHOOK_TLS_SECRET_NAME" + value: webhook-tls-secret + - name: "WEBHOOK_TLS_CERT_DIR" + value: /webhook/tls + {{- end }} {{- range $key, $value := .Values.extraEnvVarsCrossplane }} - name: {{ $key | replace "." "_" }} value: {{ $value | quote }} @@ -89,10 +128,14 @@ spec: volumeMounts: - mountPath: /cache name: package-cache - {{- if .Values.registryCaBundleConfig.name }} + {{- if .Values.registryCaBundleConfig.name }} - mountPath: /certs name: ca-certs - {{- end }} + {{- end }} + {{- if .Values.webhooks.enabled }} + - mountPath: /webhook/tls + name: webhook-tls-secret + {{- end }} volumes: - name: package-cache {{- if .Values.packageCache.pvc }} @@ -111,6 +154,18 @@ spec: - key: {{ .Values.registryCaBundleConfig.key }} path: {{ .Values.registryCaBundleConfig.key }} {{- end }} + {{- if .Values.webhooks.enabled }} + - name: webhook-tls-secret + secret: + # NOTE(muvaf): The tls.crt is used both by the server (requires it to + # be a single cert) and the caBundle fields of webhook configs and CRDs + # which can accept a whole bundle of certificates. In order to meet + # the requirements of both, we require a single certificate instead of + # a bundle. + # It's assumed that initializer generates this anyway, so it should be + # fine. + secretName: webhook-tls-secret + {{- end }} {{- if .Values.nodeSelector }} nodeSelector: {{ toYaml .Values.nodeSelector | nindent 8 }} {{- end }} diff --git a/cluster/charts/crossplane/templates/secret.yaml b/cluster/charts/crossplane/templates/secret.yaml new file mode 100644 index 000000000..bb7056955 --- /dev/null +++ b/cluster/charts/crossplane/templates/secret.yaml @@ -0,0 +1,12 @@ +{{- if .Values.webhooks.enabled }} +# The reason this is created empty and filled by the init container is that it's +# mounted by the actual container, so if it wasn't created by Helm, then the +# deployment wouldn't be deployed at all with secret to mount not found error. +# In addition, Helm would delete this secret after uninstallation so the new +# installation of Crossplane would use its own certificate. +apiVersion: v1 +kind: Secret +metadata: + name: webhook-tls-secret +type: Opaque +{{- end }} \ No newline at end of file diff --git a/cluster/charts/crossplane/templates/service.yaml b/cluster/charts/crossplane/templates/service.yaml new file mode 100644 index 000000000..5f5585af9 --- /dev/null +++ b/cluster/charts/crossplane/templates/service.yaml @@ -0,0 +1,18 @@ +{{- if .Values.webhooks.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "crossplane.name" . }}-webhooks + labels: + app: {{ template "crossplane.name" . }} + release: {{ .Release.Name }} + {{- include "crossplane.labels" . | indent 4 }} +spec: + selector: + app: {{ template "crossplane.name" . }} + release: {{ .Release.Name }} + ports: + - protocol: TCP + port: 9443 + targetPort: 9443 +{{- end }} diff --git a/cluster/charts/crossplane/values.yaml.tmpl b/cluster/charts/crossplane/values.yaml.tmpl index d41763a20..871b00415 100755 --- a/cluster/charts/crossplane/values.yaml.tmpl +++ b/cluster/charts/crossplane/values.yaml.tmpl @@ -14,7 +14,10 @@ affinity: {} # -- Custom labels to add into metadata customLabels: {} -# -- Custom Annotations to add to crossplane serviceaccount +# -- Custom annotations to add to the Crossplane deployment and pod +customAnnotations: {} + +# -- Custom annotations to add to the serviceaccount of Crossplane serviceAccount: customAnnotations: {} @@ -32,6 +35,9 @@ imagePullSecrets: registryCaBundleConfig: {} +webhooks: + enabled: false + rbacManager: deploy: true skipAggregatedClusterRoles: false diff --git a/cluster/composition/postgresqlinstance.yaml b/cluster/composition/postgresqlinstance.yaml index 7bee54853..d58a78407 100644 --- a/cluster/composition/postgresqlinstance.yaml +++ b/cluster/composition/postgresqlinstance.yaml @@ -33,9 +33,9 @@ spec: properties: engineVersion: description: 'EngineVersion specifies the desired PostgreSQL engine - version. Allowed Versions: 9.6 and 11.' + version. Allowed Versions: 10 and 11.' enum: - - "9.6" + - "10" - "11" type: string storageGB: diff --git a/cluster/crds/apiextensions.crossplane.io_compositeresourcedefinitions.yaml b/cluster/crds/apiextensions.crossplane.io_compositeresourcedefinitions.yaml index 3da33d162..d193af73c 100644 --- a/cluster/crds/apiextensions.crossplane.io_compositeresourcedefinitions.yaml +++ b/cluster/crds/apiextensions.crossplane.io_compositeresourcedefinitions.yaml @@ -241,6 +241,14 @@ spec: - type type: object type: array + deprecated: + description: The deprecated field specifies that this version + is deprecated and should not be used. + type: boolean + deprecationWarning: + description: DeprecationWarning specifies the message that should + be shown to the user when using this version. + type: string name: description: Name of this version, e.g. “v1”, “v2beta1”, etc. Composite resources are served under this version at `/apis///...` diff --git a/cluster/crds/apiextensions.crossplane.io_compositionrevisions.yaml b/cluster/crds/apiextensions.crossplane.io_compositionrevisions.yaml index 32b2d2480..5b6314d15 100644 --- a/cluster/crds/apiextensions.crossplane.io_compositionrevisions.yaml +++ b/cluster/crds/apiextensions.crossplane.io_compositionrevisions.yaml @@ -245,6 +245,20 @@ spec: - patches type: object type: array + publishConnectionDetailsWithStoreConfigRef: + default: + name: default + description: PublishConnectionDetailsWithStoreConfig specifies the + secret store config with which the connection secrets of composite + resource dynamically provisioned using this composition will be + published. + properties: + name: + description: Name of the referenced object. + type: string + required: + - name + type: object resources: description: Resources is the list of resource templates that will be used when a composite resource referring to this composition @@ -528,7 +542,11 @@ spec: writeConnectionSecretsToNamespace: description: WriteConnectionSecretsToNamespace specifies the namespace in which the connection secrets of composite resource dynamically - provisioned using this composition will be created. + provisioned using this composition will be created. This field is + planned to be removed in a future release in favor of PublishConnectionDetailsWithStoreConfigRef. + Currently, both could be set independently and connection details + would be published to both without affecting each other as long + as related fields at MR level specified. type: string required: - compositeTypeRef diff --git a/cluster/crds/apiextensions.crossplane.io_compositions.yaml b/cluster/crds/apiextensions.crossplane.io_compositions.yaml index a66d381bf..f947d2a00 100644 --- a/cluster/crds/apiextensions.crossplane.io_compositions.yaml +++ b/cluster/crds/apiextensions.crossplane.io_compositions.yaml @@ -269,6 +269,20 @@ spec: - patches type: object type: array + publishConnectionDetailsWithStoreConfigRef: + default: + name: default + description: PublishConnectionDetailsWithStoreConfig specifies the + secret store config with which the connection secrets of composite + resource dynamically provisioned using this composition will be + published. + properties: + name: + description: Name of the referenced object. + type: string + required: + - name + type: object resources: description: Resources is the list of resource templates that will be used when a composite resource referring to this composition @@ -580,7 +594,11 @@ spec: writeConnectionSecretsToNamespace: description: WriteConnectionSecretsToNamespace specifies the namespace in which the connection secrets of composite resource dynamically - provisioned using this composition will be created. + provisioned using this composition will be created. This field is + planned to be replaced in a future release in favor of PublishConnectionDetailsWithStoreConfigRef. + Currently, both could be set independently and connection details + would be published to both without affecting each other as long + as related fields at MR level specified. type: string required: - compositeTypeRef diff --git a/cluster/crds/pkg.crossplane.io_configurationrevisions.yaml b/cluster/crds/pkg.crossplane.io_configurationrevisions.yaml index 44acfa306..20eb55aee 100644 --- a/cluster/crds/pkg.crossplane.io_configurationrevisions.yaml +++ b/cluster/crds/pkg.crossplane.io_configurationrevisions.yaml @@ -115,6 +115,15 @@ spec: whether to skip resolving dependencies for a package. Setting this value to true may have unintended consequences. Default is false. type: boolean + webhookTLSSecretName: + description: WebhookTLSSecretName is the name of the TLS Secret that + will be used by the provider to serve a TLS-enabled webhook server. + The certificate will be injected to webhook configurations as well + as CRD conversion webhook strategy if needed. If it's not given, + provider will not have a certificate mounted to its filesystem, + webhook configurations won't be deployed and if there is a CRD with + webhook conversion strategy, the installation will fail. + type: string required: - desiredState - image diff --git a/cluster/crds/pkg.crossplane.io_providerrevisions.yaml b/cluster/crds/pkg.crossplane.io_providerrevisions.yaml index c0a248521..7e69f08b7 100644 --- a/cluster/crds/pkg.crossplane.io_providerrevisions.yaml +++ b/cluster/crds/pkg.crossplane.io_providerrevisions.yaml @@ -115,6 +115,15 @@ spec: whether to skip resolving dependencies for a package. Setting this value to true may have unintended consequences. Default is false. type: boolean + webhookTLSSecretName: + description: WebhookTLSSecretName is the name of the TLS Secret that + will be used by the provider to serve a TLS-enabled webhook server. + The certificate will be injected to webhook configurations as well + as CRD conversion webhook strategy if needed. If it's not given, + provider will not have a certificate mounted to its filesystem, + webhook configurations won't be deployed and if there is a CRD with + webhook conversion strategy, the installation will fail. + type: string required: - desiredState - image diff --git a/cluster/crds/secrets.crossplane.io_storeconfigs.yaml b/cluster/crds/secrets.crossplane.io_storeconfigs.yaml new file mode 100644 index 000000000..e3254e1c2 --- /dev/null +++ b/cluster/crds/secrets.crossplane.io_storeconfigs.yaml @@ -0,0 +1,278 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: storeconfigs.secrets.crossplane.io +spec: + group: secrets.crossplane.io + names: + categories: + - crossplane + - store + kind: StoreConfig + listKind: StoreConfigList + plural: storeconfigs + singular: storeconfig + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .spec.type + name: TYPE + type: string + - jsonPath: .spec.defaultScope + name: DEFAULT-SCOPE + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: A StoreConfig configures how Crossplane controllers should store + connection details. + 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: A StoreConfigSpec defines the desired state of a StoreConfig. + properties: + defaultScope: + description: DefaultScope used for scoping secrets for "cluster-scoped" + resources. If store type is "Kubernetes", this would mean the default + namespace to store connection secrets for cluster scoped resources. + In case of "Vault", this would be used as the default parent path. + Typically, should be set as Crossplane installation namespace. + type: string + kubernetes: + description: Kubernetes configures a Kubernetes secret store. If the + "type" is "Kubernetes" but no config provided, in cluster config + will be used. + properties: + auth: + description: Credentials used to connect to the Kubernetes API. + properties: + env: + description: Env is a reference to an environment variable + that contains credentials that must be used to connect to + the provider. + properties: + name: + description: Name is the name of an environment variable. + type: string + required: + - name + type: object + fs: + description: Fs is a reference to a filesystem location that + contains credentials that must be used to connect to the + provider. + properties: + path: + description: Path is a filesystem path. + type: string + required: + - path + type: object + secretRef: + description: A SecretRef is a reference to a secret key that + contains the credentials that must be used to connect to + the provider. + properties: + key: + description: The key to select. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - key + - name + - namespace + type: object + source: + description: Source of the credentials. + enum: + - None + - Secret + - Environment + - Filesystem + type: string + required: + - source + type: object + required: + - auth + type: object + type: + default: Kubernetes + description: Type configures which secret store to be used. Only the + configuration block for this store will be used and others will + be ignored if provided. Default is Kubernetes. + type: string + vault: + description: Vault configures a Vault secret store. + properties: + auth: + description: Auth configures an authentication method for Vault. + properties: + method: + description: Method configures which auth method will be used. + type: string + token: + description: Token configures Token Auth for Vault. + properties: + env: + description: Env is a reference to an environment variable + that contains credentials that must be used to connect + to the provider. + properties: + name: + description: Name is the name of an environment variable. + type: string + required: + - name + type: object + fs: + description: Fs is a reference to a filesystem location + that contains credentials that must be used to connect + to the provider. + properties: + path: + description: Path is a filesystem path. + type: string + required: + - path + type: object + secretRef: + description: A SecretRef is a reference to a secret key + that contains the credentials that must be used to connect + to the provider. + properties: + key: + description: The key to select. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - key + - name + - namespace + type: object + source: + description: Source of the credentials. + enum: + - None + - Secret + - Environment + - Filesystem + type: string + required: + - source + type: object + required: + - method + type: object + caBundle: + description: CABundle configures CA bundle for Vault Server. + properties: + env: + description: Env is a reference to an environment variable + that contains credentials that must be used to connect to + the provider. + properties: + name: + description: Name is the name of an environment variable. + type: string + required: + - name + type: object + fs: + description: Fs is a reference to a filesystem location that + contains credentials that must be used to connect to the + provider. + properties: + path: + description: Path is a filesystem path. + type: string + required: + - path + type: object + secretRef: + description: A SecretRef is a reference to a secret key that + contains the credentials that must be used to connect to + the provider. + properties: + key: + description: The key to select. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - key + - name + - namespace + type: object + source: + description: Source of the credentials. + enum: + - None + - Secret + - Environment + - Filesystem + type: string + required: + - source + type: object + mountPath: + description: MountPath is the mount path of the KV secrets engine. + type: string + server: + description: Server is the url of the Vault server, e.g. "https://vault.acme.org" + type: string + version: + default: v2 + description: Version of the KV Secrets engine of Vault. https://www.vaultproject.io/docs/secrets/kv + type: string + required: + - auth + - mountPath + - server + type: object + required: + - defaultScope + type: object + required: + - spec + type: object + served: true + storage: true + subresources: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/cluster/images/crossplane/Dockerfile b/cluster/images/crossplane/Dockerfile index 65c4a6631..7b4ca7b05 100644 --- a/cluster/images/crossplane/Dockerfile +++ b/cluster/images/crossplane/Dockerfile @@ -5,6 +5,7 @@ ARG TARGETARCH ADD bin/$TARGETOS\_$TARGETARCH/crossplane /usr/local/bin/ ADD crds /crds +ADD webhookconfigurations /webhookconfigurations EXPOSE 8080 USER 65532 ENTRYPOINT ["crossplane"] diff --git a/cluster/images/crossplane/Makefile b/cluster/images/crossplane/Makefile index b4f727da0..72f26d2d6 100755 --- a/cluster/images/crossplane/Makefile +++ b/cluster/images/crossplane/Makefile @@ -25,6 +25,7 @@ img.build.shared: @cp Dockerfile $(IMAGE_TEMP_DIR) || $(FAIL) @cp -r $(OUTPUT_DIR)/bin/ $(IMAGE_TEMP_DIR)/bin || $(FAIL) @cp -a ../../../cluster/crds $(IMAGE_TEMP_DIR) || $(FAIL) + @cp -a ../../../cluster/webhookconfigurations $(IMAGE_TEMP_DIR) || $(FAIL) @docker buildx build $(BUILD_ARGS) \ --platform $(IMAGE_PLATFORMS) \ -t $(IMAGE) \ diff --git a/cluster/kustomization.yaml b/cluster/kustomization.yaml index d128b3850..60a5e0b62 100644 --- a/cluster/kustomization.yaml +++ b/cluster/kustomization.yaml @@ -10,3 +10,4 @@ resources: - crds/pkg.crossplane.io_locks.yaml - crds/pkg.crossplane.io_providerrevisions.yaml - crds/pkg.crossplane.io_providers.yaml +- crds/secrets.crossplane.io_storeconfigs.yaml diff --git a/cluster/webhookconfigurations/manifests.yaml b/cluster/webhookconfigurations/manifests.yaml new file mode 100644 index 000000000..6ee06863f --- /dev/null +++ b/cluster/webhookconfigurations/manifests.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-apiextensions-crossplane-io-v1-compositeresourcedefinition + failurePolicy: Fail + name: compositeresourcedefinitions.apiextensions.crossplane.io + rules: + - apiGroups: + - apiextensions.crossplane.io + apiVersions: + - v1 + operations: + - UPDATE + resources: + - compositeresourcedefinitions + sideEffects: None diff --git a/cmd/crank/build.go b/cmd/crank/build.go index a73b6c125..ade8471db 100644 --- a/cmd/crank/build.go +++ b/cmd/crank/build.go @@ -93,7 +93,7 @@ func (c *buildCmd) Run(child *buildChild, logger logging.Logger) error { pkgName = xpkg.FriendlyID(pkgName, hash.Hex) } - f, err := child.fs.Create(xpkg.BuildPath(root, pkgName)) + f, err := child.fs.Create(xpkg.BuildPath(root, pkgName, xpkg.XpkgExtension)) if err != nil { logger.Debug(errCreatePackage, "error", err) return errors.Wrap(err, errCreatePackage) diff --git a/cmd/crossplane/core/core.go b/cmd/crossplane/core/core.go index 24272a591..8f2861a9c 100644 --- a/cmd/crossplane/core/core.go +++ b/cmd/crossplane/core/core.go @@ -32,6 +32,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/logging" "github.com/crossplane/crossplane-runtime/pkg/ratelimiter" + apiextensionsv1 "github.com/crossplane/crossplane/apis/apiextensions/v1" "github.com/crossplane/crossplane/internal/controller/apiextensions" "github.com/crossplane/crossplane/internal/controller/pkg" pkgcontroller "github.com/crossplane/crossplane/internal/controller/pkg/controller" @@ -59,21 +60,24 @@ func (c *Command) Run() error { } type startCommand struct { - Namespace string `short:"n" help:"Namespace used to unpack and run packages." default:"crossplane-system" env:"POD_NAMESPACE"` - CacheDir string `short:"c" help:"Directory used for caching package images." default:"/cache" env:"CACHE_DIR"` - LeaderElection bool `short:"l" help:"Use leader election for the controller manager." default:"false" env:"LEADER_ELECTION"` - Registry string `short:"r" help:"Default registry used to fetch packages when not specified in tag." default:"${default_registry}" env:"REGISTRY"` - CABundlePath string `help:"Additional CA bundle to use when fetching packages from registry." env:"CA_BUNDLE_PATH"` + Namespace string `short:"n" help:"Namespace used to unpack and run packages." default:"crossplane-system" env:"POD_NAMESPACE"` + CacheDir string `short:"c" help:"Directory used for caching package images." default:"/cache" env:"CACHE_DIR"` + LeaderElection bool `short:"l" help:"Use leader election for the controller manager." default:"false" env:"LEADER_ELECTION"` + Registry string `short:"r" help:"Default registry used to fetch packages when not specified in tag." default:"${default_registry}" env:"REGISTRY"` + CABundlePath string `help:"Additional CA bundle to use when fetching packages from registry." env:"CA_BUNDLE_PATH"` + WebhookTLSSecretName string `help:"The name of the TLS Secret that will be used by the webhook servers of core Crossplane and providers." env:"WEBHOOK_TLS_SECRET_NAME"` + WebhookTLSCertDir string `help:"The directory of TLS certificate that will be used by the webhook server of core Crossplane. There should be tls.crt and tls.key files." env:"WEBHOOK_TLS_CERT_DIR"` SyncInterval time.Duration `short:"s" help:"How often all resources will be double-checked for drift from the desired state." default:"1h"` PollInterval time.Duration `help:"How often individual resources will be checked for drift from the desired state." default:"1m"` MaxReconcileRate int `help:"The global maximum rate per second at which resources may checked for drift from the desired state." default:"10"` EnableCompositionRevisions bool `group:"Alpha Features:" help:"Enable support for CompositionRevisions."` + EnableExternalSecretStores bool `group:"Alpha Features:" help:"Enable support for ExternalSecretStores."` } // Run core Crossplane controllers. -func (c *startCommand) Run(s *runtime.Scheme, log logging.Logger) error { +func (c *startCommand) Run(s *runtime.Scheme, log logging.Logger) error { //nolint:gocyclo cfg, err := ctrl.GetConfig() if err != nil { return errors.Wrap(err, "Cannot get config") @@ -100,17 +104,22 @@ func (c *startCommand) Run(s *runtime.Scheme, log logging.Logger) error { return errors.Wrap(err, "Cannot create manager") } + feats := &feature.Flags{} + if c.EnableCompositionRevisions { + feats.Enable(features.EnableAlphaCompositionRevisions) + log.Info("Alpha feature enabled", "flag", features.EnableAlphaCompositionRevisions) + } + if c.EnableExternalSecretStores { + feats.Enable(features.EnableAlphaExternalSecretStores) + log.Info("Alpha feature enabled", "flag", features.EnableAlphaExternalSecretStores) + } + o := controller.Options{ Logger: log, MaxConcurrentReconciles: c.MaxReconcileRate, PollInterval: c.PollInterval, GlobalRateLimiter: ratelimiter.NewGlobal(c.MaxReconcileRate), - Features: &feature.Flags{}, - } - - if c.EnableCompositionRevisions { - o.Features.Enable(features.EnableAlphaCompositionRevisions) - log.Info("Alpha feature enabled", "flag", features.EnableAlphaCompositionRevisions) + Features: feats, } if err := apiextensions.Setup(mgr, o); err != nil { @@ -118,10 +127,12 @@ func (c *startCommand) Run(s *runtime.Scheme, log logging.Logger) error { } po := pkgcontroller.Options{ - Options: o, - Cache: xpkg.NewImageCache(c.CacheDir, afero.NewOsFs()), - Namespace: c.Namespace, - DefaultRegistry: c.Registry, + Options: o, + Cache: xpkg.NewFsPackageCache(c.CacheDir, afero.NewOsFs()), + Namespace: c.Namespace, + DefaultRegistry: c.Registry, + Features: feats, + WebhookTLSSecretName: c.WebhookTLSSecretName, } if c.CABundlePath != "" { @@ -136,5 +147,18 @@ func (c *startCommand) Run(s *runtime.Scheme, log logging.Logger) error { return errors.Wrap(err, "Cannot add packages controllers to manager") } + if c.WebhookTLSCertDir != "" { + ws := mgr.GetWebhookServer() + ws.Port = 9443 + ws.CertDir = c.WebhookTLSCertDir + ws.TLSMinVersion = "1.3" + // TODO(muvaf): Once the implementation of other webhook handlers are + // fleshed out, implement a registration pattern similar to scheme + // registrations. + if err := (&apiextensionsv1.CompositeResourceDefinition{}).SetupWebhookWithManager(mgr); err != nil { + return errors.Wrap(err, "cannot setup webhook for compositeresourcedefinitions") + } + } + return errors.Wrap(mgr.Start(ctrl.SetupSignalHandler()), "Cannot start controller manager") } diff --git a/cmd/crossplane/core/init.go b/cmd/crossplane/core/init.go index 24fc78720..c5dcc32e5 100644 --- a/cmd/crossplane/core/init.go +++ b/cmd/crossplane/core/init.go @@ -19,12 +19,13 @@ package core import ( "context" + admv1 "k8s.io/api/admissionregistration/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/crossplane/crossplane-runtime/pkg/errors" - "github.com/crossplane/crossplane-runtime/pkg/logging" "github.com/crossplane/crossplane/internal/initializer" @@ -34,25 +35,49 @@ import ( type initCommand struct { Providers []string `name:"provider" help:"Pre-install a Provider by giving its image URI. This argument can be repeated."` Configurations []string `name:"configuration" help:"Pre-install a Configuration by giving its image URI. This argument can be repeated."` + Namespace string `short:"n" help:"Namespace used to set as default scope in default secret store config." default:"crossplane-system" env:"POD_NAMESPACE"` + + WebhookTLSSecretName string `help:"The name of the Secret that the initializer will fill with webhook TLS certificate bundle." env:"WEBHOOK_TLS_SECRET_NAME"` + WebhookServiceName string `help:"The name of the Service object that the webhook service will be run." env:"WEBHOOK_SERVICE_NAME"` + WebhookServiceNamespace string `help:"The namespace of the Service object that the webhook service will be run." env:"WEBHOOK_SERVICE_NAMESPACE"` + WebhookServicePort int32 `help:"The port of the Service that the webhook service will be run." env:"WEBHOOK_SERVICE_PORT"` } // Run starts the initialization process. func (c *initCommand) Run(s *runtime.Scheme, log logging.Logger) error { cfg, err := ctrl.GetConfig() if err != nil { - return errors.Wrap(err, "Cannot get config") + return errors.Wrap(err, "cannot get config") } cl, err := client.New(cfg, client.Options{Scheme: s}) if err != nil { return errors.Wrap(err, "cannot create new kubernetes client") } - i := initializer.New(cl, - initializer.NewCoreCRDs("/crds", s), - initializer.NewLockObject(), + var steps []initializer.Step + if c.WebhookTLSSecretName != "" { + nn := types.NamespacedName{ + Name: c.WebhookTLSSecretName, + Namespace: c.Namespace, + } + svc := admv1.ServiceReference{ + Name: c.WebhookServiceName, + Namespace: c.WebhookServiceNamespace, + Port: &c.WebhookServicePort, + } + steps = append(steps, + initializer.NewWebhookCertificateGenerator(nn, c.Namespace, + log.WithValues("Step", "WebhookCertificateGenerator")), + initializer.NewCoreCRDs("/crds", s, initializer.WithWebhookTLSSecretRef(nn)), + initializer.NewWebhookConfigurations("/webhookconfigurations", s, nn, svc)) + } else { + steps = append(steps, initializer.NewCoreCRDs("/crds", s)) + } + + steps = append(steps, initializer.NewLockObject(), initializer.NewPackageInstaller(c.Providers, c.Configurations), - ) - if err := i.Init(context.TODO()); err != nil { + initializer.NewStoreConfigObject(c.Namespace)) + if err := initializer.New(cl, log, steps...).Init(context.TODO()); err != nil { return errors.Wrap(err, "cannot initialize core") } log.Info("Initialization has been completed") diff --git a/cmd/crossplane/main.go b/cmd/crossplane/main.go index e0fdc5731..c6965a251 100644 --- a/cmd/crossplane/main.go +++ b/cmd/crossplane/main.go @@ -18,6 +18,7 @@ package main import ( "github.com/alecthomas/kong" + admv1 "k8s.io/api/admissionregistration/v1" appsv1 "k8s.io/api/apps/v1" coordinationv1 "k8s.io/api/coordination/v1" corev1 "k8s.io/api/core/v1" @@ -85,6 +86,7 @@ func main() { ctx.FatalIfErrorf(coordinationv1.AddToScheme(s), "cannot add coordination v1 Kubernetes API types to scheme") ctx.FatalIfErrorf(extv1.AddToScheme(s), "cannot add apiextensions v1 Kubernetes API types to scheme") ctx.FatalIfErrorf(extv1beta1.AddToScheme(s), "cannot add apiextensions v1beta1 Kubernetes API types to scheme") + ctx.FatalIfErrorf(admv1.AddToScheme(s), "cannot add admissionregistration v1 Kubernetes API types to scheme") ctx.FatalIfErrorf(apis.AddToScheme(s), "cannot add Crossplane API types to scheme") ctx.FatalIfErrorf(ctx.Run(s)) } diff --git a/cmd/crossplane/rbac/init.go b/cmd/crossplane/rbac/init.go index 9863bad87..0c6f74a6a 100644 --- a/cmd/crossplane/rbac/init.go +++ b/cmd/crossplane/rbac/init.go @@ -49,7 +49,7 @@ func (c *initCommand) Run(s *runtime.Scheme, log logging.Logger) error { return errors.Wrap(err, "cannot create new kubernetes client") } // NOTE(muvaf): The plural form of the kind name is not available in Go code. - i := initializer.New(cl, + i := initializer.New(cl, log, initializer.NewCRDWaiter([]string{ fmt.Sprintf("%s.%s", "compositeresourcedefinitions", v1.Group), fmt.Sprintf("%s.%s", "providerrevisions", pkgv1.Group), diff --git a/design/README.md b/design/README.md index 14cb028df..9cd3da7dc 100644 --- a/design/README.md +++ b/design/README.md @@ -3,7 +3,7 @@ This directory contains all documents informing Crossplane's design. Crossplane designs must transition through up to three of the following phases: -1. Proposals. A proposal is simply a Github issue against this reposistory with +1. Proposals. A proposal is simply a Github issue against this repository with the [`proposal` label][proposal-label]. Proposals need not be more than two to three paragraphs. 2. One-pagers. A One-pager is a brief document pitching an idea for further @@ -11,7 +11,7 @@ designs must transition through up to three of the following phases: page long. They provide just enough context to achieve buy-in from Crossplane maintainers. 3. Design documents. A design document is a longer, more detailed design. Design - documents should typically be preceeded by a one-pager and/or a good amount + documents should typically be preceded by a one-pager and/or a good amount of research and experimentation. All designs must start as a proposal. In some simple cases this proposal alone diff --git a/design/defunct/design-doc-agent.md b/design/defunct/design-doc-agent.md index 0e4868517..ed58fc1b7 100644 --- a/design/defunct/design-doc-agent.md +++ b/design/defunct/design-doc-agent.md @@ -277,7 +277,7 @@ agent needs to know the namespace that it should sync to in the central cluster. The easiest configuration would be the one where we specify the `Secret` and a target namespace via installation commands of the agent. Both of these inputs -will act as default and they can be overriden for each `Namespace` +will act as default and they can be overridden for each `Namespace` independently. For example, multiple namespaces can have different requirements with the same name which could cause conficts in the central cluster because all namespaces are rolled up into one namespace. In order to prevent conflicts, the diff --git a/design/defunct/one-pager-packages-security-isolation.md b/design/defunct/one-pager-packages-security-isolation.md index dfacae071..bfb783618 100644 --- a/design/defunct/one-pager-packages-security-isolation.md +++ b/design/defunct/one-pager-packages-security-isolation.md @@ -27,7 +27,7 @@ Instead, it is a centralized control plane intended to manage resources external There is not really a need for Packages being installed into the control plane to request arbitrary permissions. Crossplane can be more opinionated about the types of permissions granted within its control plane and lock things down in a more secure way. -Futhermore, packages are currently scoped to a single type that is used in all scenarios and by all personas. +Furthermore, packages are currently scoped to a single type that is used in all scenarios and by all personas. All new types of functionality, from an end user wanting to install a simple application to an administrator wanting to install support for low level infrastructure types, are all treated the same. They have the same approach to security and access control and the artifacts that they install have the same scope of impact to the control plane. This one size fits all approach is not ideal though; the multiple scenarios and personas need finer grain control around security, access, and isolation. diff --git a/design/defunct/one-pager-template-stacks.md b/design/defunct/one-pager-template-stacks.md index 94ffed294..d03e11dad 100644 --- a/design/defunct/one-pager-template-stacks.md +++ b/design/defunct/one-pager-template-stacks.md @@ -56,7 +56,7 @@ and some set of cluster state. * **Crossplane Managed Cluster** : A Kubernetes cluster being managed by Crossplane, which may or may not have Crossplane installed within that cluster -* **SM (Stack Manager)** : The cluster priviliged deployment that manages +* **SM (Stack Manager)** : The cluster privileged deployment that manages `Stack`, `ClusterStackInstall` and `StackInstall` resources. One per Crossplane cluster. The Stack Manager creates `Deployment` resources as defined by a `Stack` to handle resources of the CRD types that `Stack` diff --git a/design/design-doc-provider-strategy.md b/design/design-doc-provider-strategy.md index 79ba3a143..35735f7ff 100644 --- a/design/design-doc-provider-strategy.md +++ b/design/design-doc-provider-strategy.md @@ -343,7 +343,7 @@ spec: location: West US 2 minimalTlsVersion: TLS12 sslEnforcement: Disabled - version: "9.6" + version: "11" sku: tier: GeneralPurpose capacity: 2 @@ -363,7 +363,7 @@ spec: location: "East US" administratorLogin: "psqladminun" skuName: "GP_Gen5_4" # different than native where 3 fields are used. - version: "9.6" + version: "11" storageMb: 640000 # different than native where this is given under storageProfile publicNetworkAccessEnabled: true # schema same, but not supported in our native provider yet. sslEnforcementEnabled: true # in native, enum Disabled/Enabled instead of boolean @@ -383,7 +383,7 @@ spec: location: West US 2 minimalTlsVersion: TLS12 sslEnforcement: Disabled - version: "9.6" + version: "11" sku: tier: GeneralPurpose capacity: 2 @@ -743,4 +743,4 @@ The costs that we will need to account for by this decision are roughly: * The separation will signal that the Terraform is not just an implementation detail, but in reality, it actually is. * The CRDs and controllers we generate are completely XRM-compliant and work - just like other resources from the end user perspective. \ No newline at end of file + just like other resources from the end user perspective. diff --git a/design/design-doc-rbac-manager.md b/design/design-doc-rbac-manager.md index 9b0114b28..c5df91fb5 100644 --- a/design/design-doc-rbac-manager.md +++ b/design/design-doc-rbac-manager.md @@ -838,7 +838,7 @@ role binding. This alternative approach is appealing in that it leverages * Cluster roles must be manually cleaned up when a namespace is deleted. In all the difference between using namespace-aligned cluster roles and actual -namespaced roles is neglible both in terms of functionality and complexity, so +namespaced roles is negligible both in terms of functionality and complexity, so the decision comes down to which approach is more 'idiomatic'. [Aggregated ClusterRoles]: https://kubernetes.io/docs/reference/access-authn-authz/rbac/#aggregated-clusterroles diff --git a/design/one-pager-composition-revisions.md b/design/one-pager-composition-revisions.md index c41021a86..3bfbcd3e0 100644 --- a/design/one-pager-composition-revisions.md +++ b/design/one-pager-composition-revisions.md @@ -102,7 +102,7 @@ spec: kind: CloudSQLInstance spec: forProvider: - databaseVersion: POSTGRES_9_6 + databaseVersion: POSTGRES_12 region: us-central1 settings: tier: db-custom-1-3840 @@ -147,7 +147,7 @@ spec: kind: CloudSQLInstance spec: forProvider: - databaseVersion: POSTGRES_9_6 + databaseVersion: POSTGRES_12 region: us-central1 settings: tier: db-custom-1-3840 @@ -305,4 +305,4 @@ straightforward to build if there is demand for the functionality. [metacontroller-controller-revisions]: https://metacontroller.github.io/metacontroller/api/controllerrevision.html [kubernetes-controller-revisions]: https://kubernetes.io/docs/tasks/manage-daemon/rollback-daemon-set/#understanding-daemonset-revisions [xr-to-composition]: images/xr-to-composition.svg -[xr-to-revision]: images/xr-to-revision.svg \ No newline at end of file +[xr-to-revision]: images/xr-to-revision.svg diff --git a/design/one-pager-k8s-native-providers.md b/design/one-pager-k8s-native-providers.md index 9d2e291d6..b4ae23641 100644 --- a/design/one-pager-k8s-native-providers.md +++ b/design/one-pager-k8s-native-providers.md @@ -335,7 +335,7 @@ spec: name: postgresql-standard writeConnectionSecretToRef: name: postgresqlconn - engineVersion: "9.6" + engineVersion: "12" ``` On creation of the `PostgreSQLInstance` claim, the claim controller would @@ -559,4 +559,4 @@ func (c *ClusterController) SetupWithManager(mgr ctrl.Manager) error { [crossplane-runtime]: https://github.com/crossplane/crossplane-runtime [crossplane-runtime #22]: https://github.com/crossplane/crossplane-runtime/issues/22 [crossplane-runtime #34]: https://github.com/crossplane/crossplane-runtime/issues/34 -[crossplane #859]: https://github.com/crossplane/crossplane/issues/859 \ No newline at end of file +[crossplane #859]: https://github.com/crossplane/crossplane/issues/859 diff --git a/design/one-pager-managed-resource-api-design.md b/design/one-pager-managed-resource-api-design.md index 06342837d..09717f35c 100644 --- a/design/one-pager-managed-resource-api-design.md +++ b/design/one-pager-managed-resource-api-design.md @@ -39,7 +39,7 @@ * _Status_ The sub-resource of Kubernetes resources that represents the most up-to-date information about the resource. -For other terms, see [glossary]. +For other terms, see [terminology]. ## Background Crossplane extends Kubernetes heavily and the maintainers always try to adopt @@ -503,7 +503,7 @@ what and we know that we'll get a full object body. However; * Pointer type should be used only if the corresponding field is pointer type in the provider's SDK type. -Note that some required fields by all CRUD calls might be late-initalized. For +Note that some required fields by all CRUD calls might be late-initialized. For example, a config parameter can only be fetched from the provider and CRUD operations except `Create` requires it. In those cases, mark the field as optional since at any step we may not have it. @@ -684,7 +684,7 @@ Generic managed reconciler's `ExternalObservation` struct could be extended by adding a field about that sync status and reconciler can mark the sync status in one of the `Condition`s we already have or add a new one. -[package]: https://docs.aws.amazon.com/sdk-for-go/v2/api/service/eks/ -[glossary]: https://github.com/crossplane/crossplane/blob/master/docs/concepts.md#glossary +[package]: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/eks +[terminology]: https://github.com/crossplane/crossplane/blob/master/docs/concepts/terminology.md [from crossplane-runtime]: https://github.com/crossplane/crossplane-runtime/blob/ca4b6b4/apis/core/v1alpha1/resource.go#L77 -[Kubernetes API Conventions - Spec and Status]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status \ No newline at end of file +[Kubernetes API Conventions - Spec and Status]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status diff --git a/design/one-pager-package-format-v2.md b/design/one-pager-package-format-v2.md index 820b4b553..e763e6a0e 100644 --- a/design/one-pager-package-format-v2.md +++ b/design/one-pager-package-format-v2.md @@ -121,7 +121,7 @@ spec: dependsOn: # Required. Specifies an OCI image containing a package dependency. This key # may be either 'provider' or 'configuration'. This is sugar; in either case - # the package manager determines whether the depencency is really a Provider + # the package manager determines whether the dependency is really a Provider # or a Configuration by unpacking it and inspecting its kind. - provider: example/provider-uncommon # Required. Will be extended to support version ranges in future, but @@ -197,7 +197,7 @@ spec: dependsOn: # Required. Specifies an OCI image containing a package dependency. This key # may be either 'provider' or 'configuration'. This is sugar; in either case - # the package manager determines whether the depencency is really a Provider + # the package manager determines whether the dependency is really a Provider # or a Configuration by unpacking it and inspecting its kind. - provider: example/provider-example # Required. Will be extended to support version ranges in future, but @@ -205,7 +205,7 @@ spec: version: v0.1.0 # Required. Specifies an OCI image containing a package dependency. This key # may be either 'provider' or 'configuration'. This is sugar; in either case - # the package manager determines whether the depencency is really a Provider + # the package manager determines whether the dependency is really a Provider # or a Configuration by unpacking it and inspecting its kind. - configuration: example/some-dependency # Required. Will be extended to support version ranges in future, but diff --git a/docs/cloud-providers/aws/aws-provider.md b/docs/cloud-providers/aws/aws-provider.md index 9a784c6c4..e13091eed 100644 --- a/docs/cloud-providers/aws/aws-provider.md +++ b/docs/cloud-providers/aws/aws-provider.md @@ -28,8 +28,8 @@ Run `setup.sh` to read `aws` credentials and region, and create an `aws provider` instance in Crossplane: ```bash -curl -O https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/configure/aws/providerconfig.yaml -curl -O https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/configure/aws/setup.sh +curl -O https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/configure/aws/providerconfig.yaml +curl -O https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/configure/aws/setup.sh chmod +x setup.sh ./setup.sh [--profile aws_profile] ``` diff --git a/docs/cloud-providers/gcp/gcp-provider.md b/docs/cloud-providers/gcp/gcp-provider.md index 530a41279..c5e37cbc8 100644 --- a/docs/cloud-providers/gcp/gcp-provider.md +++ b/docs/cloud-providers/gcp/gcp-provider.md @@ -33,7 +33,7 @@ account will have access to the services and roles sufficient to run the Crossplane GCP examples. ```bash -curl -O https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/configure/gcp/credentials.sh +curl -O https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/configure/gcp/credentials.sh ./credentials.sh # ... EXAMPLE OUTPUT ONLY # export ORGANIZATION_ID=987654321 diff --git a/docs/concepts/composition.md b/docs/concepts/composition.md index bad32d2a7..8ec4dd2dd 100644 --- a/docs/concepts/composition.md +++ b/docs/concepts/composition.md @@ -150,7 +150,7 @@ spec: kind: CloudSQLInstance spec: forProvider: - databaseVersion: POSTGRES_9_6 + databaseVersion: POSTGRES_12 region: us-central1 settings: tier: db-custom-1-3840 diff --git a/docs/concepts/managed-resources.md b/docs/concepts/managed-resources.md index 7e2658d6b..4a99a73d7 100644 --- a/docs/concepts/managed-resources.md +++ b/docs/concepts/managed-resources.md @@ -63,7 +63,7 @@ spec: ``` ```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/provision/aws.yaml +kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/provision/aws.yaml ``` Creating the above instance will cause Crossplane to provision an RDS instance @@ -99,7 +99,7 @@ metadata: name: cloudsqlpostgresql spec: forProvider: - databaseVersion: POSTGRES_9_6 + databaseVersion: POSTGRES_12 region: us-central1 settings: tier: db-custom-1-3840 @@ -111,7 +111,7 @@ spec: ``` ```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/provision/gcp.yaml +kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/provision/gcp.yaml ``` Creating the above instance will cause Crossplane to provision a CloudSQL @@ -177,7 +177,7 @@ spec: ``` ```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/provision/azure.yaml +kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/provision/azure.yaml ``` Creating the above instance will cause Crossplane to provision a PostgreSQL @@ -361,7 +361,7 @@ a string in a specific format that includes other information such as resource group name. In Crossplane, users have 3 fields to refer to another resource. Here is an -example from Azure MySQL managed resource referring to a Azure Resource Group: +example from Azure MySQL managed resource referring to an Azure Resource Group: ```yaml spec: @@ -444,4 +444,4 @@ including Velero. [api-reference]: ../api-docs/overview.md [provider]: providers.md [issue-727]: https://github.com/crossplane/crossplane/issues/727 -[issue-1143]: https://github.com/crossplane/crossplane/issues/1143 \ No newline at end of file +[issue-1143]: https://github.com/crossplane/crossplane/issues/1143 diff --git a/docs/concepts/packages.md b/docs/concepts/packages.md index 3f601db3a..c645cdb87 100644 --- a/docs/concepts/packages.md +++ b/docs/concepts/packages.md @@ -486,7 +486,7 @@ by [pre-pulling images] onto nodes in the cluster. [provider-docs]: https://doc.crds.dev/github.com/crossplane/crossplane/meta.pkg.crossplane.io/Provider/v1 [configuration-docs]: https://doc.crds.dev/github.com/crossplane/crossplane/meta.pkg.crossplane.io/Configuration/v1 [lock-api]: https://doc.crds.dev/github.com/crossplane/crossplane/pkg.crossplane.io/Lock/v1beta1 -[getting-started-with-gcp]: https://github.com/crossplane/crossplane/tree/master/docs/snippets/package/gcp +[getting-started-with-gcp]: https://github.com/crossplane/crossplane/tree/release-1.7/docs/snippets/package/gcp [specification]: https://github.com/Masterminds/semver#basic-comparisons [composition]: composition.md [IAM Roles for Service Accounts]: https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html diff --git a/docs/contributing/adding_external_secret_store_support.md b/docs/contributing/adding_external_secret_store_support.md new file mode 100644 index 000000000..83704977f --- /dev/null +++ b/docs/contributing/adding_external_secret_store_support.md @@ -0,0 +1,139 @@ +--- +title: Adding Secret Store Support +toc: true +weight: 1004 +indent: true +--- + +# Adding External Secret Store Support to an Existing Provider + +To add support for [External Secret Stores] in a provider, we need the following +changes at a high level: + +1. Bump Crossplane Runtime and Crossplane Tools to latest and generate existing +resources to include `PublishConnectionDetails` API. +2. Add a new Type and CRD for Secret StoreConfig. +3. Add feature flag for enabling External Secret Store support. +4. Add Secret Store Connection Details Manager as a `ConnectionPublisher` if +feature enabled. + +In this document, we will go through each step in details. You can check +[this PR as a complete example]. + +> If your provider is a Terrajet based provider, then please check +> [this PR instead]. + +## Steps + +**1. Bump Crossplane Runtime and Crossplane Tools to latest and generate +existing resources to include `PublishConnectionDetails` API.** + +We need a workaround for code generation since latest runtime both adds new API +but also adds a new interface to managed.resourceSpec. Without this workaround, +expect errors similar to below: + + ```shell + 16:40:56 [ .. ] go generate darwin_amd64 + angryjet: error: error loading packages using pattern ./...: /Users/hasanturken/ Workspace/crossplane/provider-gcp/apis/cache/v1beta1/zz_ generated.managedlist.go:27:14: cannot use &l.Items[i] (value of type * CloudMemorystoreInstance) as "github.com/crossplane/crossplane-runtime/pkg/ resource".Managed value in assignment: missing method GetPublishConnectionDetailsTo + exit status 1 + apis/generate.go:30: running "go": exit status 1 + 16:41:04 [FAIL] + make[1]: *** [go.generate] Error 1 + make: *** [generate] Error 2 + ``` + +First, we need to consume a temporary runtime version together with the latest +Crossplane Tools: + + ```shell + go mod edit -replace=github.com/crossplane/crossplane-runtime=github.com/turkenh/crossplane-runtime@v0.0.0-20220314141040-6f74175d3c1f + go get github.com/crossplane/crossplane-tools@master + + go mod tidy + ``` + +Then, remove `trivialVersions=true` in the file `api/generate.go`: + + ```diff +-//go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=../hack/boilerplate.go.txt paths=./... crd:trivialVersions=true,crdVersions=v1 output:artifacts:config=../package/crds ++//go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=../hack/boilerplate.go.txt paths=./... crd:crdVersions=v1 output:artifacts:config=../package/crds + ``` + +Now, we can generate CRDs with `PublishConnectionDetailsTo` API: + + ```shell + make generate + ``` + +Finally, we can revert our workaround by consuming the latest Crossplane +Runtime: + + ```shell + go mod edit -dropreplace=github.com/crossplane/crossplane-runtime + go get github.com/crossplane/crossplane-runtime@master + go mod tidy + make generate + ``` + +**2. Add a new Type and CRD for Secret StoreConfig.** + +See [this commit as an example on how to add the type]. It is expected to be +almost same for all providers except groupName which includes the name short +name of the provider (e.g. `gcp.crossplane.io`) + +Generate the CRD with: + + ```shell + make generate + ``` + +**3. Add feature flag for enabling External Secret Store support.** + +We will add a feature flag to enable the feature which would be off by default. +As part of this step, we will also create a `default` `StoreConfig` during +provider start up, which stores connection secrets into the same Kubernetes +cluster. + +To be consistent across all providers, please define +`--enable-external-secret-stores` as a boolean which is false by default. + +See [this commit as an example for adding the feature flag]. + +**4. Add Secret Store Connection Details Manager as a `ConnectionPublisher` if +feature enabled.** + +Add the following to the Setup function controller. Unfortunately this step +requires some dirty work as we need to this for all types: + + ```diff + func SetupServiceAccountKey(mgr ctrl.Manager, o controller.Options) error { + name := managed.ControllerName(v1alpha1.ServiceAccountKeyGroupKind) + ++ cps := []managed.ConnectionPublisher{managed.NewAPISecretPublisher(mgr.GetClient(), mgr.GetScheme())} ++ if o.Features.Enabled(features.EnableAlphaExternalSecretStores) { ++ cps = append(cps, connection.NewDetailsManager(mgr.GetClient(), scv1alpha1.StoreConfigGroupVersionKind)) ++ } ++ + r := managed.NewReconciler(mgr, + resource.ManagedKind(v1alpha1.ServiceAccountKeyGroupVersionKind), + managed.WithInitializers(), + managed.WithExternalConnecter(&serviceAccountKeyServiceConnector{client: mgr.GetClient()}), + managed.WithPollInterval(o.PollInterval), + managed.WithLogger(o.Logger.WithValues("controller", name)), +- managed.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name)))) ++ managed.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))), ++ managed.WithConnectionPublishers(cps...)) + + return ctrl.NewControllerManagedBy(mgr). + Named(name). + ``` + +You can check [this commit as an example for changes in Setup functions] as an +example. + +[External Secret Stores]: https://github.com/crossplane/crossplane/blob/master/design/design-doc-external-secret-stores.md +[this PR as a complete example]: https://github.com/crossplane/provider-gcp/pull/421 +[this PR instead]: https://github.com/crossplane-contrib/provider-jet-template/pull/23/commits +[this commit as an example on how to add the type]: https://github.com/crossplane/provider-aws/pull/1242/commits/d8a2df323fa2489d82bf1843d2fe338de033c61d +[this commit as an example for adding the feature flag]: https://github.com/crossplane/provider-gcp/pull/421/commits/b5898c62dc6668d9918496de8aa9bc365c371f82 +[this commit as an example for changes in Setup functions]: https://github.com/crossplane/provider-gcp/pull/421/commits/9700d0c4fdb7e1fba8805afa309c1b1c7aa167a6 \ No newline at end of file diff --git a/docs/contributing/provider_development_guide.md b/docs/contributing/provider_development_guide.md index 706b5864b..03a97e167 100644 --- a/docs/contributing/provider_development_guide.md +++ b/docs/contributing/provider_development_guide.md @@ -319,7 +319,7 @@ Crossplane controllers, like those scaffolded by kubebuilder, are built around the [controller-runtime] library. controller-runtime flavoured controllers encapsulate most of their domain-specific logic in a [`reconcile.Reconciler`] implementation. Most Crossplane controllers are one of the three kinds mentioned -under [What Makes a Crossplane Managed Service]. Each of these controller kinds +under [What Makes a Crossplane Infrastructure Resource]. Each of these controller kinds are similar enough across implementations that [crossplane-runtime] provides 'default' reconcilers. These reconcilers encode what the Crossplane community has learned about managing external systems and narrow the problem space from diff --git a/docs/getting-started/create-configuration.md b/docs/getting-started/create-configuration.md index e5b3e1363..e01e09eee 100644 --- a/docs/getting-started/create-configuration.md +++ b/docs/getting-started/create-configuration.md @@ -92,7 +92,7 @@ spec: ``` ```console -curl -OL https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/package/definition.yaml +curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/package/definition.yaml ``` > You might notice that the XRD we created specifies both "names" and "claim @@ -169,7 +169,7 @@ spec: ``` ```console -curl -OL https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/package/aws/composition.yaml +curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/package/aws/composition.yaml ``` @@ -321,7 +321,7 @@ spec: dbInstanceClass: db.t2.small masterUsername: masteruser engine: postgres - engineVersion: "9.6" + engineVersion: "12" skipFinalSnapshotBeforeDeletion: true publiclyAccessible: true writeConnectionSecretToRef: @@ -343,7 +343,7 @@ spec: ``` ```console -curl -OL https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/package/aws-with-vpc/composition.yaml +curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/package/aws-with-vpc/composition.yaml ``` @@ -369,7 +369,7 @@ spec: kind: CloudSQLInstance spec: forProvider: - databaseVersion: POSTGRES_9_6 + databaseVersion: POSTGRES_12 region: us-central1 settings: tier: db-custom-1-3840 @@ -399,7 +399,7 @@ spec: ``` ```console -curl -OL https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/package/gcp/composition.yaml +curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/package/gcp/composition.yaml ``` @@ -485,7 +485,7 @@ spec: ``` ```console -curl -OL https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/package/azure/composition.yaml +curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/package/azure/composition.yaml ``` @@ -535,7 +535,7 @@ spec: ``` ```console -curl -OL https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/package/alibaba/composition.yaml +curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/package/alibaba/composition.yaml ``` @@ -571,14 +571,14 @@ metadata: vpc: default spec: crossplane: - version: ">=v1.2.0-0" + version: ">=v1.4.0-0" dependsOn: - provider: crossplane/provider-aws - version: "v0.18.2" + version: ">=v0.18.2" ``` ```console -curl -OL https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/package/aws/crossplane.yaml +curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/package/aws/crossplane.yaml kubectl crossplane build configuration ``` @@ -591,7 +591,7 @@ you may specify a specific package by using the `-f` flag. ```console # Set this to the Docker Hub username or OCI registry you wish to use. REG=my-package-repo -kubectl crossplane push configuration ${REG}/getting-started-with-aws:master +kubectl crossplane push configuration ${REG}/getting-started-with-aws:v1.7.0 ``` > Note that the Crossplane CLI will not follow symbolic links for files in the @@ -611,14 +611,14 @@ metadata: vpc: new spec: crossplane: - version: ">=v1.2.0-0" + version: ">=v1.4.0-0" dependsOn: - provider: crossplane/provider-aws - version: "v0.18.2" + version: ">=v0.18.2" ``` ```console -curl -OL https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/package/aws-with-vpc/crossplane.yaml +curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/package/aws-with-vpc/crossplane.yaml kubectl crossplane build configuration ``` @@ -631,7 +631,7 @@ you may specify a specific package by using the `-f` flag. ```console # Set this to the Docker Hub username or OCI registry you wish to use. REG=my-package-repo -kubectl crossplane push configuration ${REG}/getting-started-with-aws-with-vpc:master +kubectl crossplane push configuration ${REG}/getting-started-with-aws-with-vpc:v1.7.0 ``` > Note that the Crossplane CLI will not follow symbolic links for files in the @@ -650,14 +650,14 @@ metadata: provider: gcp spec: crossplane: - version: ">=v1.0.0-0" + version: ">=v1.4.0-0" dependsOn: - provider: crossplane/provider-gcp version: ">=v0.13.0" ``` ```console -curl -OL https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/package/gcp/crossplane.yaml +curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/package/gcp/crossplane.yaml kubectl crossplane build configuration ``` @@ -670,7 +670,7 @@ you may specify a specific package by using the `-f` flag. ```console # Set this to the Docker Hub username or OCI registry you wish to use. REG=my-package-repo -kubectl crossplane push configuration ${REG}/getting-started-with-gcp:master +kubectl crossplane push configuration ${REG}/getting-started-with-gcp:v1.7.0 ``` > Note that the Crossplane CLI will not follow symbolic links for files in the @@ -689,14 +689,14 @@ metadata: provider: azure spec: crossplane: - version: ">=v1.0.0-0" + version: ">=v1.4.0-0" dependsOn: - provider: crossplane/provider-azure version: ">=v0.13.0" ``` ```console -curl -OL https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/package/azure/crossplane.yaml +curl -OL https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/package/azure/crossplane.yaml kubectl crossplane build configuration ``` @@ -709,7 +709,7 @@ you may specify a specific package by using the `-f` flag. ```console # Set this to the Docker Hub username or OCI registry you wish to use. REG=my-package-repo -kubectl crossplane push configuration ${REG}/getting-started-with-azure:master +kubectl crossplane push configuration ${REG}/getting-started-with-azure:v1.7.0 ``` > Note that the Crossplane CLI will not follow symbolic links for files in the diff --git a/docs/getting-started/install-configure.md b/docs/getting-started/install-configure.md index bcfac12c2..445332e3b 100644 --- a/docs/getting-started/install-configure.md +++ b/docs/getting-started/install-configure.md @@ -243,7 +243,7 @@ provider that can satisfy a `PostgreSQLInstance`. Let's get started! > section. ```console -kubectl crossplane install configuration registry.upbound.io/xp/getting-started-with-aws:latest +kubectl crossplane install configuration registry.upbound.io/xp/getting-started-with-aws:v1.7.0 ``` Wait until all packages become healthy: @@ -284,7 +284,7 @@ spec: key: creds ``` ```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/configure/aws/providerconfig.yaml +kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/configure/aws/providerconfig.yaml ``` @@ -300,7 +300,7 @@ kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/master/ > section. ```console -kubectl crossplane install configuration registry.upbound.io/xp/getting-started-with-aws-with-vpc:latest +kubectl crossplane install configuration registry.upbound.io/xp/getting-started-with-aws-with-vpc:v1.7.0 ``` Wait until all packages become healthy: @@ -341,7 +341,7 @@ spec: key: creds ``` ```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/configure/aws/providerconfig.yaml +kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/configure/aws/providerconfig.yaml ``` @@ -357,7 +357,7 @@ kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/master/ > section. ```console -kubectl crossplane install configuration registry.upbound.io/xp/getting-started-with-gcp:latest +kubectl crossplane install configuration registry.upbound.io/xp/getting-started-with-gcp:v1.7.0 ``` Wait until all packages become healthy: @@ -430,7 +430,7 @@ spec: > section. ```console -kubectl crossplane install configuration registry.upbound.io/xp/getting-started-with-azure:latest +kubectl crossplane install configuration registry.upbound.io/xp/getting-started-with-azure:v1.7.0 ``` Wait until all packages become healthy: @@ -471,7 +471,7 @@ spec: ``` ```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/configure/azure/providerconfig.yaml +kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/configure/azure/providerconfig.yaml ``` diff --git a/docs/getting-started/provision-infrastructure.md b/docs/getting-started/provision-infrastructure.md index 2aeedf694..ded3893a6 100644 --- a/docs/getting-started/provision-infrastructure.md +++ b/docs/getting-started/provision-infrastructure.md @@ -66,7 +66,7 @@ spec: ``` ```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/compose/claim-aws.yaml +kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/compose/claim-aws.yaml ``` @@ -95,7 +95,7 @@ spec: ``` ```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/compose/claim-aws.yaml +kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/compose/claim-aws.yaml ``` @@ -118,7 +118,7 @@ spec: ``` ```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/compose/claim-gcp.yaml +kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/compose/claim-gcp.yaml ``` @@ -141,7 +141,7 @@ spec: ``` ```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/compose/claim-azure.yaml +kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/compose/claim-azure.yaml ``` @@ -240,7 +240,7 @@ spec: ``` ```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/master/docs/snippets/compose/pod.yaml +kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.7/docs/snippets/compose/pod.yaml ``` This `Pod` simply connects to a PostgreSQL database and prints its name, so you diff --git a/docs/guides/composition-revisions.md b/docs/guides/composition-revisions.md index e68d39da5..0ef31e805 100644 --- a/docs/guides/composition-revisions.md +++ b/docs/guides/composition-revisions.md @@ -71,7 +71,8 @@ When you enable Composition Revisions three things happen: Each time you edit a `Composition` Crossplane will automatically create a `CompositionRevision` that represents that 'revision' of the `Composition` - that unique state. Each revision is allocated an increasing revision number. -This `CompositionRevision` consumers an idea about which revision is 'newest'. +This gives `CompositionRevision` consumers an idea about which revision is +'newest'. Crossplane distinguishes between the 'newest' and the 'current' revision of a `Composition`. That is, if you revert a `Composition` to a previous state that @@ -154,4 +155,4 @@ spec: [composition-type]: ../concepts/composition.md [composition-term]: ../concepts/terminology.md#composition [canary]: https://martinfowler.com/bliki/CanaryRelease.html -[install-guide]: ../getting-started/install-configure.md \ No newline at end of file +[install-guide]: ../getting-started/install-configure.md diff --git a/docs/guides/multi-tenant.md b/docs/guides/multi-tenant.md index 8c2f59a82..19d798b58 100644 --- a/docs/guides/multi-tenant.md +++ b/docs/guides/multi-tenant.md @@ -196,7 +196,7 @@ scope using a `Role`. This allows for giving users the ability to create and manage `MySQLInstances` in their given namespace, but not the ability to see those defined in other namespaces. -Futhermore, because the `metadata.namespace` is a field on the XRC, patching can +Furthermore, because the `metadata.namespace` is a field on the XRC, patching can be utilized to configure managed resources based on the namespace in which the corresponding XRC was defined. This is especially useful if a platform builder wants to designate specific credentials or a set of credentials that users in a diff --git a/docs/guides/self-signed-ca-certs.md b/docs/guides/self-signed-ca-certs.md new file mode 100644 index 000000000..4477fb401 --- /dev/null +++ b/docs/guides/self-signed-ca-certs.md @@ -0,0 +1,56 @@ +--- +title: Self-Signed CA Certs +toc: true +weight: 270 +indent: true +--- + +# Overview of Crossplane for Registry with Self-Signed CA Certificate + +> ! Using self-signed certificates is not advised in production, it is +recommended to only use self-signed certificates for testing. + +When Crossplane loads Configuration and Provider Packages from private +registries, it must be configured to trust the CA and Intermediate certs. + +Crossplane needs to be installed via the Helm chart with the +`registryCaBundleConfig.name` and `registryCaBundleConfig.key` parameters +defined. See [Install Crossplane]. + +## Conifgure + +1. Create a CA Bundle (A file containing your Root and Intermediate +certificates in a specific order). This can be done with any text editor or +from the command line, so long as the resulting file contains all required crt +files in the proper order. In many cases, this will be either a single +self-signed Root CA crt file, or an Intermediate crt and Root crt file. The +order of the crt files should be from lowest to highest in signing order. +For example, if you have a chain of two certificates below your Root +certificate, you place the bottom level Intermediate cert at the beginning of +the file, then the Intermediate cert that singed that cert, then the Root cert +that signed that cert. + +2. Save the files as `[yourdomain].ca-bundle`. + +3. Create a Kubernetes ConfigMap in your Crossplane system namespace: + +``` +kubectl -n [Crossplane system namespace] create cm ca-bundle-config \ +--from-file=ca-bundle=./[yourdomain].ca-bundle +``` + +4. Set the `registryCaBundleConfig.name` Helm chart parameter to +`ca-bundle-config` and the `registryCaBundleConfig.key` parameter to +`ca-bundle`. + +> Providing Helm with parameter values is convered in the Helm docs, +[Helm install](https://helm.sh/docs/helm/helm_install/). An example block +in an `override.yaml` file would look like this: +``` + registryCaBundleConfig: + name: ca-bundle-config + key: ca-bundle +``` + + +[Install Crossplane]: ../reference/install.md \ No newline at end of file diff --git a/docs/guides/vault-as-secret-store.md b/docs/guides/vault-as-secret-store.md new file mode 100644 index 000000000..99fd2cb99 --- /dev/null +++ b/docs/guides/vault-as-secret-store.md @@ -0,0 +1,482 @@ +--- +title: Vault as an External Secret Store +toc: true +weight: 230 +indent: true +--- + +# Using Vault as an External Secret Store + +This guide walks through the steps required to configure Crossplane and +its Providers to use [Vault] as an [External Secret Store]. For the sake of +completeness, we will also include steps for Vault installation and setup, +however, you can skip those and use your existing Vault. + +> External Secret Stores are an alpha feature. They are not yet recommended for +> production use, and are disabled by default. + +Crossplane consumes and also produces sensitive information to operate which +could be categorized as follows: + +1. **Provider credentials:** These are the credentials required for Providers +to authenticate against external APIs. For example, AWS Access/Secret keys, GCP +service account json, etc. +2. **Connection Details:** Once an infrastructure provisioned, we usually +need some connection data to consume it. Most of the time, this +information includes sensitive information like usernames, passwords or access +keys. +3. **Sensitive Inputs to Managed Resources:** There are some Managed resources +which expect input parameters that could be sensitive. Initial password of a +managed database is a good example of this category. + +It is already possible to use Vault for the 1st category (i.e. Provider +Credentials) as described in [the previous guide]. 3rd use case is a relatively +rare and being tracked with [this issue]. + +In this guide we will focus on the 2nd category, which is storing Connection +Details for managed resources in Vault. + +## Steps + +> Some steps in this guide duplicates [the previous guide] on Vault injection. +> However, for convenience, we put them here as well with minor +> changes/improvements. + +At a high level we will run the following steps: + +- Install and Unseal Vault. +- Configure Vault with Kubernetes Auth. +- Install and Configure Crossplane by enabling the feature. +- Install and Configure Provider GCP by enabling the feature. +- Deploy a Composition and CompositeResourceDefinition. +- Create a Claim. +- Verify all secrets land in Vault as expected. + +For simplicity, we will deploy Vault into the same cluster as Crossplane, +however, this is not a requirement as long as Vault has Kubernetes auth enabled +for the cluster where Crossplane is running. + +### Prepare Vault + +1. Install Vault Helm Chart + +```shell +kubectl create ns vault-system + +helm repo add hashicorp https://helm.releases.hashicorp.com --force-update +helm -n vault-system upgrade --install vault hashicorp/vault +``` + +2. [Unseal] Vault + +```shell +kubectl -n vault-system exec vault-0 -- vault operator init -key-shares=1 -key-threshold=1 -format=json > cluster-keys.json +VAULT_UNSEAL_KEY=$(cat cluster-keys.json | jq -r ".unseal_keys_b64[]") +kubectl -n vault-system exec vault-0 -- vault operator unseal $VAULT_UNSEAL_KEY +``` + +3. Configure Vault with Kubernetes Auth. + +In order for Vault to be able to authenticate requests based on Kubernetes +service accounts, the [Kubernetes auth method] must be enabled. +This requires logging in to Vault and configuring it with a service account +token, API server address, and certificate. Because we are running Vault in +Kubernetes, these values are already available via the container filesystem and +environment variables. + +Get Vault Root Token: + +```shell +cat cluster-keys.json | jq -r ".root_token" +``` + +Login as root and enable/configure Kubernetes Auth: + +```shell +kubectl -n vault-system exec -it vault-0 -- /bin/sh + +vault login # use root token from above + +vault auth enable kubernetes +vault write auth/kubernetes/config \ + token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ + kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \ + kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt + +exit # exit vault container +``` + +4. Enable Vault Key Value Secret Engine + +There are two different versions of [Vault KV Secrets Engine], `v1` and `v2`, +which you can find more details in the linked documentation page. +We will use `v2` in this guide as an example, however, both versions are +supported as an external secret store. + +```shell +kubectl -n vault-system exec -it vault-0 -- vault secrets enable -path=secret kv-v2 +``` + +5. Create a Vault Policy and Role for Crossplane + +```shell +kubectl -n vault-system exec -i vault-0 -- vault policy write crossplane - < Prerequisite: You should have a working **default** `ProviderConfig` for +> GCP available. + +1. Create a `Composition` and a `CompositeResourceDefinition`: + +```shell +echo "apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: compositeessinstances.ess.example.org + annotations: + feature: ess +spec: + group: ess.example.org + names: + kind: CompositeESSInstance + plural: compositeessinstances + claimNames: + kind: ESSInstance + plural: essinstances + connectionSecretKeys: + - publicKey + - publicKeyType + versions: + - name: v1alpha1 + served: true + referenceable: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + parameters: + type: object + properties: + serviceAccount: + type: string + required: + - serviceAccount + required: + - parameters" | kubectl apply -f - + +echo "apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: essinstances.ess.example.org + labels: + feature: ess +spec: + publishConnectionDetailsWithStoreConfigRef: + name: vault + compositeTypeRef: + apiVersion: ess.example.org/v1alpha1 + kind: CompositeESSInstance + resources: + - name: serviceaccount + base: + apiVersion: iam.gcp.crossplane.io/v1alpha1 + kind: ServiceAccount + metadata: + name: ess-test-sa + spec: + forProvider: + displayName: a service account to test ess + - name: serviceaccountkey + base: + apiVersion: iam.gcp.crossplane.io/v1alpha1 + kind: ServiceAccountKey + spec: + forProvider: + serviceAccountSelector: + matchControllerRef: true + publishConnectionDetailsTo: + name: ess-mr-conn + metadata: + labels: + environment: development + team: backend + configRef: + name: vault + connectionDetails: + - fromConnectionSecretKey: publicKey + - fromConnectionSecretKey: publicKeyType" | kubectl apply -f - +``` + +2. Create a `Claim`: + +```shell +echo "apiVersion: ess.example.org/v1alpha1 +kind: ESSInstance +metadata: + name: my-ess + namespace: default +spec: + parameters: + serviceAccount: ess-test-sa + compositionSelector: + matchLabels: + feature: ess + publishConnectionDetailsTo: + name: ess-claim-conn + metadata: + labels: + environment: development + team: backend + configRef: + name: vault" | kubectl apply -f - +``` + +3. Verify all resources SYNCED and READY: + +```shell +kubectl get managed +# Example output: +# NAME READY SYNCED DISPLAYNAME EMAIL DISABLED +# serviceaccount.iam.gcp.crossplane.io/my-ess-zvmkz-vhklg True True a service account to test ess my-ess-zvmkz-vhklg@testingforbugbounty.iam.gserviceaccount.com + +# NAME READY SYNCED KEY_ID CREATED_AT EXPIRES_AT +# serviceaccountkey.iam.gcp.crossplane.io/my-ess-zvmkz-bq8pz True True 5cda49b7c32393254b5abb121b4adc07e140502c 2022-03-23T10:54:50Z + +kubectl -n default get claim +# Example output: +# NAME READY CONNECTION-SECRET AGE +# my-ess True 19s + +kubectl get composite +# Example output: +# NAME READY COMPOSITION AGE +# my-ess-zvmkz True essinstances.ess.example.org 32s +``` + +### Verify the Connection Secrets landed to Vault + +```shell +# Check connection secrets in the "default" scope (namespace). +kubectl -n vault-system exec -i vault-0 -- vault kv list /secret/default +# Example output: +# Keys +# ---- +# ess-claim-conn + +# Check connection secrets in the "crossplane-system" scope (namespace). +kubectl -n vault-system exec -i vault-0 -- vault kv list /secret/crossplane-system +# Example output: +# Keys +# ---- +# d2408335-eb88-4146-927b-8025f405da86 +# ess-mr-conn + +# Check contents of claim connection secret +kubectl -n vault-system exec -i vault-0 -- vault kv get /secret/default/ess-claim-conn +# Example output: +# ======= Metadata ======= +# Key Value +# --- ----- +# created_time 2022-03-18T21:24:07.2085726Z +# custom_metadata map[environment:development secret.crossplane.io/owner-uid:881cd9a0-6cc6-418f-8e1d-b36062c1e108 team:backend] +# deletion_time n/a +# destroyed false +# version 1 +# +# ======== Data ======== +# Key Value +# --- ----- +# publicKey -----BEGIN PUBLIC KEY----- +# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzsEYCokmYEsZJCc9QN/8 +# Fm1M/kTPp7Gat/MXLTP3zFyCTBFVNLN79MbAKdinWi6ePXEb75vzB79IdZcWj8lo +# 8trnS64QjNB9Vs4Xk5UvDALwleFN/bZeperxivDPwVPvT9Aqy/U9kohoS/LHyE8w +# uWQb5AuMeVQ1gtCTnCqQZ4d2MSVhQXYVvAWax1spJ9LT7mHub5j95xDdYIcOV3VJ +# l9CIo4VrWIT8THFN2NnjTrGq9+0TzXY0bV674bjJkfBC6v6yXs5HTetG+Uekq/xf +# FCjrrDi1+2UR9Mu2WTuvl8qn50be+mbwdJO5wE32jewxdYrVVmj19+PkaEeAwGTc +# vwIDAQAB +# -----END PUBLIC KEY----- +# publicKeyType TYPE_RAW_PUBLIC_KEY + +# Check contents of managed resource connection secret +kubectl -n vault-system exec -i vault-0 -- vault kv get /secret/crossplane-system/ess-mr-conn +# Example output: +# ======= Metadata ======= +# Key Value +# --- ----- +# created_time 2022-03-18T21:21:07.9298076Z +# custom_metadata map[environment:development secret.crossplane.io/owner-uid:4cd973f8-76fc-45d6-ad45-0b27b5e9252a team:backend] +# deletion_time n/a +# destroyed false +# version 2 +# +# ========= Data ========= +# Key Value +# --- ----- +# privateKey { +# "type": "service_account", +# "project_id": "REDACTED", +# "private_key_id": "REDACTED", +# "private_key": "-----BEGIN PRIVATE KEY-----\nREDACTED\n-----END PRIVATE KEY-----\n", +# "client_email": "ess-test-sa@REDACTED.iam.gserviceaccount.com", +# "client_id": "REDACTED", +# "auth_uri": "https://accounts.google.com/o/oauth2/auth", +# "token_uri": "https://oauth2.googleapis.com/token", +# "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", +# "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/ess-test-sa%40REDACTED.iam.gserviceaccount.com" +# } +# privateKeyType TYPE_GOOGLE_CREDENTIALS_FILE +# publicKey -----BEGIN PUBLIC KEY----- +# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzsEYCokmYEsZJCc9QN/8 +# Fm1M/kTPp7Gat/MXLTP3zFyCTBFVNLN79MbAKdinWi6ePXEb75vzB79IdZcWj8lo +# 8trnS64QjNB9Vs4Xk5UvDALwleFN/bZeperxivDPwVPvT9Aqy/U9kohoS/LHyE8w +# uWQb5AuMeVQ1gtCTnCqQZ4d2MSVhQXYVvAWax1spJ9LT7mHub5j95xDdYIcOV3VJ +# l9CIo4VrWIT8THFN2NnjTrGq9+0TzXY0bV674bjJkfBC6v6yXs5HTetG+Uekq/xf +# FCjrrDi1+2UR9Mu2WTuvl8qn50be+mbwdJO5wE32jewxdYrVVmj19+PkaEeAwGTc +# vwIDAQAB +# -----END PUBLIC KEY----- +# publicKeyType TYPE_RAW_PUBLIC_KEY +``` + +The commands above verifies using the cli, however, you can also connect to the +Vault UI and check secrets there. + +```shell +kubectl -n vault-system port-forward vault-0 8200:8200 +``` + +Now, you can open http://127.0.0.1:8200/ui in browser and login with the root token. + +### Cleanup + +Delete the claim which should clean up all the resources created. + +``` +kubectl -n default delete claim my-ess +``` + + + +[Vault]: https://www.vaultproject.io/ +[External Secret Store]: https://github.com/crossplane/crossplane/blob/master/design/design-doc-external-secret-stores.md +[the previous guide]: vault-injection.md +[this issue]: https://github.com/crossplane/crossplane/issues/2985 +[Kubernetes Auth Method]: https://www.vaultproject.io/docs/auth/kubernetes +[Unseal]: https://www.vaultproject.io/docs/concepts/seal +[Vault KV Secrets Engine]: https://www.vaultproject.io/docs/secrets/kv +[Vault Agent Sidecar Injection]: https://www.vaultproject.io/docs/platform/k8s/injector diff --git a/docs/guides/vault-injection.md b/docs/guides/vault-injection.md index fbc4c6ee3..1c10320c8 100644 --- a/docs/guides/vault-injection.md +++ b/docs/guides/vault-injection.md @@ -292,7 +292,7 @@ metadata: name: postgres-vault-demo spec: forProvider: - databaseVersion: POSTGRES_9_6 + databaseVersion: POSTGRES_12 region: us-central1 settings: tier: db-custom-1-3840 diff --git a/docs/reference/composition.md b/docs/reference/composition.md index 0cb11c930..951570bdf 100644 --- a/docs/reference/composition.md +++ b/docs/reference/composition.md @@ -308,7 +308,7 @@ spec: kind: CloudSQLInstance spec: forProvider: - databaseVersion: POSTGRES_9_6 + databaseVersion: POSTGRES_12 region: us-central1 settings: dataDiskType: PD_SSD diff --git a/docs/reference/install.md b/docs/reference/install.md index 37730689d..947af36af 100644 --- a/docs/reference/install.md +++ b/docs/reference/install.md @@ -85,7 +85,8 @@ and their default values. | `leaderElection` | Enable leader election for Crossplane Managers pod | `true` | | `nodeSelector` | Enable nodeSelector for Crossplane pod | `{}` | | `customLabels` | Custom labels to add into metadata | `{}` | -| `serviceAccount.customAnnotations` | Custom annotations to add to the sercviceaccount of Crossplane | `{}` | +| `customAnnotations` | Custom annotations to add to the Crossplane deployment and pod | `{}` | +| `serviceAccount.customAnnotations` | Custom annotations to add to the serviceaccount of Crossplane | `{}` | | `priorityClassName` | Priority class name for Crossplane and RBAC Manager (if enabled) pods | `""` | | `resourcesCrossplane.limits.cpu` | CPU resource limits for Crossplane | `100m` | | `resourcesCrossplane.limits.memory` | Memory resource limits for Crossplane | `512Mi` | @@ -120,6 +121,7 @@ and their default values. | `metrics.enabled` | Expose Crossplane and RBAC Manager metrics endpoint | `false` | | `extraEnvVarsCrossplane` | List of extra environment variables to set in the crossplane deployment. Any `.` in variable names will be replaced with `_` (example: `SAMPLE.KEY=value1` becomes `SAMPLE_KEY=value1`). | `{}` | | `extraEnvVarsRBACManager` | List of extra environment variables to set in the crossplane rbac manager deployment. Any `.` in variable names will be replaced with `_` (example: `SAMPLE.KEY=value1` becomes `SAMPLE_KEY=value1`). | `{}` | +| `webhooks.enabled` | Enable webhook functionality for Crossplane as well as packages installed by Crossplane. | `false` | ### Command Line diff --git a/docs/reference/troubleshoot.md b/docs/reference/troubleshoot.md index 870403135..54cb11d60 100644 --- a/docs/reference/troubleshoot.md +++ b/docs/reference/troubleshoot.md @@ -117,7 +117,7 @@ kind: Provider metadata: name: provider-aws spec: - package: crossplane/provider-aws:v0.18.1 + package: crossplane/provider-aws:v0.22.0 controllerConfigRef: name: debug-config ``` @@ -163,7 +163,7 @@ kind: Provider metadata: name: provider-aws spec: - package: crossplane/provider-aws:v0.18.1 + package: crossplane/provider-aws:v0.22.0 controllerConfigRef: name: scale-config ``` @@ -258,7 +258,7 @@ the dependency package even it says the dependency is found. This may lead to an incompatible dependency error during the installation. Below is an example where a Configuration package depends on a provider pulled -from `crossplane/provider-aws`. It defines `">v0.16.0-0` as the version +from `crossplane/provider-aws`. It defines `">=v0.18.2` as the version constraint which means all versions after `v0.16.0` including all prerelease versions, in the form of `-xyz` after the normal version string, will be considered when Crossplane tries to find the best match. @@ -272,10 +272,10 @@ metadata: provider: aws spec: crossplane: - version: ">=v1.0.0-0" + version: ">=v1.4.0-0" dependsOn: - provider: crossplane/provider-aws - version: ">v0.16.0-0" + version: ">=v0.18.2" ``` diff --git a/docs/reference/xpkg.md b/docs/reference/xpkg.md index fcf4fe665..3e4b1bd17 100644 --- a/docs/reference/xpkg.md +++ b/docs/reference/xpkg.md @@ -167,6 +167,10 @@ requirements: in the YAML stream. - Zero (0) or more `CustomResourceDefinition.apiextensions.k8s.io` objects MAY be defined in the YAML stream. +- Zero (0) or more `AdmissionWebhookConfiguration.admissionregistration.k8s.io` + objects MAY be defined in the YAML stream. +- Zero (0) or more `MutatingWebhookConfiguration.admissionregistration.k8s.io` + objects MAY be defined in the YAML stream. - Zero (0) other object types may be defined in the YAML stream. ### Object Annotations diff --git a/docs/snippets/package/aws-with-vpc/crossplane.yaml b/docs/snippets/package/aws-with-vpc/crossplane.yaml index f80efe45d..bd0454241 100644 --- a/docs/snippets/package/aws-with-vpc/crossplane.yaml +++ b/docs/snippets/package/aws-with-vpc/crossplane.yaml @@ -8,7 +8,7 @@ metadata: vpc: new spec: crossplane: - version: ">=v1.2.0-0" + version: ">=v1.4.0-0" dependsOn: - provider: crossplane/provider-aws - version: "v0.18.2" + version: ">=v0.18.2" diff --git a/docs/snippets/package/aws/crossplane.yaml b/docs/snippets/package/aws/crossplane.yaml index bc76514b5..049fb29ed 100644 --- a/docs/snippets/package/aws/crossplane.yaml +++ b/docs/snippets/package/aws/crossplane.yaml @@ -8,7 +8,7 @@ metadata: vpc: default spec: crossplane: - version: ">=v1.2.0-0" + version: ">=v1.4.0-0" dependsOn: - provider: crossplane/provider-aws - version: "v0.18.2" + version: ">=v0.18.2" diff --git a/docs/snippets/package/azure/composition.yaml b/docs/snippets/package/azure/composition.yaml index f64aa27ce..a79c10663 100644 --- a/docs/snippets/package/azure/composition.yaml +++ b/docs/snippets/package/azure/composition.yaml @@ -29,7 +29,7 @@ spec: matchControllerRef: true location: West US 2 sslEnforcement: Disabled - version: "9.6" + version: "11" sku: tier: GeneralPurpose capacity: 2 diff --git a/docs/snippets/package/azure/crossplane.yaml b/docs/snippets/package/azure/crossplane.yaml index 570ea7ccd..24a6055da 100644 --- a/docs/snippets/package/azure/crossplane.yaml +++ b/docs/snippets/package/azure/crossplane.yaml @@ -7,7 +7,7 @@ metadata: provider: azure spec: crossplane: - version: ">=v1.0.0-0" + version: ">=v1.4.0-0" dependsOn: - provider: crossplane/provider-azure version: ">=v0.13.0" diff --git a/docs/snippets/package/gcp/composition.yaml b/docs/snippets/package/gcp/composition.yaml index 2c16c2ff0..6271095c3 100644 --- a/docs/snippets/package/gcp/composition.yaml +++ b/docs/snippets/package/gcp/composition.yaml @@ -18,7 +18,7 @@ spec: kind: CloudSQLInstance spec: forProvider: - databaseVersion: POSTGRES_9_6 + databaseVersion: POSTGRES_12 region: us-central1 settings: tier: db-custom-1-3840 diff --git a/docs/snippets/package/gcp/crossplane.yaml b/docs/snippets/package/gcp/crossplane.yaml index 85c46eb8e..1d09de9d0 100644 --- a/docs/snippets/package/gcp/crossplane.yaml +++ b/docs/snippets/package/gcp/crossplane.yaml @@ -7,7 +7,7 @@ metadata: provider: gcp spec: crossplane: - version: ">=v1.0.0-0" + version: ">=v1.4.0-0" dependsOn: - provider: crossplane/provider-gcp version: ">=v0.13.0" diff --git a/docs/snippets/provision/azure.yaml b/docs/snippets/provision/azure.yaml index d2329d44b..106b06c92 100644 --- a/docs/snippets/provision/azure.yaml +++ b/docs/snippets/provision/azure.yaml @@ -16,7 +16,7 @@ spec: name: sqlserverpostgresql-rg location: West US 2 sslEnforcement: Disabled - version: "9.6" + version: "11" sku: tier: GeneralPurpose capacity: 2 diff --git a/docs/snippets/provision/gcp.yaml b/docs/snippets/provision/gcp.yaml index de2fc09e3..84e2d9cba 100644 --- a/docs/snippets/provision/gcp.yaml +++ b/docs/snippets/provision/gcp.yaml @@ -4,7 +4,7 @@ metadata: name: cloudsqlpostgresql spec: forProvider: - databaseVersion: POSTGRES_9_6 + databaseVersion: POSTGRES_12 region: us-central1 settings: tier: db-custom-1-3840 diff --git a/go.mod b/go.mod index 2ffcc9b7c..b356dd9ca 100644 --- a/go.mod +++ b/go.mod @@ -5,33 +5,32 @@ go 1.17 require ( github.com/Masterminds/semver v1.5.0 github.com/alecthomas/kong v0.2.17 - github.com/crossplane/crossplane-runtime v0.15.1-0.20220106140106-428b7c390375 - github.com/google/go-cmp v0.5.6 + github.com/crossplane/crossplane-runtime v0.15.1-0.20220315141414-988c9ba9c255 + github.com/google/go-cmp v0.5.7 // TODO(hasheddan): we prefer to consume release versions of // go-containerregistry. An incremental version is currently being used to // consume the new k8schain implementation, which fixes an issue with // reconcile timeouts when pulling packages in some environments. - github.com/google/go-containerregistry v0.8.1-0.20220120151853-ac864e57b117 - github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20220120151853-ac864e57b117 + github.com/google/go-containerregistry v0.8.1-0.20220302183023-329563766ce8 + github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20220302183023-329563766ce8 github.com/imdario/mergo v0.3.12 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.8.1 github.com/spf13/afero v1.8.0 - golang.org/x/tools v0.1.8 - k8s.io/api v0.23.0 + k8s.io/api v0.23.3 k8s.io/apiextensions-apiserver v0.23.0 - k8s.io/apimachinery v0.23.0 - k8s.io/client-go v0.23.0 + k8s.io/apimachinery v0.23.3 + k8s.io/client-go v0.23.3 k8s.io/code-generator v0.23.0 - k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b + k8s.io/utils v0.0.0-20220127004650-9b3446523e65 sigs.k8s.io/controller-runtime v0.11.0 sigs.k8s.io/controller-tools v0.8.0 sigs.k8s.io/yaml v1.3.0 ) require ( - cloud.google.com/go v0.99.0 // indirect - github.com/Azure/azure-sdk-for-go v61.2.0+incompatible // indirect + cloud.google.com/go/compute v1.1.0 // indirect + github.com/Azure/azure-sdk-for-go v61.4.0+incompatible // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.24 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect @@ -42,24 +41,27 @@ require ( github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/aws/aws-sdk-go-v2 v1.12.0 // indirect - github.com/aws/aws-sdk-go-v2/config v1.12.0 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.7.0 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.9.0 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.3 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.1.0 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.3 // indirect - github.com/aws/aws-sdk-go-v2/service/ecr v1.13.0 // indirect - github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.10.0 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.6.0 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.8.0 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.13.0 // indirect - github.com/aws/smithy-go v1.9.1 // indirect - github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20211215200129-69c85dc22db6 // indirect + github.com/armon/go-metrics v0.3.10 // indirect + github.com/armon/go-radix v1.0.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.13.0 // indirect + github.com/aws/aws-sdk-go-v2/config v1.13.1 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.8.0 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.4 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.2.0 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5 // indirect + github.com/aws/aws-sdk-go-v2/service/ecr v1.14.0 // indirect + github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.11.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.9.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.14.0 // indirect + github.com/aws/smithy-go v1.10.0 // indirect + github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220216180153-3d7835abdf40 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v3 v3.0.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chrismellard/docker-credential-acr-env v0.0.0-20220119192733-fe33c00cee21 // indirect - github.com/containerd/stargz-snapshotter/estargz v0.10.1 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.11.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/docker/cli v20.10.12+incompatible // indirect @@ -70,7 +72,7 @@ require ( github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/go-logr/logr v1.2.0 // indirect + github.com/go-logr/logr v1.2.2 // indirect github.com/go-logr/zapr v1.2.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect @@ -80,54 +82,86 @@ require ( github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20220110151055-a61fd0a8e2bb // indirect - github.com/google/gofuzz v1.1.0 // indirect - github.com/google/uuid v1.2.0 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20220302183023-329563766ce8 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/googleapis/gnostic v0.5.5 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v1.0.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-plugin v1.4.3 // indirect + github.com/hashicorp/go-retryablehttp v0.6.6 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1 // indirect + github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 // indirect + github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/go-uuid v1.0.2 // indirect + github.com/hashicorp/go-version v1.2.1 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/vault/api v1.3.1 // indirect + github.com/hashicorp/vault/sdk v0.3.0 // indirect + github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.13.6 // indirect + github.com/klauspost/compress v1.14.2 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.4.3 // indirect + github.com/mitchellh/reflectwalk v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/oklog/run v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5 // indirect + github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect + github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/prometheus/client_golang v1.11.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.28.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect github.com/spf13/cobra v1.3.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.2.0 // indirect github.com/vbatts/tar-split v0.11.2 // indirect - go.uber.org/atomic v1.7.0 // indirect + go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.1 // indirect - golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce // indirect + golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed // indirect golang.org/x/mod v0.5.1 // indirect - golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect + golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect - golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect + golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect - golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect + golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect + golang.org/x/tools v0.1.9 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350 // indirect + google.golang.org/grpc v1.44.0 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect k8s.io/component-base v0.23.0 // indirect k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c // indirect - k8s.io/klog/v2 v2.30.0 // indirect - k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect - sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.0 // indirect + k8s.io/klog/v2 v2.40.1 // indirect + k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf // indirect + sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect ) diff --git a/go.sum b/go.sum index 4424f13f9..825d69501 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ +4d63.com/gochecknoglobals v0.1.0/go.mod h1:wfdC5ZjKSPr7CybKEcgJhUOgeAQW1+7WcyK8OvUilfo= bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -13,6 +15,7 @@ cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6 cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.60.0/go.mod h1:yw2G51M9IfRboUH61Us8GqCeF1PzPblB823Mn2q2eAU= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= @@ -29,33 +32,43 @@ cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+Y cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= -cloud.google.com/go v0.99.0 h1:y/cM2iqGgGi5D5DQZl6D9STN/3dR/Vx5Mp8s752oJTY= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.1.0 h1:pyPhehLfZ6pVzRgJmXGYvCY4K7WSWRhVw0AwhgVvS84= +cloud.google.com/go/compute v1.1.0/go.mod h1:2NIffxgWfORSI7EOYMFatGTfjMLnqrOKBEyYb6NoRgA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU= cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.5.0/go.mod h1:ZEwJccE3z93Z2HWvstpri00jOg7oO4UZDtKhwDwqF0w= +cloud.google.com/go/spanner v1.7.0/go.mod h1:sd3K2gZ9Fd0vMPLXzeCrF6fq4i63Q7aTLW/lBIfBkIk= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 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= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Antonboom/errname v0.1.5/go.mod h1:DugbBstvPFQbv/5uLcRRzfrNqKE9tVdVCqWCLp6Cifo= +github.com/Antonboom/nilnil v0.1.0/go.mod h1:PhHLvRPSghY5Y7mX4TW+BHZQYo1A8flE5H20D3IPZBo= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v46.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v61.2.0+incompatible h1:sSormXkfW0ov1vh6ihTBRQxdfg73fPqkccl50GbR9iM= -github.com/Azure/azure-sdk-for-go v61.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v61.4.0+incompatible h1:BF2Pm3aQWIa6q9KmxyF1JYKYXtVw67vtvu2Wd54NGuY= +github.com/Azure/azure-sdk-for-go v61.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= @@ -91,10 +104,16 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= 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/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= 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= @@ -117,11 +136,13 @@ github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:m 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= +github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/alecthomas/kong v0.2.17 h1:URDISCI96MIgcIlQyoCAlhOmrSw6pZScBNkctg8r0W0= github.com/alecthomas/kong v0.2.17/go.mod h1:ka3VZ8GZNPXv9Ov+j4YNLkI8mTuhXyr/0ktSlqIydQQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -130,56 +151,69 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF 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/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= +github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= +github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo= github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/ashanbrown/forbidigo v1.2.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI= +github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde/go.mod h1:oG9Dnez7/ESBqc4EdrdNlryeo7d0KcW1ftXHm7nU/UU= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= +github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go-v2 v1.7.1/go.mod h1:L5LuPC1ZgDr2xQS7AmIec/Jlc7O/Y1u2KxJyNVab250= -github.com/aws/aws-sdk-go-v2 v1.12.0 h1:z5bijqy+eXLK/QqF6eQcwCN2qw1k+m9OUDicqCZygu0= -github.com/aws/aws-sdk-go-v2 v1.12.0/go.mod h1:tWhQI5N5SiMawto3uMAQJU5OUN/1ivhDDHq7HTsJvZ0= +github.com/aws/aws-sdk-go-v2 v1.13.0 h1:1XIXAfxsEmbhbj5ry3D3vX+6ZcUYvIqSm4CWWEuGZCA= +github.com/aws/aws-sdk-go-v2 v1.13.0/go.mod h1:L6+ZpqHaLbAaxsqV0L4cvxZY7QupWJB4fhkf8LXvC7w= github.com/aws/aws-sdk-go-v2/config v1.5.0/go.mod h1:RWlPOAW3E3tbtNAqTwvSW54Of/yP3oiZXMI0xfUdjyA= -github.com/aws/aws-sdk-go-v2/config v1.12.0 h1:WOhIzj5HdixjlvQ4SLYAOk6OUUsuu88RwcsTzexa9cg= -github.com/aws/aws-sdk-go-v2/config v1.12.0/go.mod h1:GQONFVSDdG6RRho1C730SGNyDhS1kSTnxpOE76ptBqo= +github.com/aws/aws-sdk-go-v2/config v1.13.1 h1:yLv8bfNoT4r+UvUKQKqRtdnvuWGMK5a82l4ru9Jvnuo= +github.com/aws/aws-sdk-go-v2/config v1.13.1/go.mod h1:Ba5Z4yL/UGbjQUzsiaN378YobhFo0MLfueXGiOsYtEs= github.com/aws/aws-sdk-go-v2/credentials v1.3.1/go.mod h1:r0n73xwsIVagq8RsxmZbGSRQFj9As3je72C2WzUIToc= -github.com/aws/aws-sdk-go-v2/credentials v1.7.0 h1:KFuKwPs7i5SE5a0LxqAxz75qxSjr2HnHnhu0UPGlvpM= -github.com/aws/aws-sdk-go-v2/credentials v1.7.0/go.mod h1:Kmq64kahHJtXfmnEwnvRKeNjLBqkdP++Itln9BmQerE= +github.com/aws/aws-sdk-go-v2/credentials v1.8.0 h1:8Ow0WcyDesGNL0No11jcgb1JAtE+WtubqXjgxau+S0o= +github.com/aws/aws-sdk-go-v2/credentials v1.8.0/go.mod h1:gnMo58Vwx3Mu7hj1wpcG8DI0s57c9o42UQ6wgTQT5to= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.3.0/go.mod h1:2LAuqPx1I6jNfaGDucWfA2zqQCYCOMCDHiCOciALyNw= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.9.0 h1:fPq3oloONbHaA0O8KX/KYUQk7pG9JjKBwYQvQsQDK84= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.9.0/go.mod h1:19SxQ+9zANyJCnNaoF3ovl8bFil4TaqCYEDdqNGKM+A= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.3 h1:YPNiEXnuWdkpNOwBFHhcLwkSmewwQRcPFO9dHmxU0qg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.3/go.mod h1:L72JSFj9OwHwyukeuKFFyTj6uFWE4AjB0IQp97bd9Lc= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.1.0 h1:ArRd27pSm66f7cCBDPS77wvxiS4IRjFatpzVBD7Aojc= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.1.0/go.mod h1:KdVvdk4gb7iatuHZgIkIqvJlWHBtjCJLUtD/uO/FkWw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0 h1:NITDuUZO34mqtOwFWZiXo7yAHj7kf+XPE+EiKuCBNUI= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0/go.mod h1:I6/fHT/fH460v09eg2gVrd8B/IqskhNdpcLH0WNO3QI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.4 h1:CRiQJ4E2RhfDdqbie1ZYDo8QtIo75Mk7oTdJSfwJTMQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.4/go.mod h1:XHgQ7Hz2WY2GAn//UXHofLfPXWh+s62MbMOijrg12Lw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.2.0 h1:3ADoioDMOtF4uiK59vCpplpCwugEU+v4ZFD29jDL3RQ= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.2.0/go.mod h1:BsCSJHx5DnDXIrOcqB8KN1/B+hXLG/bi4Y6Vjcx/x9E= github.com/aws/aws-sdk-go-v2/internal/ini v1.1.1/go.mod h1:Zy8smImhTdOETZqfyn01iNOe0CNggVbPjCajyaz6Gvg= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.3 h1:fmGqMNlFTHr9Y48qmYYv2qIo+TAsST3qZa2d1HcwBeo= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.3/go.mod h1:N4dv+zawriMFZBO/6UKg3zt+XO6xWOQo1neAA0lFbo4= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5 h1:ixotxbfTCFpqbuwFv/RcZwyzhkxPSYDYEMcj4niB5Uk= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5/go.mod h1:R3sWUqPcfXSiF/LSFJhjyJmpg9uV6yP2yv3YZZjldVI= github.com/aws/aws-sdk-go-v2/service/ecr v1.4.1/go.mod h1:FglZcyeiBqcbvyinl+n14aT/EWC7S1MIH+Gan2iizt0= -github.com/aws/aws-sdk-go-v2/service/ecr v1.13.0 h1:2XQbjtiOokE9jq2CSQv2wMBMYUEZ/UXwQ0jkLsTW0lk= -github.com/aws/aws-sdk-go-v2/service/ecr v1.13.0/go.mod h1:X9rkClmo0/dXh2fwvhkMoXR5zxirrzCqMgfU+Z0HIgs= +github.com/aws/aws-sdk-go-v2/service/ecr v1.14.0 h1:AAZJJAENsQ4yYbnfvqPZT8Nc1YlEd5CZ4usymlC2b4U= +github.com/aws/aws-sdk-go-v2/service/ecr v1.14.0/go.mod h1:a3WUi3JjM3MFtIYenSYPJ7UZPXsw7U7vzebnynxucks= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.4.1/go.mod h1:eD5Eo4drVP2FLTw0G+SMIPWNWvQRGGTtIZR2XeAagoA= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.10.0 h1:d+uTsVjpo8MNNjr+7+1axbFNr0BiLoITecb2cQwofJI= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.10.0/go.mod h1:wlxlU/f1AOpsYIxt86LyrztTAIhyp/6HRNHcZjLzHjg= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.11.0 h1:axOp0EcAVRrF7F8d9gqApgT/9RjN34aR+cuW5LTgROo= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.11.0/go.mod h1:iurUYk+aG+E2DO0MnQEZKh9zsaIxuTcrvCaAUTUhuTU= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.1/go.mod h1:zceowr5Z1Nh2WVP8bf/3ikB41IZW59E4yIYbg+pC6mw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.6.0 h1:rwE0kWa5qm0yEoNPwC3zhrt1tFVXTmkWRlUxLayAwyc= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.6.0/go.mod h1:wTgFkG6t7jS/6Y0SILXwfspV3IXowb6ngsAlSajW0Kc= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0 h1:4QAOB3KrvI1ApJK14sliGr3Ie2pjyvNypn/lfzDHfUw= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0/go.mod h1:K/qPe6AP2TGYv4l6n7c88zh9jWBDf6nHhvg1fx/EWfU= github.com/aws/aws-sdk-go-v2/service/sso v1.3.1/go.mod h1:J3A3RGUvuCZjvSuZEcOpHDnzZP/sKbhDWV2T1EOzFIM= -github.com/aws/aws-sdk-go-v2/service/sso v1.8.0 h1:X77LUt6Djy3Z02r6tW7Z+4FNr6GCnEG54EXfskc19M4= -github.com/aws/aws-sdk-go-v2/service/sso v1.8.0/go.mod h1:AB6v3BedyhVRIbPQbJnUsBmtup2pFiikpp5n3YyB6Ac= +github.com/aws/aws-sdk-go-v2/service/sso v1.9.0 h1:1qLJeQGBmNQW3mBNzK2CFmrQNmoXWrscPqsrAaU1aTA= +github.com/aws/aws-sdk-go-v2/service/sso v1.9.0/go.mod h1:vCV4glupK3tR7pw7ks7Y4jYRL86VvxS+g5qk04YeWrU= github.com/aws/aws-sdk-go-v2/service/sts v1.6.0/go.mod h1:q7o0j7d7HrJk/vr9uUt3BVRASvcU7gYZB9PUgPiByXg= -github.com/aws/aws-sdk-go-v2/service/sts v1.13.0 h1:n8+dZMOvwkGtmhub8B2wYvRHut45/NB7DeNhNcUnBpg= -github.com/aws/aws-sdk-go-v2/service/sts v1.13.0/go.mod h1:jQto17aC9pJ6xRa1g29uXZhbcS6qNT3PSnKfPShq4sY= +github.com/aws/aws-sdk-go-v2/service/sts v1.14.0 h1:ksiDXhvNYg0D2/UFkLejsaz3LqpW5yjNQ8Nx9Sn2c0E= +github.com/aws/aws-sdk-go-v2/service/sts v1.14.0/go.mod h1:u0xMJKDvvfocRjiozsoZglVNXRG19043xzp3r2ivLIk= github.com/aws/smithy-go v1.6.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.9.1 h1:5vetTooLk4hPWV8q6ym6+lXKAT1Urnm49YkrRKo2J8o= -github.com/aws/smithy-go v1.9.1/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20211215200129-69c85dc22db6 h1:eZSlkTaUtlhgnbn4gOl2Y248cXT+T/jtOww+pQ8m3ow= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20211215200129-69c85dc22db6/go.mod h1:8vJsEZ4iRqG+Vx6pKhWK6U00qcj0KC37IsfszMkY6UE= +github.com/aws/smithy-go v1.10.0 h1:gsoZQMNHnX+PaghNw4ynPsyGP7aUCqx5sY2dlPQsZ0w= +github.com/aws/smithy-go v1.10.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220216180153-3d7835abdf40 h1:skzbn0VkQsUGOtr8M0A2xSjZsnLvq/FaFUOd81FUu9A= +github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220216180153-3d7835abdf40/go.mod h1:8vJsEZ4iRqG+Vx6pKhWK6U00qcj0KC37IsfszMkY6UE= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -194,14 +228,21 @@ github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngE github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blizzy78/varnamelen v0.3.0/go.mod h1:hbwRdBvoBqxk34XyQ6HA0UH3G0/1TKuv5AC4eaBT0Ec= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= +github.com/breml/bidichk v0.1.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= 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/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= +github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= +github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -212,6 +253,8 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charithe/durationcheck v0.0.9/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg= +github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af/go.mod h1:Qjyv4H3//PWVzTeCezG2b9IRn6myJxJSr4TD/xo6ojU= 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/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= @@ -275,6 +318,7 @@ github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo 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.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= +github.com/containerd/containerd v1.5.9/go.mod h1:fvQqCfadDGga5HZyn3j4+dx56qj2I9YwBrlSdalvJYQ= 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= @@ -302,8 +346,9 @@ github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJ 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.10.1 h1:hd1EoVjI2Ax8Cr64tdYqnJ4i4pZU49FkEf5kU8KxQng= github.com/containerd/stargz-snapshotter/estargz v0.10.1/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= +github.com/containerd/stargz-snapshotter/estargz v0.11.0 h1:t0IW5kOmY7AXDAWRUs2uVzDhijAUOAYVr/dyRhOQvBg= +github.com/containerd/stargz-snapshotter/estargz v0.11.0/go.mod h1:/KsZXsJRllMbTKFfG0miFQWViQKdI9+9aSXs+HN0+ac= 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= @@ -330,6 +375,7 @@ github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B 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/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= @@ -338,28 +384,33 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee 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-20190620071333-e64a0ec8b42a/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.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/crossplane/crossplane-runtime v0.15.1-0.20220106140106-428b7c390375 h1:wc0PmOXiNYFgV4k7zVy2NWCqXIpca2tnKA2wureoQNA= -github.com/crossplane/crossplane-runtime v0.15.1-0.20220106140106-428b7c390375/go.mod h1:CH05KIlxoEHEE4aLpUhPuvF+9qXsN6/H6YIDnUEjlDs= +github.com/crossplane/crossplane-runtime v0.15.1-0.20220315141414-988c9ba9c255 h1:l9eAErqfcEzGpq5dMfO/4GTCG2qXgiQgH5J+xhBHqYc= +github.com/crossplane/crossplane-runtime v0.15.1-0.20220315141414-988c9ba9c255/go.mod h1:IPT3HTsovwmbw3i+SdsOyaC3r3b7TW+otBMmZsHLnSU= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= 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/daixiang0/gci v0.2.9/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc= github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= +github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -403,33 +454,47 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= +github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= +github.com/esimonov/ifshort v1.0.3/go.mod h1:yZqNJUrNn20K8Q9n2CrjTKYyVEmX209Hgu+M1LBpeZE= +github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk= +github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM= +github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-critic/go-critic v0.6.1/go.mod h1:SdNCfU0yF3UBjtaZGw6586/WocupMOJuiqgom5DsQxM= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -437,16 +502,20 @@ github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3I github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk= github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= @@ -460,15 +529,34 @@ github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= +github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= +github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= +github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= +github.com/go-toolsmith/astequal v1.0.1/go.mod h1:4oGA3EZXTVItV/ipGiOx7NWkY5veFfcsOJVS2YxltLw= +github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= +github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= +github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= +github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= +github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= +github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= +github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= +github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobuffalo/flect v0.2.3 h1:f/ZukRnSNA/DUpSNDadko7Qc0PhGvsew35p/2tu+CRY= github.com/gobuffalo/flect v0.2.3/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= 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/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= 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= @@ -499,6 +587,7 @@ github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -518,11 +607,25 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= +github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= +github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= +github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= +github.com/golangci/golangci-lint v1.43.0/go.mod h1:VIFlUqidx5ggxDfQagdvd9E67UjMXtTHBkBQ7sHoC5Q= +github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= +github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= +github.com/golangci/misspell v0.3.5/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= +github.com/golangci/revgrep v0.0.0-20210930125155-c22e5001d4f2/go.mod h1:LK+zW4MpyytAWQRz0M4xnzEk50lSvqDQKfx304apFkY= +github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/cel-go v0.9.0/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= +github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= +github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErVRrD+nfy23RCzyl7eqVCEmlT1Zs= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -534,19 +637,22 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-containerregistry v0.8.0/go.mod h1:wW5v71NHGnQyb4k+gSshjxidrC7lN33MdWEn+Mz9TsI= github.com/google/go-containerregistry v0.8.1-0.20220110151055-a61fd0a8e2bb/go.mod h1:wW5v71NHGnQyb4k+gSshjxidrC7lN33MdWEn+Mz9TsI= -github.com/google/go-containerregistry v0.8.1-0.20220120151853-ac864e57b117 h1:Jcm0O+2f2Ul/sgoxCts2ocI1BTaJpP56SBb+0iO40Ug= -github.com/google/go-containerregistry v0.8.1-0.20220120151853-ac864e57b117/go.mod h1:wW5v71NHGnQyb4k+gSshjxidrC7lN33MdWEn+Mz9TsI= -github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20220120151853-ac864e57b117 h1:bRrDPmm+4eFXtlwBa63SONIL/21QUdWi//hBcUaLZiE= -github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20220120151853-ac864e57b117/go.mod h1:BH7pLQnIZhfVpL7cRyWhvvz1bZLY9V45/HvXVh5UMDY= -github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20220110151055-a61fd0a8e2bb h1:TVAKj23PFWViWiy7iGOuVSHSD8T8DZ7gdDvW138EFgg= -github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20220110151055-a61fd0a8e2bb/go.mod h1:SK4EqntTk6tHEyNngoqHUwjjZaW6mfzLukei4+cbvu8= +github.com/google/go-containerregistry v0.8.1-0.20220302183023-329563766ce8 h1:9+qmGDBMJSLoQBd9rPNO+gIN9kiWq5x4b8FIeCfIIYs= +github.com/google/go-containerregistry v0.8.1-0.20220302183023-329563766ce8/go.mod h1:MMbnwuvLeZJRPqhTs8jDWc8xGlOs5YCGx1TSc/qdExk= +github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20220302183023-329563766ce8 h1:gc/JJZk2Rdo8jXs8TNbZoEpKy9ngh0dtnnPcrebGRos= +github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20220302183023-329563766ce8/go.mod h1:m6nUV1MgElByyfWJCAKsEt+5s/BtU9ZLjyHDmoeAHcM= +github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20220128225446-c63684ed5f15/go.mod h1:juPcrnQFaTyH72YA4Lt2MwEFSGi3orvGXyzxzo+RLss= +github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20220302183023-329563766ce8 h1:M8uvwefV0EmZSCMz+JSWCKAUPl7Xzu3CV9+wWiSiXLA= +github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20220302183023-329563766ce8/go.mod h1:MO/Ilc3XTxy/Pi8aMXEiRUl6icOqResFyhSFCLlqtR8= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -557,6 +663,7 @@ github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -567,11 +674,14 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEivX+9mPgw= +github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/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/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -581,53 +691,107 @@ github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2c github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= +github.com/gordonklaus/ineffassign v0.0.0-20210225214923-2e10b2664254/go.mod h1:M9mZEtGIsR1oDaZagNPNG9iq9n2HrhZ17dsXk73V3Lw= +github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= +github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= +github.com/gostaticanalysis/analysisutil v0.1.0/go.mod h1:dMhHRU9KTiDcuLGdy87/2gTR8WruwYZrKdRq9m1O6uw= +github.com/gostaticanalysis/analysisutil v0.4.1/go.mod h1:18U/DLpRgIUd459wGxVHE0fRgmo1UgHDcbw7F5idXu0= +github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc= +github.com/gostaticanalysis/comment v1.3.0/go.mod h1:xMicKDx7XRXYdVwY9f9wQpDJVnqWxw9wCauCMKp+IBI= +github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= +github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= +github.com/gostaticanalysis/forcetypeassert v0.0.0-20200621232751-01d4955beaa5/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak= +github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= +github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= +github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= -github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= 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/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.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= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-getter v1.4.0/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.0.0 h1:bkKf0BeBXcSYa7f5Fyi9gMuQ8gNsxeiNpZjR6VxNZeo= github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= 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 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM= +github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= +github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= +github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= +github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 h1:cCRo8gK7oq6A2L6LICkUZ+/a5rLiRXFMf1Qd4xSwxTc= +github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1 h1:78ki3QBevHwYrVxnyVeaEz+7WtifHhauYF23es/0KlI= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/password v0.1.1/go.mod h1:9hH302QllNwu1o2TGYtSk8I8kTAN0ca1EHpwhm5Mmzo= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 h1:nd0HIW15E6FG1MsnArYaHfuw9C2zgzM8LxkG5Ty/788= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= +github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -639,10 +803,19 @@ github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM= +github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw= +github.com/hashicorp/vault/sdk v0.3.0 h1:kR3dpxNkhh/wr6ycaJYqp6AFT/i2xaftbfnwZduTKEY= +github.com/hashicorp/vault/sdk v0.3.0/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= +github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= @@ -653,16 +826,26 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= +github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= +github.com/jhump/protoreflect v1.6.1 h1:4/2yi5LyDPP7nN+Hiird1SAJ6YoxUm13/oxHGRnbPd8= +github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= +github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= +github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/josharian/txtarfs v0.0.0-20210218200122-0702f000015a/go.mod h1:izVPOvVRsHiKkeGCT6tYBNWyDVuzj9wAaBb5R9qamfw= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -674,16 +857,23 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= 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/julz/importas v0.0.0-20210419104244-841f0c0fe66d/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/errcheck v1.6.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.11.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 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= +github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.14.2 h1:S0OHlFk/Gbon/yauFJ4FfJJF5V0fc5HbBTJazi28pRw= +github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= 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= @@ -698,6 +888,19 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kulti/thelper v0.4.0/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U= +github.com/kunwardeep/paralleltest v1.0.3/go.mod h1:vLydzomDFpk7yu5UX02RmP0H8QfRPOV/oFhWN85Mjb4= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg= +github.com/ldez/gomoddirectives v0.2.2/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= +github.com/ldez/tagliatelle v0.2.0/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88= +github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -708,12 +911,16 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.0/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/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= +github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -726,29 +933,49 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 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.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= +github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= +github.com/mgechev/revive v1.1.2/go.mod h1:bnXsMr+ZTH09V5rssEI+jHAZ4z+ZdyhgO/zsy3EhK+0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= 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/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +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/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= @@ -764,20 +991,36 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8qUplsoSU4k= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8= +github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= 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= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= +github.com/mwitkow/go-proto-validators v0.2.0/go.mod h1:ZfA1hW+UH/2ZHOWvQ3HnQaU0DtnpXu850MZiy+YUgcc= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= +github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nishanths/exhaustive v0.2.3/go.mod h1:bhIX678Nx8inLM9PbpvK1yv6oGtoP8BfaIeMzgBNKvc= +github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ= +github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB9sbB1usJ+xjQE= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +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.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 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.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -795,6 +1038,7 @@ 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.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.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -805,8 +1049,10 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 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.20211117181255-693428a734f5 h1:q37d91F6BO4Jp1UqWiun0dUFYaqv6WsKTLTCaWv+8LY= github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 h1:+czc/J8SlhPKLOtVLMQc+xDCFBT73ZStMsRhSsUhsSg= +github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198/go.mod h1:j4h1pJW6ZcJTgMZWP3+7RlG3zTaP02aDZ/Qw0sppK7Q= 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= @@ -824,13 +1070,23 @@ github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqi github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= +github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= +github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= +github.com/pierrec/lz4 v2.5.2+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= @@ -838,8 +1094,10 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= @@ -881,18 +1139,44 @@ github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA= +github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= +github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= +github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30= +github.com/quasilyte/go-ruleguard v0.3.13/go.mod h1:Ul8wwdqR6kBVOCt2dipDBkE+T6vAV/iixkrKuRTN1oQ= +github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= +github.com/quasilyte/go-ruleguard/dsl v0.3.10/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= +github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc= +github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428214800-545e0d2e0bf7/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= +github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= 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= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryancurrah/gomodguard v1.2.3/go.mod h1:rYbA/4Tg5c54mV1sv4sQTP5WOPBcoLtnBZ7/TEhXAbg= +github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA= 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 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= +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/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= -github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM= +github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= 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= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/securego/gosec/v2 v2.9.1/go.mod h1:oDcDLcatOJxkCGaCaq8lua1jTnYf6Sou4wdiJ1n4iHc= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= +github.com/shirou/gopsutil/v3 v3.21.10/go.mod h1:t75NhzCZ/dYyPQjyQmrAYP6c8+LCdFANeBMdLPCNnew= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 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= @@ -903,11 +1187,14 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sivchari/tenv v1.4.7/go.mod h1:5nF+bITvkebQVanjU6IuMbvIot/7ReNsUV7I5NbprB0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= +github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= @@ -921,6 +1208,7 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= @@ -934,19 +1222,24 @@ github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= -github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU= +github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= 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/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-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.1.4/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= @@ -955,23 +1248,44 @@ 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/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ= 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/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= +github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= +github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= +github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8= +github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= +github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= +github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tomarrell/wrapcheck/v2 v2.4.0/go.mod h1:68bQ/eJg55BROaRTbMjC7vuhL2OgfoG8bLp9ZyoBfyY= +github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= +github.com/tommy-muehle/go-mnd/v2 v2.4.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= 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.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= +github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= 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/uudashr/gocognit v1.0.5/go.mod h1:wgYz0mitoKOTysqxTDMOUXg+Jb5SvtihkfmugIZYpEA= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= +github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= +github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE= 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= @@ -983,8 +1297,14 @@ github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema 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-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yeya24/promlinter v0.1.0/go.mod h1:rs5vtZzeBHqqMwXqFScncpCF6u06lezhZepno9AB1Oc= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -997,8 +1317,10 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPS github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c/go.mod h1:xCI7ZzBfRuGgBXyXO6yfWfDmlWd35khcWpUa4L0xI/k= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -1010,6 +1332,7 @@ go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lL go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= +go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403/go.mod h1:jHoPAGnDrCy6kaI2tAze5Prf0Nr0w/oNkROt2lw3n3o= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -1032,24 +1355,32 @@ go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16g go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +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/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1061,15 +1392,18 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U 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-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI= -golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed h1:YoWVYYAfvQ4ddHv3OKmIvX7NCAhFGTj62VP2l2kfBbA= +golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1080,6 +1414,7 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1108,6 +1443,7 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1132,6 +1468,7 @@ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1140,6 +1477,7 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= @@ -1162,6 +1500,7 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -1169,8 +1508,10 @@ golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127074510-2fabfed7e28f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1194,6 +1535,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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= @@ -1209,6 +1551,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1254,6 +1597,7 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w 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= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1303,25 +1647,32 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1336,18 +1687,26 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190307163923-6a08e3108db3/go.mod h1:25r3+/G6/xytQM8iWZKq3Hn0kr0rgFKPUNVEL/dr3z4= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190321232350-e250d351ecad/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -1359,8 +1718,13 @@ golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190916130336-e45ffcd953cc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1368,9 +1732,11 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1380,33 +1746,62 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200622203043-20e05c1c8ffa/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200624225443-88f3c62a19ff/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200625211823-6506e20df31f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200630154851-b2d8b0336632/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200706234117-b22de6825cf7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200812195022-5ae4c3c160a0/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200831203904-5a2aa26beb65/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201001104356-43ebab892c4c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201028025901-8cd080b735b3/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201114224030-61ea331ec02b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201118003311-bd56c0adb394/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201230224404-63754364767c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210104081019-d8d6ddbec6ee/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= +golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= -golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= +golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1419,6 +1814,7 @@ google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEt google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= @@ -1449,16 +1845,20 @@ google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUb google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.65.0/go.mod h1:ArYhxgGadlWmqO1IqVujw6Cs8IdD33bTmzKo2Sh+cbg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= +google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1467,6 +1867,7 @@ google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dT 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= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= @@ -1488,6 +1889,8 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200707001353-8e8330bf89df/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1529,7 +1932,13 @@ google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350 h1:YxHp5zqIcAShDEvRr5/0rVESVS+njYF68PSdazrNLJo= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1542,6 +1951,7 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 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.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= @@ -1559,8 +1969,11 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1587,27 +2000,33 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 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/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.6/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -1619,8 +2038,9 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk= +gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1629,20 +2049,23 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= 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.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= -k8s.io/api v0.23.0 h1:WrL1gb73VSC8obi8cuYETJGXEoFNEh3LU0Pt+Sokgro= k8s.io/api v0.23.0/go.mod h1:8wmDdLBHBNxtOIytwLstXt5E9PddnZb0GaMcqsvDBpg= +k8s.io/api v0.23.3 h1:KNrME8KHGr12Ozjf8ytOewKzZh6hl/hHUZeHddT3a38= +k8s.io/api v0.23.3/go.mod h1:w258XdGyvCmnBj/vGzQMj6kzdufJZVUwEM1U2fRJwSQ= k8s.io/apiextensions-apiserver v0.23.0 h1:uii8BYmHYiT2ZTAJxmvc3X8UhNYMxl2A0z0Xq3Pm+WY= k8s.io/apiextensions-apiserver v0.23.0/go.mod h1:xIFAEEDlAZgpVBl/1VSjGDmLoXAWRG40+GsWhKhAxY4= k8s.io/apimachinery v0.20.1/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.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= -k8s.io/apimachinery v0.23.0 h1:mIfWRMjBuMdolAWJ3Fd+aPTMv3X9z+waiARMpvvb0HQ= k8s.io/apimachinery v0.23.0/go.mod h1:fFCTTBKvKcwTPFzjlcxp91uPFZr+JA0FubU4fLzzFYc= +k8s.io/apimachinery v0.23.3 h1:7IW6jxNzrXTsP0c8yXz2E5Yx/WTzVPTsHIx/2Vm0cIk= +k8s.io/apimachinery v0.23.3/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= 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= @@ -1651,8 +2074,9 @@ k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= 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.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= -k8s.io/client-go v0.23.0 h1:vcsOqyPq7XV3QmQRCBH/t9BICJM9Q1M18qahjv+rebY= k8s.io/client-go v0.23.0/go.mod h1:hrDnpnK1mSr65lHHcUuIZIXDgEbzc7/683c6hyG4jTA= +k8s.io/client-go v0.23.3 h1:23QYUmCQ/W6hW78xIwm3XqZrrKZM+LWDqW2zfo+szJs= +k8s.io/client-go v0.23.3/go.mod h1:47oMd+YvAOqZM7pcQ6neJtBiFH7alOyfunYN48VsmwE= k8s.io/code-generator v0.23.0 h1:lhyd2KJVCEmpjaCpuoooGs+e3xhPwpYvupnNRidO0Ds= k8s.io/code-generator v0.23.0/go.mod h1:vQvOhDXhuzqiVfM/YHp+dmg10WDZCchJVObc9MvowsE= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= @@ -1671,18 +2095,26 @@ k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw= k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.40.1 h1:P4RRucWk/lFOlDdkAr3mc7iWFkgKrZY9qZMAgek06S4= +k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4= k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= +k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf h1:M9XBsiMslw2lb2ZzglC0TOkBPK5NQi0/noUrdnoFwUg= +k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b h1:wxEMGetGMur3J1xuGLQY7GEQYg9bZxKn3tKo5k/eYcs= k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220127004650-9b3446523e65 h1:ONWS0Wgdg5wRiQIAui7L/023aC9+IxrIrydY7l8llsE= +k8s.io/utils v0.0.0-20220127004650-9b3446523e65/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= +mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= +mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= +mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7/go.mod h1:hBpJkZE8H/sb+VRFvw2+rBpHNsTBcvSpk61hr8mzXZE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= @@ -1693,13 +2125,15 @@ sigs.k8s.io/controller-runtime v0.11.0 h1:DqO+c8mywcZLFJWILq4iktoECTyn30Bkj0Cwgq sigs.k8s.io/controller-runtime v0.11.0/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA= sigs.k8s.io/controller-tools v0.8.0 h1:uUkfTGEwrguqYYfcI2RRGUnC8mYdCFDqfwPKUcNJh1o= sigs.k8s.io/controller-tools v0.8.0/go.mod h1:qE2DXhVOiEq5ijmINcFbqi9GZrrUjzB1TuJU0xa6eoY= -sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s= sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= +sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y= +sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= 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.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v4 v4.2.0 h1:kDvPBbnPk+qYmkHmSo8vKGp438IASWofnbbUKDE/bv0= sigs.k8s.io/structured-merge-diff/v4 v4.2.0/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/internal/client/clientset/versioned/clientset.go b/internal/client/clientset/versioned/clientset.go index 082bfef62..c4f147f40 100644 --- a/internal/client/clientset/versioned/clientset.go +++ b/internal/client/clientset/versioned/clientset.go @@ -26,6 +26,7 @@ import ( pkgv1 "github.com/crossplane/crossplane/internal/client/clientset/versioned/typed/pkg/v1" pkgv1alpha1 "github.com/crossplane/crossplane/internal/client/clientset/versioned/typed/pkg/v1alpha1" pkgv1beta1 "github.com/crossplane/crossplane/internal/client/clientset/versioned/typed/pkg/v1beta1" + secretsv1alpha1 "github.com/crossplane/crossplane/internal/client/clientset/versioned/typed/secrets/v1alpha1" discovery "k8s.io/client-go/discovery" rest "k8s.io/client-go/rest" flowcontrol "k8s.io/client-go/util/flowcontrol" @@ -37,6 +38,7 @@ type Interface interface { PkgV1alpha1() pkgv1alpha1.PkgV1alpha1Interface PkgV1beta1() pkgv1beta1.PkgV1beta1Interface PkgV1() pkgv1.PkgV1Interface + SecretsV1alpha1() secretsv1alpha1.SecretsV1alpha1Interface } // Clientset contains the clients for groups. Each group has exactly one @@ -47,6 +49,7 @@ type Clientset struct { pkgV1alpha1 *pkgv1alpha1.PkgV1alpha1Client pkgV1beta1 *pkgv1beta1.PkgV1beta1Client pkgV1 *pkgv1.PkgV1Client + secretsV1alpha1 *secretsv1alpha1.SecretsV1alpha1Client } // ApiextensionsV1 retrieves the ApiextensionsV1Client @@ -69,6 +72,11 @@ func (c *Clientset) PkgV1() pkgv1.PkgV1Interface { return c.pkgV1 } +// SecretsV1alpha1 retrieves the SecretsV1alpha1Client +func (c *Clientset) SecretsV1alpha1() secretsv1alpha1.SecretsV1alpha1Interface { + return c.secretsV1alpha1 +} + // Discovery retrieves the DiscoveryClient func (c *Clientset) Discovery() discovery.DiscoveryInterface { if c == nil { @@ -125,6 +133,10 @@ func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, if err != nil { return nil, err } + cs.secretsV1alpha1, err = secretsv1alpha1.NewForConfigAndClient(&configShallowCopy, httpClient) + if err != nil { + return nil, err + } cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient) if err != nil { @@ -150,6 +162,7 @@ func New(c rest.Interface) *Clientset { cs.pkgV1alpha1 = pkgv1alpha1.New(c) cs.pkgV1beta1 = pkgv1beta1.New(c) cs.pkgV1 = pkgv1.New(c) + cs.secretsV1alpha1 = secretsv1alpha1.New(c) cs.DiscoveryClient = discovery.NewDiscoveryClient(c) return &cs diff --git a/internal/client/clientset/versioned/fake/clientset_generated.go b/internal/client/clientset/versioned/fake/clientset_generated.go index 9218032ec..4197e5924 100644 --- a/internal/client/clientset/versioned/fake/clientset_generated.go +++ b/internal/client/clientset/versioned/fake/clientset_generated.go @@ -28,6 +28,8 @@ import ( fakepkgv1alpha1 "github.com/crossplane/crossplane/internal/client/clientset/versioned/typed/pkg/v1alpha1/fake" pkgv1beta1 "github.com/crossplane/crossplane/internal/client/clientset/versioned/typed/pkg/v1beta1" fakepkgv1beta1 "github.com/crossplane/crossplane/internal/client/clientset/versioned/typed/pkg/v1beta1/fake" + secretsv1alpha1 "github.com/crossplane/crossplane/internal/client/clientset/versioned/typed/secrets/v1alpha1" + fakesecretsv1alpha1 "github.com/crossplane/crossplane/internal/client/clientset/versioned/typed/secrets/v1alpha1/fake" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/discovery" @@ -104,3 +106,8 @@ func (c *Clientset) PkgV1beta1() pkgv1beta1.PkgV1beta1Interface { func (c *Clientset) PkgV1() pkgv1.PkgV1Interface { return &fakepkgv1.FakePkgV1{Fake: &c.Fake} } + +// SecretsV1alpha1 retrieves the SecretsV1alpha1Client +func (c *Clientset) SecretsV1alpha1() secretsv1alpha1.SecretsV1alpha1Interface { + return &fakesecretsv1alpha1.FakeSecretsV1alpha1{Fake: &c.Fake} +} diff --git a/internal/client/clientset/versioned/fake/register.go b/internal/client/clientset/versioned/fake/register.go index 92295f87b..daf19b0b8 100644 --- a/internal/client/clientset/versioned/fake/register.go +++ b/internal/client/clientset/versioned/fake/register.go @@ -23,6 +23,7 @@ import ( pkgv1 "github.com/crossplane/crossplane/apis/pkg/v1" pkgv1alpha1 "github.com/crossplane/crossplane/apis/pkg/v1alpha1" pkgv1beta1 "github.com/crossplane/crossplane/apis/pkg/v1beta1" + secretsv1alpha1 "github.com/crossplane/crossplane/apis/secrets/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" @@ -38,6 +39,7 @@ var localSchemeBuilder = runtime.SchemeBuilder{ pkgv1alpha1.AddToScheme, pkgv1beta1.AddToScheme, pkgv1.AddToScheme, + secretsv1alpha1.AddToScheme, } // AddToScheme adds all types of this clientset into the given scheme. This allows composition diff --git a/internal/client/clientset/versioned/scheme/register.go b/internal/client/clientset/versioned/scheme/register.go index 1b38b3bb7..3edb838ff 100644 --- a/internal/client/clientset/versioned/scheme/register.go +++ b/internal/client/clientset/versioned/scheme/register.go @@ -23,6 +23,7 @@ import ( pkgv1 "github.com/crossplane/crossplane/apis/pkg/v1" pkgv1alpha1 "github.com/crossplane/crossplane/apis/pkg/v1alpha1" pkgv1beta1 "github.com/crossplane/crossplane/apis/pkg/v1beta1" + secretsv1alpha1 "github.com/crossplane/crossplane/apis/secrets/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" @@ -38,6 +39,7 @@ var localSchemeBuilder = runtime.SchemeBuilder{ pkgv1alpha1.AddToScheme, pkgv1beta1.AddToScheme, pkgv1.AddToScheme, + secretsv1alpha1.AddToScheme, } // AddToScheme adds all types of this clientset into the given scheme. This allows composition diff --git a/internal/feature/generate.go b/internal/client/clientset/versioned/typed/secrets/v1alpha1/doc.go similarity index 58% rename from internal/feature/generate.go rename to internal/client/clientset/versioned/typed/secrets/v1alpha1/doc.go index b9e7eed0d..1de979aac 100644 --- a/internal/feature/generate.go +++ b/internal/client/clientset/versioned/typed/secrets/v1alpha1/doc.go @@ -1,7 +1,5 @@ -// +build generate - /* -Copyright 2019 The Crossplane Authors. +Copyright 2021 The Crossplane Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,13 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package feature - -// NOTE(negz): See the below link for details on what is happening here. -// https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module - -import ( - _ "golang.org/x/tools/cmd/stringer" //nolint:typecheck -) +// Code generated by client-gen. DO NOT EDIT. -//go:generate go run golang.org/x/tools/cmd/stringer -type=Flag +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/internal/client/clientset/versioned/typed/secrets/v1alpha1/fake/doc.go b/internal/client/clientset/versioned/typed/secrets/v1alpha1/fake/doc.go new file mode 100644 index 000000000..b9226dbcc --- /dev/null +++ b/internal/client/clientset/versioned/typed/secrets/v1alpha1/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/internal/client/clientset/versioned/typed/secrets/v1alpha1/fake/fake_secrets_client.go b/internal/client/clientset/versioned/typed/secrets/v1alpha1/fake/fake_secrets_client.go new file mode 100644 index 000000000..03e06ffd1 --- /dev/null +++ b/internal/client/clientset/versioned/typed/secrets/v1alpha1/fake/fake_secrets_client.go @@ -0,0 +1,35 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeSecretsV1alpha1 struct { + *testing.Fake +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeSecretsV1alpha1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/internal/client/clientset/versioned/typed/secrets/v1alpha1/generated_expansion.go b/internal/client/clientset/versioned/typed/secrets/v1alpha1/generated_expansion.go new file mode 100644 index 000000000..e39a65f24 --- /dev/null +++ b/internal/client/clientset/versioned/typed/secrets/v1alpha1/generated_expansion.go @@ -0,0 +1,19 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 diff --git a/internal/client/clientset/versioned/typed/secrets/v1alpha1/secrets_client.go b/internal/client/clientset/versioned/typed/secrets/v1alpha1/secrets_client.go new file mode 100644 index 000000000..8a327e791 --- /dev/null +++ b/internal/client/clientset/versioned/typed/secrets/v1alpha1/secrets_client.go @@ -0,0 +1,102 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "net/http" + + v1alpha1 "github.com/crossplane/crossplane/apis/secrets/v1alpha1" + "github.com/crossplane/crossplane/internal/client/clientset/versioned/scheme" + rest "k8s.io/client-go/rest" +) + +type SecretsV1alpha1Interface interface { + RESTClient() rest.Interface +} + +// SecretsV1alpha1Client is used to interact with features provided by the secrets.crossplane.io group. +type SecretsV1alpha1Client struct { + restClient rest.Interface +} + +// NewForConfig creates a new SecretsV1alpha1Client for the given config. +// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), +// where httpClient was generated with rest.HTTPClientFor(c). +func NewForConfig(c *rest.Config) (*SecretsV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + httpClient, err := rest.HTTPClientFor(&config) + if err != nil { + return nil, err + } + return NewForConfigAndClient(&config, httpClient) +} + +// NewForConfigAndClient creates a new SecretsV1alpha1Client for the given config and http client. +// Note the http client provided takes precedence over the configured transport values. +func NewForConfigAndClient(c *rest.Config, h *http.Client) (*SecretsV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientForConfigAndClient(&config, h) + if err != nil { + return nil, err + } + return &SecretsV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new SecretsV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *SecretsV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new SecretsV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *SecretsV1alpha1Client { + return &SecretsV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *SecretsV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/internal/controller/apiextensions/apiextensions.go b/internal/controller/apiextensions/apiextensions.go index 1ee5da0dc..6b223ab38 100644 --- a/internal/controller/apiextensions/apiextensions.go +++ b/internal/controller/apiextensions/apiextensions.go @@ -20,6 +20,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "github.com/crossplane/crossplane-runtime/pkg/controller" + "github.com/crossplane/crossplane/internal/controller/apiextensions/composition" "github.com/crossplane/crossplane/internal/controller/apiextensions/definition" "github.com/crossplane/crossplane/internal/controller/apiextensions/offered" diff --git a/internal/controller/apiextensions/claim/connection.go b/internal/controller/apiextensions/claim/connection.go new file mode 100644 index 000000000..d0f112fe7 --- /dev/null +++ b/internal/controller/apiextensions/claim/connection.go @@ -0,0 +1,72 @@ +package claim + +import ( + "context" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + "github.com/crossplane/crossplane-runtime/pkg/resource" +) + +// NopConnectionUnpublisher is a ConnectionUnpublisher that does nothing. +type NopConnectionUnpublisher struct{} + +// NewNopConnectionUnpublisher returns a new NopConnectionUnpublisher +func NewNopConnectionUnpublisher() *NopConnectionUnpublisher { + return &NopConnectionUnpublisher{} +} + +// UnpublishConnection does nothing and returns no error with +// UnpublishConnection. Expected to be used where deletion of connection +// secret is already handled by K8s garbage collection and there is actually +// nothing to do to unpublish connection details. +func (n *NopConnectionUnpublisher) UnpublishConnection(_ context.Context, _ resource.LocalConnectionSecretOwner, _ managed.ConnectionDetails) error { + return nil +} + +// SecretStoreConnectionUnpublisher unpublishes secret store connection secrets. +type SecretStoreConnectionUnpublisher struct { + // TODO(turkenh): Use a narrower interface, i.e. we don't need Publish + // method here. Please note we cannot use ConnectionUnpublisher interface + // defined in this package as it expects a LocalConnectionSecretOwner + // which is exactly what this struct is providing. Ideally, we should + // split managed.ConnectionPublisher as Publisher and Unpublisher, but I + // would like to leave this to a further PR after we graduate Secret Store + // and decide to clean up the old API. + publisher managed.ConnectionPublisher +} + +// NewSecretStoreConnectionUnpublisher returns a new SecretStoreConnectionUnpublisher. +func NewSecretStoreConnectionUnpublisher(p managed.ConnectionPublisher) *SecretStoreConnectionUnpublisher { + return &SecretStoreConnectionUnpublisher{ + publisher: p, + } +} + +// UnpublishConnection details for the supplied Managed resource. +func (u *SecretStoreConnectionUnpublisher) UnpublishConnection(ctx context.Context, so resource.LocalConnectionSecretOwner, c managed.ConnectionDetails) error { + return u.publisher.UnpublishConnection(ctx, newClaimAsSecretOwner(so), c) +} + +// soClaim is a type that enables using claim type with Secret Store +// UnpublishConnection method by satisfyng resource.ConnectionSecretOwner +// interface. +type soClaim struct { + resource.LocalConnectionSecretOwner +} + +func newClaimAsSecretOwner(lo resource.LocalConnectionSecretOwner) *soClaim { + return &soClaim{ + LocalConnectionSecretOwner: lo, + } +} + +func (s soClaim) SetWriteConnectionSecretToReference(_ *xpv1.SecretReference) {} + +func (s soClaim) GetWriteConnectionSecretToReference() *xpv1.SecretReference { + // SecretStoreConnectionUnpublisher does not use + // WriteConnectionSecretToReference interface, so, we are implementing + // just to satisfy resource.ConnectionSecretOwner interface. + return nil +} diff --git a/internal/controller/apiextensions/claim/reconciler.go b/internal/controller/apiextensions/claim/reconciler.go index 783d7f1cd..a2f9a43e5 100644 --- a/internal/controller/apiextensions/claim/reconciler.go +++ b/internal/controller/apiextensions/claim/reconciler.go @@ -34,6 +34,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/event" "github.com/crossplane/crossplane-runtime/pkg/logging" "github.com/crossplane/crossplane-runtime/pkg/meta" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/crossplane-runtime/pkg/resource/unstructured" "github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/claim" @@ -55,6 +56,7 @@ const ( errGetClaim = "cannot get composite resource claim" errGetComposite = "cannot get referenced composite resource" errDeleteComposite = "cannot delete referenced composite resource" + errDeleteCDs = "cannot delete connection details" errRemoveFinalizer = "cannot remove composite resource claim finalizer" errAddFinalizer = "cannot add composite resource claim finalizer" errConfigureComposite = "cannot configure composite resource" @@ -125,6 +127,42 @@ func (fn ConnectionPropagatorFn) PropagateConnection(ctx context.Context, to res return fn(ctx, to, from) } +// A ConnectionPropagatorChain runs multiple connection propagators. +type ConnectionPropagatorChain []ConnectionPropagator + +// PropagateConnection details from one resource to the other. +// This method calls PropagateConnection for all ConnectionPropagator's in the +// chain and returns propagated if at least one ConnectionPropagator propagates +// the connection details but exits with an error if any of them fails without +// calling the remaining ones. +func (pc ConnectionPropagatorChain) PropagateConnection(ctx context.Context, to resource.LocalConnectionSecretOwner, from resource.ConnectionSecretOwner) (propagated bool, err error) { + for _, p := range pc { + var pg bool + pg, err = p.PropagateConnection(ctx, to, from) + if pg { + propagated = true + } + if err != nil { + return propagated, err + } + } + return propagated, nil +} + +// A ConnectionUnpublisher is responsible for cleaning up connection secret. +type ConnectionUnpublisher interface { + // UnpublishConnection details for the supplied Managed resource. + UnpublishConnection(ctx context.Context, so resource.LocalConnectionSecretOwner, c managed.ConnectionDetails) error +} + +// A ConnectionUnpublisherFn is responsible for cleaning up connection secret. +type ConnectionUnpublisherFn func(ctx context.Context, so resource.LocalConnectionSecretOwner, c managed.ConnectionDetails) error + +// UnpublishConnection details of a local connection secret owner. +func (fn ConnectionUnpublisherFn) UnpublishConnection(ctx context.Context, so resource.LocalConnectionSecretOwner, c managed.ConnectionDetails) error { + return fn(ctx, so, c) +} + // A Reconciler reconciles composite resource claims by creating exactly one kind of // concrete composite resource. Each composite resource claim kind should create an instance // of this controller for each composite resource kind they can bind to, using @@ -162,13 +200,15 @@ type crClaim struct { resource.Finalizer Binder Configurator + ConnectionUnpublisher } func defaultCRClaim(c client.Client) crClaim { return crClaim{ - Finalizer: resource.NewAPIFinalizer(c, finalizer), - Binder: NewAPIBinder(c), - Configurator: NewAPIClaimConfigurator(c), + Finalizer: resource.NewAPIFinalizer(c, finalizer), + Binder: NewAPIBinder(c), + Configurator: NewAPIClaimConfigurator(c), + ConnectionUnpublisher: NewNopConnectionUnpublisher(), } } @@ -207,6 +247,14 @@ func WithConnectionPropagator(p ConnectionPropagator) ReconcilerOption { } } +// WithConnectionUnpublisher specifies which ConnectionUnpublisher should be +// used to unpublish resource connection details. +func WithConnectionUnpublisher(u ConnectionUnpublisher) ReconcilerOption { + return func(r *Reconciler) { + r.claim.ConnectionUnpublisher = u + } +} + // WithBinder specifies which Binder should be used to bind // resources to their claim. func WithBinder(b Binder) ReconcilerOption { @@ -332,6 +380,17 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco record.Event(cm, event.Warning(reasonDelete, err)) return reconcile.Result{}, err } + + } + + // Claims do not publish connection details but may propagate XR + // secrets. Hence, we need to clean up propagated secrets when the + // claim is deleted. + if err := r.claim.UnpublishConnection(ctx, cm, nil); err != nil { + log.Debug(errDeleteCDs, "error", err) + err = errors.Wrap(err, errDeleteCDs) + record.Event(cm, event.Warning(reasonDelete, err)) + return reconcile.Result{}, err } log.Debug("Successfully deleted composite resource") diff --git a/internal/controller/apiextensions/composite/api_test.go b/internal/controller/apiextensions/composite/api_test.go index ca8a10840..5042e2e2d 100644 --- a/internal/controller/apiextensions/composite/api_test.go +++ b/internal/controller/apiextensions/composite/api_test.go @@ -46,7 +46,7 @@ func TestPublishConnection(t *testing.T) { errBoom := errors.New("boom") owner := &fake.MockConnectionSecretOwner{ - Ref: &xpv1.SecretReference{ + WriterTo: &xpv1.SecretReference{ Namespace: "coolnamespace", Name: "coolsecret", }, diff --git a/internal/controller/apiextensions/composite/connection.go b/internal/controller/apiextensions/composite/connection.go new file mode 100644 index 000000000..498271bec --- /dev/null +++ b/internal/controller/apiextensions/composite/connection.go @@ -0,0 +1,193 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package composite + +import ( + "context" + + "github.com/pkg/errors" + "sigs.k8s.io/controller-runtime/pkg/client" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + "github.com/crossplane/crossplane-runtime/pkg/resource" + v1 "github.com/crossplane/crossplane/apis/apiextensions/v1" +) + +// ConnectionDetailsFetcher fetches the connection details of the Composed resource. +type ConnectionDetailsFetcher interface { + FetchConnectionDetails(ctx context.Context, cd resource.Composed, t v1.ComposedTemplate) (managed.ConnectionDetails, error) +} + +// A ConnectionDetailsFetcherChain chains multiple ConnectionDetailsFetchers. +type ConnectionDetailsFetcherChain []ConnectionDetailsFetcher + +// FetchConnectionDetails of the supplied composed resource, if any. +func (fc ConnectionDetailsFetcherChain) FetchConnectionDetails(ctx context.Context, cd resource.Composed, t v1.ComposedTemplate) (managed.ConnectionDetails, error) { + all := make(managed.ConnectionDetails) + for _, p := range fc { + conn, err := p.FetchConnectionDetails(ctx, cd, t) + if err != nil { + return all, err + } + for k, v := range conn { + all[k] = v + } + } + return all, nil +} + +// SecretStoreConnectionPublisher is a ConnectionPublisher that stores +// connection details on the configured SecretStore. +type SecretStoreConnectionPublisher struct { + publisher managed.ConnectionPublisher + filter []string +} + +// NewSecretStoreConnectionPublisher returns a SecretStoreConnectionPublisher +func NewSecretStoreConnectionPublisher(p managed.ConnectionPublisher, filter []string) *SecretStoreConnectionPublisher { + return &SecretStoreConnectionPublisher{ + publisher: p, + filter: filter, + } +} + +// PublishConnection details for the supplied resource. +func (p *SecretStoreConnectionPublisher) PublishConnection(ctx context.Context, o resource.ConnectionSecretOwner, c managed.ConnectionDetails) (published bool, err error) { + // This resource does not want to expose a connection secret. + if o.GetPublishConnectionDetailsTo() == nil { + return false, nil + } + + data := map[string][]byte{} + m := map[string]bool{} + for _, key := range p.filter { + m[key] = true + } + + for key, val := range c { + // If the filter does not have any keys, we allow all given keys to be + // published. + if len(m) == 0 || m[key] { + data[key] = val + } + } + + return p.publisher.PublishConnection(ctx, o, data) +} + +// UnpublishConnection details for the supplied resource. +func (p *SecretStoreConnectionPublisher) UnpublishConnection(ctx context.Context, o resource.ConnectionSecretOwner, c managed.ConnectionDetails) error { + return p.publisher.UnpublishConnection(ctx, o, c) +} + +// SecretStoreConnectionDetailsFetcher is a ConnectionDetailsFetcher that +// fetches connection details to the configured SecretStore. +type SecretStoreConnectionDetailsFetcher struct { + fetcher managed.ConnectionDetailsFetcher +} + +// NewSecretStoreConnectionDetailsFetcher returns a +// SecretStoreConnectionDetailsFetcher +func NewSecretStoreConnectionDetailsFetcher(f managed.ConnectionDetailsFetcher) *SecretStoreConnectionDetailsFetcher { + return &SecretStoreConnectionDetailsFetcher{ + fetcher: f, + } +} + +// FetchConnectionDetails of the supplied composed resource, if any. +func (f *SecretStoreConnectionDetailsFetcher) FetchConnectionDetails(ctx context.Context, cd resource.Composed, t v1.ComposedTemplate) (managed.ConnectionDetails, error) { // nolint:gocyclo + // NOTE(turkenh): Added linter exception for gocyclo similar to existing + // APIConnectionDetailsFetcher.FetchConnectionDetails method given most + // of the complexity coming from simply if checks and, I wanted to keep this + // as identical as possible to aforementioned method. This can be refactored + // with the removal of "WriteConnectionSecretRef" API. + + so := cd.(resource.ConnectionSecretOwner) + data, err := f.fetcher.FetchConnection(ctx, so) + if err != nil { + return nil, errors.Wrap(err, errFetchSecret) + } + + conn := managed.ConnectionDetails{} + for _, d := range t.ConnectionDetails { + switch tp := connectionDetailType(d); tp { + case v1.ConnectionDetailTypeFromConnectionSecretKey: + if d.FromConnectionSecretKey == nil { + return nil, errors.Errorf(errFmtConnDetailKey, tp) + } + if data == nil || data[*d.FromConnectionSecretKey] == nil { + // We don't consider this an error because it's possible the + // key will still be written at some point in the future. + continue + } + key := *d.FromConnectionSecretKey + if d.Name != nil { + key = *d.Name + } + if key != "" { + conn[key] = data[*d.FromConnectionSecretKey] + } + case v1.ConnectionDetailTypeFromFieldPath, v1.ConnectionDetailTypeFromValue, v1.ConnectionDetailTypeUnknown: + // We do nothing here with these cases, either: + // - ConnectionDetailTypeFromFieldPath,ConnectionDetailTypeFromValue + // => Already covered by APIConnectionDetailsFetcher.FetchConnectionDetails + // - ConnectionDetailTypeUnknown + // => We weren't able to determine the type of this connection detail. + } + } + + if len(conn) == 0 { + return nil, nil + } + + return conn, nil +} + +// NewSecretStoreConnectionDetailsConfigurator returns a Configurator that +// configures a composite resource using its composition. +func NewSecretStoreConnectionDetailsConfigurator(c client.Client) *SecretStoreConnectionDetailsConfigurator { + return &SecretStoreConnectionDetailsConfigurator{client: c} +} + +// A SecretStoreConnectionDetailsConfigurator configures a composite resource +// using its composition. +type SecretStoreConnectionDetailsConfigurator struct { + client client.Client +} + +// Configure any required fields that were omitted from the composite resource +// by copying them from its composition. +func (c *SecretStoreConnectionDetailsConfigurator) Configure(ctx context.Context, cp resource.Composite, comp *v1.Composition) error { + apiVersion, kind := cp.GetObjectKind().GroupVersionKind().ToAPIVersionAndKind() + if comp.Spec.CompositeTypeRef.APIVersion != apiVersion || comp.Spec.CompositeTypeRef.Kind != kind { + return errors.New(errCompositionNotCompatible) + } + + if cp.GetPublishConnectionDetailsTo() != nil || comp.Spec.PublishConnectionDetailsWithStoreConfigRef == nil { + return nil + } + + cp.SetPublishConnectionDetailsTo(&xpv1.PublishConnectionDetailsTo{ + Name: string(cp.GetUID()), + SecretStoreConfigRef: &xpv1.Reference{ + Name: comp.Spec.PublishConnectionDetailsWithStoreConfigRef.Name, + }, + }) + + return errors.Wrap(c.client.Update(ctx, cp), errUpdateComposite) +} diff --git a/internal/controller/apiextensions/composite/reconciler.go b/internal/controller/apiextensions/composite/reconciler.go index b8ac0eac0..8f576dc4a 100644 --- a/internal/controller/apiextensions/composite/reconciler.go +++ b/internal/controller/apiextensions/composite/reconciler.go @@ -44,22 +44,26 @@ import ( const ( timeout = 2 * time.Minute defaultPollInterval = 1 * time.Minute + finalizer = "composite.apiextensions.crossplane.io" ) // Error strings const ( - errGet = "cannot get composite resource" - errUpdate = "cannot update composite resource" - errUpdateStatus = "cannot update composite resource status" - errSelectComp = "cannot select Composition" - errFetchComp = "cannot fetch Composition" - errConfigure = "cannot configure composite resource" - errPublish = "cannot publish connection details" - errRenderCD = "cannot render composed resource" - errRenderCR = "cannot render composite resource" - errValidate = "refusing to use invalid Composition" - errInline = "cannot inline Composition patch sets" - errAssociate = "cannot associate composed resources with Composition resource templates" + errGet = "cannot get composite resource" + errUpdate = "cannot update composite resource" + errUpdateStatus = "cannot update composite resource status" + errAddFinalizer = "cannot add composite resource finalizer" + errRemoveFinalizer = "cannot remove composite resource finalizer" + errSelectComp = "cannot select Composition" + errFetchComp = "cannot fetch Composition" + errConfigure = "cannot configure composite resource" + errPublish = "cannot publish connection details" + errUnpublish = "cannot unpublish connection details" + errRenderCD = "cannot render composed resource" + errRenderCR = "cannot render composite resource" + errValidate = "refusing to use invalid Composition" + errInline = "cannot inline Composition patch sets" + errAssociate = "cannot associate composed resources with Composition resource templates" errFmtRender = "cannot render composed resource from resource template at index %d" ) @@ -69,6 +73,8 @@ const ( reasonResolve event.Reason = "SelectComposition" reasonCompose event.Reason = "ComposeResources" reasonPublish event.Reason = "PublishConnectionSecret" + reasonInit event.Reason = "InitializeCompositeResource" + reasonDelete event.Reason = "DeleteCompositeResource" ) // ControllerName returns the recommended name for controllers that use this @@ -82,26 +88,6 @@ type ConnectionSecretFilterer interface { GetConnectionSecretKeys() []string } -// A ConnectionPublisher publishes the supplied ConnectionDetails for the -// supplied resource. Publishers must handle the case in which the supplied -// ConnectionDetails are empty. -type ConnectionPublisher interface { - // PublishConnection details for the supplied resource. Publishing must be - // additive; i.e. if details (a, b, c) are published, subsequently - // publishing details (b, c, d) should update (b, c) but not remove a. - // Returns 'published' if the publish was not a no-op. - PublishConnection(ctx context.Context, o resource.ConnectionSecretOwner, c managed.ConnectionDetails) (published bool, err error) -} - -// A ConnectionPublisherFn publishes the supplied ConnectionDetails for the -// supplied resource. -type ConnectionPublisherFn func(ctx context.Context, o resource.ConnectionSecretOwner, c managed.ConnectionDetails) (published bool, err error) - -// PublishConnection details for the supplied resource. -func (fn ConnectionPublisherFn) PublishConnection(ctx context.Context, o resource.ConnectionSecretOwner, c managed.ConnectionDetails) (published bool, err error) { - return fn(ctx, o, c) -} - // A CompositionSelector selects a composition reference. type CompositionSelector interface { SelectComposition(ctx context.Context, cr resource.Composite) error @@ -157,11 +143,6 @@ func (fn RendererFn) Render(ctx context.Context, cp resource.Composite, cd resou return fn(ctx, cp, cd, t) } -// ConnectionDetailsFetcher fetches the connection details of the Composed resource. -type ConnectionDetailsFetcher interface { - FetchConnectionDetails(ctx context.Context, cd resource.Composed, t v1.ComposedTemplate) (managed.ConnectionDetails, error) -} - // A ConnectionDetailsFetcherFn fetches the connection details of the supplied // composed resource, if any. type ConnectionDetailsFetcherFn func(ctx context.Context, cd resource.Composed, t v1.ComposedTemplate) (managed.ConnectionDetails, error) @@ -267,6 +248,16 @@ func WithReadinessChecker(c ReadinessChecker) ReconcilerOption { } } +// WithCompositeFinalizer specifies how the composition to be used should be +// selected. +// WithCompositeFinalizer specifies which Finalizer should be used to finalize +// composites when they are deleted. +func WithCompositeFinalizer(f resource.Finalizer) ReconcilerOption { + return func(r *Reconciler) { + r.composite.Finalizer = f + } +} + // WithCompositionSelector specifies how the composition to be used should be // selected. func WithCompositionSelector(s CompositionSelector) ReconcilerOption { @@ -283,11 +274,11 @@ func WithConfigurator(c Configurator) ReconcilerOption { } } -// WithConnectionPublisher specifies how the Reconciler should publish +// WithConnectionPublishers specifies how the Reconciler should publish // connection secrets. -func WithConnectionPublisher(p ConnectionPublisher) ReconcilerOption { +func WithConnectionPublishers(p ...managed.ConnectionPublisher) ReconcilerOption { return func(r *Reconciler) { - r.composite.ConnectionPublisher = p + r.composite.ConnectionPublisher = managed.PublisherChain(p) } } @@ -305,10 +296,11 @@ type composition struct { } type compositeResource struct { + resource.Finalizer CompositionSelector Configurator - ConnectionPublisher Renderer + managed.ConnectionPublisher } type composedResource struct { @@ -338,6 +330,7 @@ func NewReconciler(mgr manager.Manager, of resource.CompositeKind, opts ...Recon }, composite: compositeResource{ + Finalizer: resource.NewAPIFinalizer(kube, finalizer), CompositionSelector: NewAPILabelSelectorResolver(kube), Configurator: NewConfiguratorChain(NewAPINamingConfigurator(kube), NewAPIConfigurator(kube)), ConnectionPublisher: NewAPIFilteredSecretPublisher(kube, []string{}), @@ -410,6 +403,34 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco "name", cr.GetName(), ) + if meta.WasDeleted(cr) { + log = log.WithValues("deletion-timestamp", cr.GetDeletionTimestamp()) + + if err := r.composite.UnpublishConnection(ctx, cr, nil); err != nil { + log.Debug(errUnpublish, "error", err) + err = errors.Wrap(err, errUnpublish) + r.record.Event(cr, event.Warning(reasonDelete, err)) + return reconcile.Result{}, err + } + + if err := r.composite.RemoveFinalizer(ctx, cr); err != nil { + log.Debug(errRemoveFinalizer, "error") + err = errors.Wrap(err, errRemoveFinalizer) + r.record.Event(cr, event.Warning(reasonDelete, err)) + return reconcile.Result{}, err + } + + log.Debug("Successfully deleted composite resource") + return reconcile.Result{Requeue: false}, nil + } + + if err := r.composite.AddFinalizer(ctx, cr); err != nil { + log.Debug(errAddFinalizer, "error") + err = errors.Wrap(err, errAddFinalizer) + r.record.Event(cr, event.Warning(reasonInit, err)) + return reconcile.Result{}, err + } + if err := r.composite.SelectComposition(ctx, cr); err != nil { log.Debug(errSelectComp, "error", err) err = errors.Wrap(err, errSelectComp) diff --git a/internal/controller/apiextensions/composite/reconciler_test.go b/internal/controller/apiextensions/composite/reconciler_test.go index 338030c3b..34c01079e 100644 --- a/internal/controller/apiextensions/composite/reconciler_test.go +++ b/internal/controller/apiextensions/composite/reconciler_test.go @@ -23,6 +23,7 @@ import ( "github.com/google/go-cmp/cmp" corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/utils/pointer" "sigs.k8s.io/controller-runtime/pkg/client" @@ -33,6 +34,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/crossplane-runtime/pkg/resource/fake" + "github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/composite" "github.com/crossplane/crossplane-runtime/pkg/test" v1 "github.com/crossplane/crossplane/apis/apiextensions/v1" @@ -89,6 +91,119 @@ func TestReconcile(t *testing.T) { err: errors.Wrap(errBoom, errGet), }, }, + "UnpublishConnectionError": { + reason: "We should return any error encountered while unpublishing connection details.", + args: args{ + mgr: &fake.Manager{}, + opts: []ReconcilerOption{ + WithClientApplicator(resource.ClientApplicator{ + Client: &test.MockClient{ + MockGet: test.NewMockGetFn(nil, func(obj client.Object) error { + if o, ok := obj.(*composite.Unstructured); ok { + now := metav1.Now() + o.SetDeletionTimestamp(&now) + } + return nil + }), + }, + }), + WithCompositeFinalizer(resource.NewNopFinalizer()), + WithConnectionPublishers(managed.ConnectionPublisherFns{ + UnpublishConnectionFn: func(ctx context.Context, o resource.ConnectionSecretOwner, c managed.ConnectionDetails) error { + return errBoom + }, + }), + }, + }, + want: want{ + err: errors.Wrap(errBoom, errUnpublish), + }, + }, + "RemoveFinalizerError": { + reason: "We should return any error encountered while removing finalizer.", + args: args{ + mgr: &fake.Manager{}, + opts: []ReconcilerOption{ + WithClientApplicator(resource.ClientApplicator{ + Client: &test.MockClient{ + MockGet: test.NewMockGetFn(nil, func(obj client.Object) error { + if o, ok := obj.(*composite.Unstructured); ok { + now := metav1.Now() + o.SetDeletionTimestamp(&now) + } + return nil + }), + }, + }), + WithCompositeFinalizer(resource.FinalizerFns{ + RemoveFinalizerFn: func(ctx context.Context, obj resource.Object) error { + return errBoom + }, + }), + WithConnectionPublishers(managed.ConnectionPublisherFns{ + UnpublishConnectionFn: func(ctx context.Context, o resource.ConnectionSecretOwner, c managed.ConnectionDetails) error { + return nil + }, + }), + }, + }, + want: want{ + err: errors.Wrap(errBoom, errRemoveFinalizer), + }, + }, + "SuccessfulDelete": { + reason: "We should return no error when deleted successfully.", + args: args{ + mgr: &fake.Manager{}, + opts: []ReconcilerOption{ + WithClientApplicator(resource.ClientApplicator{ + Client: &test.MockClient{ + MockGet: test.NewMockGetFn(nil, func(obj client.Object) error { + if o, ok := obj.(*composite.Unstructured); ok { + now := metav1.Now() + o.SetDeletionTimestamp(&now) + } + return nil + }), + }, + }), + WithCompositeFinalizer(resource.FinalizerFns{ + RemoveFinalizerFn: func(ctx context.Context, obj resource.Object) error { + return nil + }, + }), + WithConnectionPublishers(managed.ConnectionPublisherFns{ + UnpublishConnectionFn: func(ctx context.Context, o resource.ConnectionSecretOwner, c managed.ConnectionDetails) error { + return nil + }, + }), + }, + }, + want: want{ + err: nil, + }, + }, + "AddFinalizerError": { + reason: "We should return any error encountered while adding finalizer.", + args: args{ + mgr: &fake.Manager{}, + opts: []ReconcilerOption{ + WithClientApplicator(resource.ClientApplicator{ + Client: &test.MockClient{ + MockGet: test.NewMockGetFn(nil), + }, + }), + WithCompositeFinalizer(resource.FinalizerFns{ + AddFinalizerFn: func(ctx context.Context, obj resource.Object) error { + return errBoom + }, + }), + }, + }, + want: want{ + err: errors.Wrap(errBoom, errAddFinalizer), + }, + }, "SelectCompositionError": { reason: "We should return any error encountered while selecting a composition.", args: args{ @@ -99,6 +214,7 @@ func TestReconcile(t *testing.T) { MockGet: test.NewMockGetFn(nil), }, }), + WithCompositeFinalizer(resource.NewNopFinalizer()), WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, _ resource.Composite) error { return errBoom })), @@ -118,6 +234,7 @@ func TestReconcile(t *testing.T) { MockGet: test.NewMockGetFn(nil), }, }), + WithCompositeFinalizer(resource.NewNopFinalizer()), WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error { cr.SetCompositionReference(&corev1.ObjectReference{}) return nil @@ -141,6 +258,7 @@ func TestReconcile(t *testing.T) { MockGet: test.NewMockGetFn(nil), }, }), + WithCompositeFinalizer(resource.NewNopFinalizer()), WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error { cr.SetCompositionReference(&corev1.ObjectReference{}) return nil @@ -165,6 +283,7 @@ func TestReconcile(t *testing.T) { MockGet: test.NewMockGetFn(nil), }, }), + WithCompositeFinalizer(resource.NewNopFinalizer()), WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error { cr.SetCompositionReference(&corev1.ObjectReference{}) return nil @@ -195,6 +314,7 @@ func TestReconcile(t *testing.T) { return nil }), }), + WithCompositeFinalizer(resource.NewNopFinalizer()), WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error { cr.SetCompositionReference(&corev1.ObjectReference{}) return nil @@ -233,6 +353,7 @@ func TestReconcile(t *testing.T) { return nil }), }), + WithCompositeFinalizer(resource.NewNopFinalizer()), WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error { cr.SetCompositionReference(&corev1.ObjectReference{}) return nil @@ -264,6 +385,7 @@ func TestReconcile(t *testing.T) { MockUpdate: test.NewMockUpdateFn(errBoom), }, }), + WithCompositeFinalizer(resource.NewNopFinalizer()), WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error { cr.SetCompositionReference(&corev1.ObjectReference{}) return nil @@ -301,6 +423,7 @@ func TestReconcile(t *testing.T) { return errBoom }), }), + WithCompositeFinalizer(resource.NewNopFinalizer()), WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error { cr.SetCompositionReference(&corev1.ObjectReference{}) return nil @@ -338,6 +461,7 @@ func TestReconcile(t *testing.T) { return nil }), }), + WithCompositeFinalizer(resource.NewNopFinalizer()), WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error { cr.SetCompositionReference(&corev1.ObjectReference{}) return nil @@ -379,6 +503,7 @@ func TestReconcile(t *testing.T) { return nil }), }), + WithCompositeFinalizer(resource.NewNopFinalizer()), WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error { cr.SetCompositionReference(&corev1.ObjectReference{}) return nil @@ -423,6 +548,7 @@ func TestReconcile(t *testing.T) { return nil }), }), + WithCompositeFinalizer(resource.NewNopFinalizer()), WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error { cr.SetCompositionReference(&corev1.ObjectReference{}) return nil @@ -474,6 +600,7 @@ func TestReconcile(t *testing.T) { return nil }), }), + WithCompositeFinalizer(resource.NewNopFinalizer()), WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error { cr.SetCompositionReference(&corev1.ObjectReference{}) return nil @@ -529,6 +656,7 @@ func TestReconcile(t *testing.T) { return nil }), }), + WithCompositeFinalizer(resource.NewNopFinalizer()), WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error { cr.SetCompositionReference(&corev1.ObjectReference{}) return nil @@ -579,6 +707,7 @@ func TestReconcile(t *testing.T) { return nil }), }), + WithCompositeFinalizer(resource.NewNopFinalizer()), WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error { cr.SetCompositionReference(&corev1.ObjectReference{}) return nil @@ -590,9 +719,11 @@ func TestReconcile(t *testing.T) { WithConfigurator(ConfiguratorFn(func(_ context.Context, _ resource.Composite, _ *v1.Composition) error { return nil })), - WithConnectionPublisher(ConnectionPublisherFn(func(ctx context.Context, o resource.ConnectionSecretOwner, c managed.ConnectionDetails) (published bool, err error) { - return false, errBoom - })), + WithConnectionPublishers(managed.ConnectionPublisherFns{ + PublishConnectionFn: func(ctx context.Context, o resource.ConnectionSecretOwner, c managed.ConnectionDetails) (published bool, err error) { + return false, errBoom + }, + }), }, }, want: want{ @@ -614,6 +745,7 @@ func TestReconcile(t *testing.T) { return nil }), }), + WithCompositeFinalizer(resource.NewNopFinalizer()), WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error { cr.SetCompositionReference(&corev1.ObjectReference{}) return nil @@ -638,9 +770,11 @@ func TestReconcile(t *testing.T) { // Our one resource is not ready. return false, nil })), - WithConnectionPublisher(ConnectionPublisherFn(func(ctx context.Context, o resource.ConnectionSecretOwner, c managed.ConnectionDetails) (published bool, err error) { - return false, nil - })), + WithConnectionPublishers(managed.ConnectionPublisherFns{ + PublishConnectionFn: func(ctx context.Context, o resource.ConnectionSecretOwner, c managed.ConnectionDetails) (published bool, err error) { + return false, nil + }, + }), }, }, want: want{ @@ -662,6 +796,7 @@ func TestReconcile(t *testing.T) { return nil }), }), + WithCompositeFinalizer(resource.NewNopFinalizer()), WithCompositionSelector(CompositionSelectorFn(func(_ context.Context, cr resource.Composite) error { cr.SetCompositionReference(&corev1.ObjectReference{}) return nil @@ -686,13 +821,15 @@ func TestReconcile(t *testing.T) { // Our one resource is ready. return true, nil })), - WithConnectionPublisher(ConnectionPublisherFn(func(ctx context.Context, o resource.ConnectionSecretOwner, got managed.ConnectionDetails) (published bool, err error) { - want := cd - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("PublishConnection(...): -want, +got:\n%s", diff) - } - return true, nil - })), + WithConnectionPublishers(managed.ConnectionPublisherFns{ + PublishConnectionFn: func(ctx context.Context, o resource.ConnectionSecretOwner, got managed.ConnectionDetails) (published bool, err error) { + want := cd + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("PublishConnection(...): -want, +got:\n%s", diff) + } + return true, nil + }, + }), }, }, want: want{ diff --git a/internal/controller/apiextensions/composite/revision.go b/internal/controller/apiextensions/composite/revision.go index 20444ee51..a51960a10 100644 --- a/internal/controller/apiextensions/composite/revision.go +++ b/internal/controller/apiextensions/composite/revision.go @@ -41,6 +41,7 @@ func AsCompositionSpec(crs v1alpha1.CompositionRevisionSpec) v1.CompositionSpec PatchSets: make([]v1.PatchSet, len(crs.PatchSets)), Resources: make([]v1.ComposedTemplate, len(crs.Resources)), WriteConnectionSecretsToNamespace: crs.WriteConnectionSecretsToNamespace, + PublishConnectionDetailsWithStoreConfigRef: crs.PublishConnectionDetailsWithStoreConfigRef, } for i := range crs.PatchSets { diff --git a/internal/controller/apiextensions/composition/revision.go b/internal/controller/apiextensions/composition/revision.go index 06d15e8cd..b70d659c7 100644 --- a/internal/controller/apiextensions/composition/revision.go +++ b/internal/controller/apiextensions/composition/revision.go @@ -58,6 +58,7 @@ func NewCompositionRevisionSpec(cs v1.CompositionSpec, revision int64) v1alpha1. PatchSets: make([]v1alpha1.PatchSet, len(cs.PatchSets)), Resources: make([]v1alpha1.ComposedTemplate, len(cs.Resources)), WriteConnectionSecretsToNamespace: cs.WriteConnectionSecretsToNamespace, + PublishConnectionDetailsWithStoreConfigRef: cs.PublishConnectionDetailsWithStoreConfigRef, } for i := range cs.PatchSets { diff --git a/internal/controller/apiextensions/definition/reconciler.go b/internal/controller/apiextensions/definition/reconciler.go index a49bed80d..8ba8dd5fe 100644 --- a/internal/controller/apiextensions/definition/reconciler.go +++ b/internal/controller/apiextensions/definition/reconciler.go @@ -33,16 +33,19 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/crossplane/crossplane-runtime/pkg/connection" "github.com/crossplane/crossplane-runtime/pkg/controller" "github.com/crossplane/crossplane-runtime/pkg/errors" "github.com/crossplane/crossplane-runtime/pkg/event" "github.com/crossplane/crossplane-runtime/pkg/logging" "github.com/crossplane/crossplane-runtime/pkg/meta" "github.com/crossplane/crossplane-runtime/pkg/ratelimiter" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/crossplane-runtime/pkg/resource/unstructured" v1 "github.com/crossplane/crossplane/apis/apiextensions/v1" + "github.com/crossplane/crossplane/apis/secrets/v1alpha1" "github.com/crossplane/crossplane/internal/controller/apiextensions/composite" "github.com/crossplane/crossplane/internal/features" "github.com/crossplane/crossplane/internal/xcrd" @@ -403,7 +406,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco recorder := r.record.WithAnnotations("controller", composite.ControllerName(d.GetName())) o := []composite.ReconcilerOption{ - composite.WithConnectionPublisher(composite.NewAPIFilteredSecretPublisher(r.client, d.GetConnectionSecretKeys())), + composite.WithConnectionPublishers(composite.NewAPIFilteredSecretPublisher(r.client, d.GetConnectionSecretKeys())), composite.WithCompositionSelector(composite.NewCompositionSelectorChain( composite.NewEnforcedCompositionSelector(*d, recorder), composite.NewAPIDefaultCompositionSelector(r.client, *meta.ReferenceTo(d, v1.CompositeResourceDefinitionGroupVersionKind), recorder), @@ -421,6 +424,33 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco o = append(o, composite.WithCompositionFetcher(composite.NewAPIRevisionFetcher(a))) } + // We only want to enable ExternalSecretStore support if the relevant + // feature flag is enabled. Otherwise, we start the XR reconcilers with + // their default ConnectionPublisher and ConnectionDetailsFetcher. + // We also add a new Configurator for ExternalSecretStore which basically + // reflects PublishConnectionDetailsWithStoreConfigRef in Composition to + // the composite resource. + if r.options.Features.Enabled(features.EnableAlphaExternalSecretStores) { + pc := []managed.ConnectionPublisher{ + composite.NewAPIFilteredSecretPublisher(r.client, d.GetConnectionSecretKeys()), + composite.NewSecretStoreConnectionPublisher(connection.NewDetailsManager(r.client, v1alpha1.StoreConfigGroupVersionKind), d.GetConnectionSecretKeys()), + } + o = append(o, composite.WithConnectionPublishers(pc...)) + + fc := composite.ConnectionDetailsFetcherChain{ + composite.NewAPIConnectionDetailsFetcher(r.client), + composite.NewSecretStoreConnectionDetailsFetcher(connection.NewDetailsManager(r.client, v1alpha1.StoreConfigGroupVersionKind)), + } + o = append(o, composite.WithConnectionDetailsFetcher(fc)) + + cc := composite.NewConfiguratorChain( + composite.NewAPINamingConfigurator(r.client), + composite.NewAPIConfigurator(r.client), + composite.NewSecretStoreConnectionDetailsConfigurator(r.client), + ) + o = append(o, composite.WithConfigurator(cc)) + } + cr := composite.NewReconciler(r.mgr, resource.CompositeKind(d.GetCompositeGroupVersionKind()), o...) ko := r.options.ForControllerRuntime() ko.Reconciler = ratelimiter.NewReconciler(composite.ControllerName(d.GetName()), cr, r.options.GlobalRateLimiter) diff --git a/internal/controller/apiextensions/offered/reconciler.go b/internal/controller/apiextensions/offered/reconciler.go index 218b83eee..80405f929 100644 --- a/internal/controller/apiextensions/offered/reconciler.go +++ b/internal/controller/apiextensions/offered/reconciler.go @@ -32,6 +32,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/crossplane/crossplane-runtime/pkg/connection" "github.com/crossplane/crossplane-runtime/pkg/controller" "github.com/crossplane/crossplane-runtime/pkg/errors" "github.com/crossplane/crossplane-runtime/pkg/event" @@ -42,7 +43,9 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource/unstructured" v1 "github.com/crossplane/crossplane/apis/apiextensions/v1" + secretsv1alpha1 "github.com/crossplane/crossplane/apis/secrets/v1alpha1" "github.com/crossplane/crossplane/internal/controller/apiextensions/claim" + "github.com/crossplane/crossplane/internal/features" "github.com/crossplane/crossplane/internal/xcrd" ) @@ -388,11 +391,26 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco return reconcile.Result{Requeue: true}, nil } + o := []claim.ReconcilerOption{ + claim.WithLogger(log.WithValues("controller", claim.ControllerName(d.GetName()))), + claim.WithRecorder(r.record.WithAnnotations("controller", claim.ControllerName(d.GetName()))), + } + + // We only want to enable ExternalSecretStore support if the relevant + // feature flag is enabled. Otherwise, we start the Claim reconcilers with + // their default Connection Propagator. + if r.options.Features.Enabled(features.EnableAlphaExternalSecretStores) { + pc := claim.ConnectionPropagatorChain{ + claim.NewAPIConnectionPropagator(r.client), + connection.NewDetailsManager(r.client, secretsv1alpha1.StoreConfigGroupVersionKind), + } + + o = append(o, claim.WithConnectionPropagator(pc), claim.WithConnectionUnpublisher(claim.NewSecretStoreConnectionUnpublisher(connection.NewDetailsManager(r.client, secretsv1alpha1.StoreConfigGroupVersionKind)))) + } + cr := claim.NewReconciler(r.mgr, resource.CompositeClaimKind(d.GetClaimGroupVersionKind()), - resource.CompositeKind(d.GetCompositeGroupVersionKind()), - claim.WithLogger(log.WithValues("controller", claim.ControllerName(d.GetName()))), - claim.WithRecorder(r.record.WithAnnotations("controller", claim.ControllerName(d.GetName())))) + resource.CompositeKind(d.GetCompositeGroupVersionKind()), o...) ko := r.options.ForControllerRuntime() ko.Reconciler = ratelimiter.NewReconciler(claim.ControllerName(d.GetName()), cr, r.options.GlobalRateLimiter) diff --git a/internal/controller/pkg/controller/options.go b/internal/controller/pkg/controller/options.go index 057c5ebb6..81b9016fe 100644 --- a/internal/controller/pkg/controller/options.go +++ b/internal/controller/pkg/controller/options.go @@ -21,6 +21,7 @@ import ( "github.com/crossplane/crossplane/internal/xpkg" "github.com/crossplane/crossplane-runtime/pkg/controller" + "github.com/crossplane/crossplane-runtime/pkg/feature" ) // Options specific to pkg controllers. @@ -28,7 +29,7 @@ type Options struct { controller.Options // Cache for package OCI images. - Cache xpkg.Cache + Cache xpkg.PackageCache // Namespace used to unpack and run packages. Namespace string @@ -39,4 +40,12 @@ type Options struct { // FetcherOptions can be used to add optional parameters to // NewK8sFetcher. FetcherOptions []xpkg.FetcherOpt + + // WebhookTLSSecretName is the Secret that will be mounted to provider Pods + // so that they can use it to serve webhooks and also the CA bundle will be + // injected to CRDs so that API server can make calls to the providers. + WebhookTLSSecretName string + + // Features that should be enabled. + Features *feature.Flags } diff --git a/internal/controller/pkg/manager/reconciler.go b/internal/controller/pkg/manager/reconciler.go index af3f878b5..995ac8329 100644 --- a/internal/controller/pkg/manager/reconciler.go +++ b/internal/controller/pkg/manager/reconciler.go @@ -81,6 +81,14 @@ const ( // ReconcilerOption is used to configure the Reconciler. type ReconcilerOption func(*Reconciler) +// WithWebhookTLSSecretName configures the name of the webhook TLS Secret that +// Reconciler will add to PackageRevisions it creates. +func WithWebhookTLSSecretName(n string) ReconcilerOption { + return func(r *Reconciler) { + r.webhookTLSSecretName = &n + } +} + // WithNewPackageFn determines the type of package being reconciled. func WithNewPackageFn(f func() v1.Package) ReconcilerOption { return func(r *Reconciler) { @@ -126,10 +134,11 @@ func WithRecorder(er event.Recorder) ReconcilerOption { // Reconciler reconciles packages. type Reconciler struct { - client resource.ClientApplicator - pkg Revisioner - log logging.Logger - record event.Recorder + client resource.ClientApplicator + pkg Revisioner + log logging.Logger + record event.Recorder + webhookTLSSecretName *string newPackage func() v1.Package newPackageRevision func() v1.PackageRevision @@ -152,21 +161,23 @@ func SetupProvider(mgr ctrl.Manager, o controller.Options) error { return errors.Wrap(err, errBuildFetcher) } - r := NewReconciler(mgr, + opts := []ReconcilerOption{ WithNewPackageFn(np), WithNewPackageRevisionFn(nr), WithNewPackageRevisionListFn(nrl), WithRevisioner(NewPackageRevisioner(f, WithDefaultRegistry(o.DefaultRegistry))), WithLogger(o.Logger.WithValues("controller", name)), WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))), - ) - + } + if o.WebhookTLSSecretName != "" { + opts = append(opts, WithWebhookTLSSecretName(o.WebhookTLSSecretName)) + } return ctrl.NewControllerManagedBy(mgr). Named(name). For(&v1.Provider{}). Owns(&v1.ProviderRevision{}). WithOptions(o.ForControllerRuntime()). - Complete(ratelimiter.NewReconciler(name, r, o.GlobalRateLimiter)) + Complete(ratelimiter.NewReconciler(name, NewReconciler(mgr, opts...), o.GlobalRateLimiter)) } // SetupConfiguration adds a controller that reconciles Configurations. @@ -354,6 +365,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco pr.SetIgnoreCrossplaneConstraints(p.GetIgnoreCrossplaneConstraints()) pr.SetSkipDependencyResolution(p.GetSkipDependencyResolution()) pr.SetControllerConfigRef(p.GetControllerConfigRef()) + pr.SetWebhookTLSSecretName(r.webhookTLSSecretName) // If current revision is not active and we have an automatic or // undefined activation policy, always activate. diff --git a/internal/controller/pkg/revision/deployment.go b/internal/controller/pkg/revision/deployment.go index db5cd25cc..52dff0ed7 100644 --- a/internal/controller/pkg/revision/deployment.go +++ b/internal/controller/pkg/revision/deployment.go @@ -20,8 +20,10 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" "github.com/crossplane/crossplane-runtime/pkg/meta" + pkgmetav1 "github.com/crossplane/crossplane/apis/pkg/meta/v1" v1 "github.com/crossplane/crossplane/apis/pkg/v1" "github.com/crossplane/crossplane/apis/pkg/v1alpha1" @@ -41,9 +43,15 @@ var ( const ( promPortName = "metrics" promPortNumber = 8080 + + webhookVolumeName = "webhook-tls-secret" + webhookTLSCertDirEnvVar = "WEBHOOK_TLS_CERT_DIR" + webhookTLSCertDir = "/webhook/tls" + webhookPortName = "webhook" + webhookPort = 9443 ) -func buildProviderDeployment(provider *pkgmetav1.Provider, revision v1.PackageRevision, cc *v1alpha1.ControllerConfig, namespace string) (*corev1.ServiceAccount, *appsv1.Deployment) { // nolint:interfacer,gocyclo +func buildProviderDeployment(provider *pkgmetav1.Provider, revision v1.PackageRevision, cc *v1alpha1.ControllerConfig, namespace string) (*corev1.ServiceAccount, *appsv1.Deployment, *corev1.Service) { // nolint:gocyclo s := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: revision.GetName(), @@ -104,12 +112,63 @@ func buildProviderDeployment(provider *pkgmetav1.Provider, revision v1.PackageRe ContainerPort: promPortNumber, }, }, + Env: []corev1.EnvVar{ + { + // NOTE(turkenh): POD_NAMESPACE is needed to + // set a default scope/namespace of the + // default StoreConfig, similar to init + // container of Core Crossplane. + Name: "POD_NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.namespace", + }, + }, + }, + }, }, }, }, }, }, } + if revision.GetWebhookTLSSecretName() != nil { + v := corev1.Volume{ + Name: webhookVolumeName, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: *revision.GetWebhookTLSSecretName(), + Items: []corev1.KeyToPath{ + // These are known and validated keys in TLS secrets. + {Key: "tls.crt", Path: "tls.crt"}, + {Key: "tls.key", Path: "tls.key"}, + }, + }, + }, + } + d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, v) + + vm := corev1.VolumeMount{ + Name: webhookVolumeName, + ReadOnly: true, + MountPath: webhookTLSCertDir, + } + d.Spec.Template.Spec.Containers[0].VolumeMounts = + append(d.Spec.Template.Spec.Containers[0].VolumeMounts, vm) + + envs := []corev1.EnvVar{ + {Name: webhookTLSCertDirEnvVar, Value: webhookTLSCertDir}, + } + d.Spec.Template.Spec.Containers[0].Env = + append(d.Spec.Template.Spec.Containers[0].Env, envs...) + + port := corev1.ContainerPort{ + Name: webhookPortName, + ContainerPort: webhookPort, + } + d.Spec.Template.Spec.Containers[0].Ports = append(d.Spec.Template.Spec.Containers[0].Ports, + port) + } templateLabels := make(map[string]string) if cc != nil { s.Labels = cc.Labels @@ -178,7 +237,10 @@ func buildProviderDeployment(provider *pkgmetav1.Provider, revision v1.PackageRe d.Spec.Template.Spec.Containers[0].EnvFrom = cc.Spec.EnvFrom } if len(cc.Spec.Env) > 0 { - d.Spec.Template.Spec.Containers[0].Env = cc.Spec.Env + // We already have some environment variables that we will always + // want to set (e.g. POD_NAMESPACE), so we just append the new ones + // that user provided if there are any. + d.Spec.Template.Spec.Containers[0].Env = append(d.Spec.Template.Spec.Containers[0].Env, cc.Spec.Env...) } } for k, v := range d.Spec.Selector.MatchLabels { // ensure the template matches the selector @@ -186,5 +248,24 @@ func buildProviderDeployment(provider *pkgmetav1.Provider, revision v1.PackageRe } d.Spec.Template.Labels = templateLabels - return s, d + svc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: revision.GetName(), + Namespace: namespace, + OwnerReferences: []metav1.OwnerReference{meta.AsController(meta.TypedReferenceTo(revision, v1.ProviderRevisionGroupVersionKind))}, + }, + Spec: corev1.ServiceSpec{ + // We use whatever is on the deployment so that ControllerConfig + // overrides are accounted for. + Selector: d.Spec.Selector.MatchLabels, + Ports: []corev1.ServicePort{ + { + Protocol: corev1.ProtocolTCP, + Port: 9443, + TargetPort: intstr.FromInt(9443), + }, + }, + }, + } + return s, d, svc } diff --git a/internal/controller/pkg/revision/deployment_test.go b/internal/controller/pkg/revision/deployment_test.go index 566204e01..80c796002 100644 --- a/internal/controller/pkg/revision/deployment_test.go +++ b/internal/controller/pkg/revision/deployment_test.go @@ -24,6 +24,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" @@ -40,10 +41,67 @@ func withPodTemplateLabels(labels map[string]string) deploymentModifier { } } +func withAdditionalVolume(v corev1.Volume) deploymentModifier { + return func(d *appsv1.Deployment) { + d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, v) + } +} + +func withAdditionalVolumeMount(vm corev1.VolumeMount) deploymentModifier { + return func(d *appsv1.Deployment) { + d.Spec.Template.Spec.Containers[0].VolumeMounts = append(d.Spec.Template.Spec.Containers[0].VolumeMounts, vm) + } +} + +func withAdditionalEnvVar(env corev1.EnvVar) deploymentModifier { + return func(d *appsv1.Deployment) { + d.Spec.Template.Spec.Containers[0].Env = append(d.Spec.Template.Spec.Containers[0].Env, env) + } +} + +func withAdditionalPort(port corev1.ContainerPort) deploymentModifier { + return func(d *appsv1.Deployment) { + d.Spec.Template.Spec.Containers[0].Ports = append(d.Spec.Template.Spec.Containers[0].Ports, port) + } +} + const ( namespace = "ns" ) +func serviceaccount(rev v1.PackageRevision) *corev1.ServiceAccount { + return &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: rev.GetName(), + Namespace: namespace, + }, + } +} + +func service(provider *pkgmetav1.Provider, rev v1.PackageRevision) *corev1.Service { + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: rev.GetName(), + Namespace: namespace, + }, + Spec: corev1.ServiceSpec{ + // We use whatever is on the deployment so that ControllerConfig + // overrides are accounted for. + Selector: map[string]string{ + "pkg.crossplane.io/revision": rev.GetName(), + "pkg.crossplane.io/provider": provider.GetName(), + }, + Ports: []corev1.ServicePort{ + { + Protocol: corev1.ProtocolTCP, + Port: 9443, + TargetPort: intstr.FromInt(9443), + }, + }, + }, + } +} + func deployment(provider *pkgmetav1.Provider, revision string, img string, modifiers ...deploymentModifier) *appsv1.Deployment { var ( replicas = int32(1) @@ -84,6 +142,16 @@ func deployment(provider *pkgmetav1.Provider, revision string, img string, modif ContainerPort: promPortNumber, }, }, + Env: []corev1.EnvVar{ + { + Name: "POD_NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.namespace", + }, + }, + }, + }, }, }, }, @@ -99,15 +167,21 @@ func deployment(provider *pkgmetav1.Provider, revision string, img string, modif } func TestBuildProviderDeployment(t *testing.T) { - type fields struct { + type args struct { provider *pkgmetav1.Provider revision *v1.ProviderRevision cc *v1alpha1.ControllerConfig } + type want struct { + sa *corev1.ServiceAccount + d *appsv1.Deployment + svc *corev1.Service + } img := "img:tag" pkgImg := "pkg-img:tag" ccImg := "cc-img:tag" + webhookTLSSecretName := "secret-name" providerWithoutImage := &pkgmetav1.Provider{ ObjectMeta: metav1.ObjectMeta{ @@ -140,6 +214,18 @@ func TestBuildProviderDeployment(t *testing.T) { }, } + revisionWithoutCCWithWebhook := &v1.ProviderRevision{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rev-123", + }, + Spec: v1.PackageRevisionSpec{ + ControllerConfigReference: nil, + Package: pkgImg, + Revision: 3, + WebhookTLSSecretName: &webhookTLSSecretName, + }, + } + revisionWithCC := &v1.ProviderRevision{ ObjectMeta: metav1.ObjectMeta{ Name: "rev-123", @@ -167,47 +253,98 @@ func TestBuildProviderDeployment(t *testing.T) { cases := map[string]struct { reason string - fields fields - want *appsv1.Deployment + fields args + want want }{ "NoImgNoCC": { reason: "If the meta provider does not specify a controller image and no ControllerConfig is referenced, the package image itself should be used.", - fields: fields{ + fields: args{ provider: providerWithoutImage, revision: revisionWithoutCC, cc: nil, }, - want: deployment(providerWithoutImage, revisionWithCC.GetName(), pkgImg), + want: want{ + sa: serviceaccount(revisionWithoutCC), + d: deployment(providerWithoutImage, revisionWithCC.GetName(), pkgImg), + svc: service(providerWithoutImage, revisionWithoutCC), + }, + }, + "ImgNoCCWithWebhookTLS": { + reason: "If the webhook tls secret name is given, then the deployment should be configured to serve behind the given service.", + fields: args{ + provider: providerWithImage, + revision: revisionWithoutCCWithWebhook, + cc: nil, + }, + want: want{ + sa: serviceaccount(revisionWithoutCCWithWebhook), + d: deployment(providerWithImage, revisionWithoutCCWithWebhook.GetName(), img, + withAdditionalVolume(corev1.Volume{ + Name: webhookVolumeName, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: webhookTLSSecretName, + Items: []corev1.KeyToPath{ + {Key: "tls.crt", Path: "tls.crt"}, + {Key: "tls.key", Path: "tls.key"}, + }, + }, + }, + }), + withAdditionalVolumeMount(corev1.VolumeMount{ + Name: webhookVolumeName, + ReadOnly: true, + MountPath: webhookTLSCertDir, + }), + withAdditionalEnvVar(corev1.EnvVar{Name: webhookTLSCertDirEnvVar, Value: webhookTLSCertDir}), + withAdditionalPort(corev1.ContainerPort{Name: webhookPortName, ContainerPort: webhookPort}), + ), + svc: service(providerWithImage, revisionWithoutCCWithWebhook), + }, }, "ImgNoCC": { reason: "If the meta provider specifies a controller image and no ControllerConfig is reference, the specified image should be used.", - fields: fields{ + fields: args{ provider: providerWithImage, revision: revisionWithoutCC, cc: nil, }, - want: deployment(providerWithoutImage, revisionWithCC.GetName(), img), + want: want{ + sa: serviceaccount(revisionWithoutCC), + d: deployment(providerWithoutImage, revisionWithoutCC.GetName(), img), + svc: service(providerWithoutImage, revisionWithoutCC), + }, }, "ImgCC": { reason: "If a ControllerConfig is referenced and it species a controller image it should always be used.", - fields: fields{ + fields: args{ provider: providerWithImage, revision: revisionWithCC, cc: cc, }, - want: deployment(providerWithImage, revisionWithCC.GetName(), ccImg, withPodTemplateLabels(map[string]string{ - "pkg.crossplane.io/revision": revisionWithCC.GetName(), - "pkg.crossplane.io/provider": providerWithImage.GetName(), - "k": "v", - })), + want: want{ + sa: serviceaccount(revisionWithCC), + d: deployment(providerWithImage, revisionWithCC.GetName(), ccImg, withPodTemplateLabels(map[string]string{ + "pkg.crossplane.io/revision": revisionWithCC.GetName(), + "pkg.crossplane.io/provider": providerWithImage.GetName(), + "k": "v", + })), + svc: service(providerWithImage, revisionWithCC), + }, }, } for name, tc := range cases { t.Run(name, func(t *testing.T) { - _, deployment := buildProviderDeployment(tc.fields.provider, tc.fields.revision, tc.fields.cc, namespace) + sa, d, svc := buildProviderDeployment(tc.fields.provider, tc.fields.revision, tc.fields.cc, namespace) - if diff := cmp.Diff(tc.want, deployment, cmpopts.IgnoreTypes(&corev1.SecurityContext{}, &corev1.PodSecurityContext{}, []metav1.OwnerReference{})); diff != "" { + if diff := cmp.Diff(tc.want.sa, sa, cmpopts.IgnoreTypes([]metav1.OwnerReference{})); diff != "" { + t.Errorf("-want, +got:\n%s\n", diff) + } + if diff := cmp.Diff(tc.want.d, d, cmpopts.IgnoreTypes(&corev1.SecurityContext{}, &corev1.PodSecurityContext{}, []metav1.OwnerReference{})); diff != "" { + t.Errorf("-want, +got:\n%s\n", diff) + } + if diff := cmp.Diff(tc.want.svc, svc, cmpopts.IgnoreTypes([]metav1.OwnerReference{})); diff != "" { t.Errorf("-want, +got:\n%s\n", diff) } }) diff --git a/internal/controller/pkg/revision/establisher.go b/internal/controller/pkg/revision/establisher.go index fe5b5fd83..274b5b2fe 100644 --- a/internal/controller/pkg/revision/establisher.go +++ b/internal/controller/pkg/revision/establisher.go @@ -18,7 +18,12 @@ package revision import ( "context" + "fmt" + "strings" + admv1 "k8s.io/api/admissionregistration/v1" + corev1 "k8s.io/api/core/v1" + extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -35,27 +40,45 @@ import ( ) const ( - errAssertResourceObj = "cannot assert object to resource.Object" - errAssertClientObj = "cannot assert object to client.Object" + errAssertResourceObj = "cannot assert object to resource.Object" + errAssertClientObj = "cannot assert object to client.Object" + errConversionWithNoWebhookCA = "cannot deploy a CRD with webhook conversion strategy without having a TLS bundle" + errGetWebhookTLSSecret = "cannot get webhook tls secret" + errWebhookSecretWithoutCABundle = "the value for the key tls.crt cannot be empty" ) // An Establisher establishes control or ownership of a set of resources in the // API server by checking that control or ownership can be established for all // resources and then establishing it. type Establisher interface { - Establish(ctx context.Context, objects []runtime.Object, parent resource.Object, control bool) ([]xpv1.TypedReference, error) + Establish(ctx context.Context, objects []runtime.Object, parent v1.PackageRevision, control bool) ([]xpv1.TypedReference, error) +} + +// NewNopEstablisher returns a new NopEstablisher. +func NewNopEstablisher() *NopEstablisher { + return &NopEstablisher{} +} + +// NopEstablisher does nothing. +type NopEstablisher struct{} + +// Establish does nothing. +func (*NopEstablisher) Establish(_ context.Context, _ []runtime.Object, _ v1.PackageRevision, _ bool) ([]xpv1.TypedReference, error) { + return nil, nil } // APIEstablisher establishes control or ownership of resources in the API // server for a parent. type APIEstablisher struct { - client client.Client + client client.Client + namespace string } // NewAPIEstablisher creates a new APIEstablisher. -func NewAPIEstablisher(client client.Client) *APIEstablisher { +func NewAPIEstablisher(client client.Client, namespace string) *APIEstablisher { return &APIEstablisher{ - client: client, + client: client, + namespace: namespace, } } @@ -70,9 +93,21 @@ type currentDesired struct { // Establish checks that control or ownership of resources can be established by // parent, then establishes it. -func (e *APIEstablisher) Establish(ctx context.Context, objs []runtime.Object, parent resource.Object, control bool) ([]xpv1.TypedReference, error) { // nolint:gocyclo +func (e *APIEstablisher) Establish(ctx context.Context, objs []runtime.Object, parent v1.PackageRevision, control bool) ([]xpv1.TypedReference, error) { // nolint:gocyclo allObjs := []currentDesired{} resourceRefs := []xpv1.TypedReference{} + var webhookTLSCert []byte + if parent.GetWebhookTLSSecretName() != nil { + s := &corev1.Secret{} + nn := types.NamespacedName{Name: *parent.GetWebhookTLSSecretName(), Namespace: e.namespace} + if err := e.client.Get(ctx, nn, s); err != nil { + return nil, errors.Wrap(err, errGetWebhookTLSSecret) + } + if len(s.Data["tls.crt"]) == 0 { + return nil, errors.New(errWebhookSecretWithoutCABundle) + } + webhookTLSCert = s.Data["tls.crt"] + } for _, res := range objs { // Assert desired object to resource.Object so that we can access its // metadata. @@ -81,6 +116,66 @@ func (e *APIEstablisher) Establish(ctx context.Context, objs []runtime.Object, p return nil, errors.New(errAssertResourceObj) } + // The generated webhook configurations have a static hard-coded name + // that the developers of the providers can't affect. Here, we make sure + // to distinguish one from the other by setting the name to the parent + // since there is always a single ValidatingWebhookConfiguration and/or + // single MutatingWebhookConfiguration object in a provider package. + // See https://github.com/kubernetes-sigs/controller-tools/issues/658 + switch conf := res.(type) { + case *admv1.ValidatingWebhookConfiguration: + if len(webhookTLSCert) == 0 { + continue + } + if pkgRef, ok := GetPackageOwnerReference(parent); ok { + conf.SetName(fmt.Sprintf("crossplane-%s-%s", strings.ToLower(pkgRef.Kind), pkgRef.Name)) + } + for i := range conf.Webhooks { + conf.Webhooks[i].ClientConfig.CABundle = webhookTLSCert + if conf.Webhooks[i].ClientConfig.Service == nil { + conf.Webhooks[i].ClientConfig.Service = &admv1.ServiceReference{} + } + conf.Webhooks[i].ClientConfig.Service.Name = parent.GetName() + conf.Webhooks[i].ClientConfig.Service.Namespace = e.namespace + conf.Webhooks[i].ClientConfig.Service.Port = pointer.Int32(webhookPort) + } + case *admv1.MutatingWebhookConfiguration: + if len(webhookTLSCert) == 0 { + continue + } + if pkgRef, ok := GetPackageOwnerReference(parent); ok { + conf.SetName(fmt.Sprintf("crossplane-%s-%s", strings.ToLower(pkgRef.Kind), pkgRef.Name)) + } + for i := range conf.Webhooks { + conf.Webhooks[i].ClientConfig.CABundle = webhookTLSCert + if conf.Webhooks[i].ClientConfig.Service == nil { + conf.Webhooks[i].ClientConfig.Service = &admv1.ServiceReference{} + } + conf.Webhooks[i].ClientConfig.Service.Name = parent.GetName() + conf.Webhooks[i].ClientConfig.Service.Namespace = e.namespace + conf.Webhooks[i].ClientConfig.Service.Port = pointer.Int32(webhookPort) + } + case *extv1.CustomResourceDefinition: + if conf.Spec.Conversion != nil && conf.Spec.Conversion.Strategy == extv1.WebhookConverter { + if len(webhookTLSCert) == 0 { + return nil, errors.New(errConversionWithNoWebhookCA) + } + if conf.Spec.Conversion.Webhook == nil { + conf.Spec.Conversion.Webhook = &extv1.WebhookConversion{} + } + if conf.Spec.Conversion.Webhook.ClientConfig == nil { + conf.Spec.Conversion.Webhook.ClientConfig = &extv1.WebhookClientConfig{} + } + if conf.Spec.Conversion.Webhook.ClientConfig.Service == nil { + conf.Spec.Conversion.Webhook.ClientConfig.Service = &extv1.ServiceReference{} + } + conf.Spec.Conversion.Webhook.ClientConfig.CABundle = webhookTLSCert + conf.Spec.Conversion.Webhook.ClientConfig.Service.Name = parent.GetName() + conf.Spec.Conversion.Webhook.ClientConfig.Service.Namespace = e.namespace + conf.Spec.Conversion.Webhook.ClientConfig.Service.Port = pointer.Int32(webhookPort) + } + } + // Make a copy of the desired object to be populated with existing // object, if it exists. copy := res.DeepCopyObject() diff --git a/internal/controller/pkg/revision/establisher_test.go b/internal/controller/pkg/revision/establisher_test.go index b5a5a6c18..8de62b779 100644 --- a/internal/controller/pkg/revision/establisher_test.go +++ b/internal/controller/pkg/revision/establisher_test.go @@ -21,12 +21,15 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" + admv1 "k8s.io/api/admissionregistration/v1" + corev1 "k8s.io/api/core/v1" + extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" "github.com/crossplane/crossplane-runtime/pkg/errors" @@ -39,11 +42,13 @@ var _ Establisher = &APIEstablisher{} func TestAPIEstablisherEstablish(t *testing.T) { errBoom := errors.New("boom") + webhookTLSSecretName := "webhook-tls" + caBundle := []byte("CABUNDLE") type args struct { est *APIEstablisher objs []runtime.Object - parent resource.Object + parent v1.PackageRevision control bool } @@ -67,7 +72,7 @@ func TestAPIEstablisherEstablish(t *testing.T) { }, }, objs: []runtime.Object{ - &apiextensions.CustomResourceDefinition{ + &extv1.CustomResourceDefinition{ ObjectMeta: metav1.ObjectMeta{ Name: "ref-me", }, @@ -102,7 +107,7 @@ func TestAPIEstablisherEstablish(t *testing.T) { }, }, objs: []runtime.Object{ - &apiextensions.CustomResourceDefinition{ + &extv1.CustomResourceDefinition{ ObjectMeta: metav1.ObjectMeta{ Name: "ref-me", }, @@ -127,6 +132,87 @@ func TestAPIEstablisherEstablish(t *testing.T) { refs: []xpv1.TypedReference{{Name: "ref-me"}}, }, }, + "SuccessfulNotExistsEstablishControlWebhookEnabled": { + reason: "Establishment should be successful if we can establish control for a parent of new objects in case webhooks are enabled.", + args: args{ + est: &APIEstablisher{ + client: &test.MockClient{ + MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error { + if s, ok := obj.(*corev1.Secret); ok { + (&corev1.Secret{ + Data: map[string][]byte{ + "tls.crt": caBundle, + }, + }).DeepCopyInto(s) + return nil + } + return kerrors.NewNotFound(schema.GroupResource{}, "") + }, + MockCreate: test.NewMockCreateFn(nil), + }, + }, + objs: []runtime.Object{ + &extv1.CustomResourceDefinition{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ref-me", + }, + Spec: extv1.CustomResourceDefinitionSpec{ + Conversion: &extv1.CustomResourceConversion{ + Strategy: extv1.WebhookConverter, + }, + }, + }, + &admv1.MutatingWebhookConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "crossplane-providerrevision-provider-name", + }, + Webhooks: []admv1.MutatingWebhook{ + { + Name: "some-webhook", + }, + }, + }, + &admv1.ValidatingWebhookConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "crossplane-providerrevision-provider-name", + }, + Webhooks: []admv1.ValidatingWebhook{ + { + Name: "some-webhook", + }, + }, + }, + }, + parent: &v1.ProviderRevision{ + TypeMeta: metav1.TypeMeta{ + Kind: "ProviderRevision", + }, + ObjectMeta: metav1.ObjectMeta{ + OwnerReferences: []metav1.OwnerReference{ + { + Kind: "Provider", + Name: "provider-name", + UID: "some-unique-uid-2312", + }, + }, + Labels: map[string]string{ + v1.LabelParentPackage: "provider-name", + }, + }, + Spec: v1.PackageRevisionSpec{ + WebhookTLSSecretName: &webhookTLSSecretName, + }, + }, + control: true, + }, + want: want{ + refs: []xpv1.TypedReference{ + {Name: "ref-me"}, + {Name: "crossplane-provider-provider-name"}, + {Name: "crossplane-provider-provider-name"}, + }, + }, + }, "SuccessfulExistsEstablishOwnership": { reason: "Establishment should be successful if we can establish ownership for a parent of existing objects.", args: args{ @@ -137,7 +223,7 @@ func TestAPIEstablisherEstablish(t *testing.T) { }, }, objs: []runtime.Object{ - &apiextensions.CustomResourceDefinition{ + &extv1.CustomResourceDefinition{ ObjectMeta: metav1.ObjectMeta{ Name: "ref-me", }, @@ -160,7 +246,7 @@ func TestAPIEstablisherEstablish(t *testing.T) { }, }, objs: []runtime.Object{ - &apiextensions.CustomResourceDefinition{ + &extv1.CustomResourceDefinition{ ObjectMeta: metav1.ObjectMeta{ Name: "ref-me", }, @@ -173,6 +259,86 @@ func TestAPIEstablisherEstablish(t *testing.T) { refs: []xpv1.TypedReference{{Name: "ref-me"}}, }, }, + "FailedCreationWebhookDisabledConversionRequested": { + reason: "Establishment should fail if the CRD requires conversion webhook and Crossplane does not have the webhooks enabled.", + args: args{ + est: &APIEstablisher{ + client: &test.MockClient{ + MockGet: test.NewMockGetFn(kerrors.NewNotFound(schema.GroupResource{}, "")), + MockCreate: test.NewMockCreateFn(nil), + }, + }, + objs: []runtime.Object{ + &extv1.CustomResourceDefinition{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ref-me", + }, + Spec: extv1.CustomResourceDefinitionSpec{ + Conversion: &extv1.CustomResourceConversion{ + Strategy: extv1.WebhookConverter, + }, + }, + }, + }, + parent: &v1.ProviderRevision{ + ObjectMeta: metav1.ObjectMeta{ + OwnerReferences: []metav1.OwnerReference{ + { + Name: "provider-name", + UID: "some-unique-uid-2312", + }, + }, + Labels: map[string]string{ + v1.LabelParentPackage: "provider-name", + }, + }, + }, + control: true, + }, + want: want{ + err: errors.New(errConversionWithNoWebhookCA), + }, + }, + "FailedGettingWebhookTLSSecret": { + reason: "Establishment should fail if a webhook TLS secret is given but cannot be fetched", + args: args{ + est: &APIEstablisher{ + client: &test.MockClient{ + MockGet: test.NewMockGetFn(errBoom), + }, + }, + parent: &v1.ProviderRevision{ + Spec: v1.PackageRevisionSpec{ + WebhookTLSSecretName: &webhookTLSSecretName, + }, + }, + }, + want: want{ + err: errors.Wrap(errBoom, errGetWebhookTLSSecret), + }, + }, + "FailedEmptyWebhookTLSSecret": { + reason: "Establishment should fail if a webhook TLS secret is given but empty", + args: args{ + est: &APIEstablisher{ + client: &test.MockClient{ + MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error { + s := &corev1.Secret{} + s.DeepCopyInto(obj.(*corev1.Secret)) + return nil + }, + }, + }, + parent: &v1.ProviderRevision{ + Spec: v1.PackageRevisionSpec{ + WebhookTLSSecretName: &webhookTLSSecretName, + }, + }, + }, + want: want{ + err: errors.New(errWebhookSecretWithoutCABundle), + }, + }, "FailedCreate": { reason: "Cannot establish control of object if we cannot create it.", args: args{ @@ -183,7 +349,7 @@ func TestAPIEstablisherEstablish(t *testing.T) { }, }, objs: []runtime.Object{ - &apiextensions.CustomResourceDefinition{ + &extv1.CustomResourceDefinition{ ObjectMeta: metav1.ObjectMeta{ Name: "ref-me", }, @@ -210,7 +376,7 @@ func TestAPIEstablisherEstablish(t *testing.T) { }, }, objs: []runtime.Object{ - &apiextensions.CustomResourceDefinition{ + &extv1.CustomResourceDefinition{ ObjectMeta: metav1.ObjectMeta{ Name: "ref-me", }, diff --git a/internal/controller/pkg/revision/hook.go b/internal/controller/pkg/revision/hook.go index a17bf89bc..e855c43c7 100644 --- a/internal/controller/pkg/revision/hook.go +++ b/internal/controller/pkg/revision/hook.go @@ -40,8 +40,10 @@ const ( errControllerConfig = "cannot get referenced controller config" errDeleteProviderDeployment = "cannot delete provider package deployment" errDeleteProviderSA = "cannot delete provider package service account" + errDeleteProviderService = "cannot delete provider package service" errApplyProviderDeployment = "cannot apply provider package deployment" errApplyProviderSA = "cannot apply provider package service account" + errApplyProviderService = "cannot apply provider package service" errUnavailableProviderDeployment = "provider package deployment is unavailable" ) @@ -95,19 +97,22 @@ func (h *ProviderHooks) Pre(ctx context.Context, pkg runtime.Object, pr v1.Packa if err != nil { return errors.Wrap(err, errControllerConfig) } - s, d := buildProviderDeployment(pkgProvider, pr, cc, h.namespace) + s, d, svc := buildProviderDeployment(pkgProvider, pr, cc, h.namespace) if err := h.client.Delete(ctx, d); resource.IgnoreNotFound(err) != nil { return errors.Wrap(err, errDeleteProviderDeployment) } if err := h.client.Delete(ctx, s); resource.IgnoreNotFound(err) != nil { return errors.Wrap(err, errDeleteProviderSA) } + if err := h.client.Delete(ctx, svc); resource.IgnoreNotFound(err) != nil { + return errors.Wrap(err, errDeleteProviderService) + } return nil } // Post creates a packaged provider controller and service account if the // revision is active. -func (h *ProviderHooks) Post(ctx context.Context, pkg runtime.Object, pr v1.PackageRevision) error { +func (h *ProviderHooks) Post(ctx context.Context, pkg runtime.Object, pr v1.PackageRevision) error { // nolint:gocyclo po, _ := xpkg.TryConvert(pkg, &pkgmetav1.Provider{}) pkgProvider, ok := po.(*pkgmetav1.Provider) if !ok { @@ -120,13 +125,18 @@ func (h *ProviderHooks) Post(ctx context.Context, pkg runtime.Object, pr v1.Pack if err != nil { return errors.Wrap(err, errControllerConfig) } - s, d := buildProviderDeployment(pkgProvider, pr, cc, h.namespace) + s, d, svc := buildProviderDeployment(pkgProvider, pr, cc, h.namespace) if err := h.client.Apply(ctx, s); err != nil { return errors.Wrap(err, errApplyProviderSA) } if err := h.client.Apply(ctx, d); err != nil { return errors.Wrap(err, errApplyProviderDeployment) } + if pr.GetWebhookTLSSecretName() != nil { + if err := h.client.Apply(ctx, svc); err != nil { + return errors.Wrap(err, errApplyProviderService) + } + } pr.SetControllerReference(xpv1.Reference{Name: d.GetName()}) for _, c := range d.Status.Conditions { diff --git a/internal/controller/pkg/revision/hook_test.go b/internal/controller/pkg/revision/hook_test.go index e6ae75a28..73e73c932 100644 --- a/internal/controller/pkg/revision/hook_test.go +++ b/internal/controller/pkg/revision/hook_test.go @@ -26,6 +26,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" "github.com/crossplane/crossplane-runtime/pkg/errors" "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/crossplane-runtime/pkg/test" @@ -366,6 +367,41 @@ func TestHookPost(t *testing.T) { err: errors.Wrap(errBoom, errApplyProviderSA), }, }, + "ErrProviderGetControllerConfigDeployment": { + reason: "Should return error if we fail to get controller config for active provider revision.", + args: args{ + hook: &ProviderHooks{ + client: resource.ClientApplicator{ + Applicator: resource.ApplyFn(func(_ context.Context, o client.Object, _ ...resource.ApplyOption) error { + return nil + }), + Client: &test.MockClient{ + MockGet: test.NewMockGetFn(errBoom), + }, + }, + }, + pkg: &pkgmetav1.Provider{}, + rev: &v1.ProviderRevision{ + Spec: v1.PackageRevisionSpec{ + ControllerConfigReference: &xpv1.Reference{ + Name: "custom-config", + }, + DesiredState: v1.PackageRevisionActive, + }, + }, + }, + want: want{ + rev: &v1.ProviderRevision{ + Spec: v1.PackageRevisionSpec{ + DesiredState: v1.PackageRevisionActive, + ControllerConfigReference: &xpv1.Reference{ + Name: "custom-config", + }, + }, + }, + err: errors.Wrap(errors.Wrap(errBoom, errControllerConfig), errControllerConfig), + }, + }, "ErrProviderApplyDeployment": { reason: "Should return error if we fail to apply deployment for active provider revision.", args: args{ diff --git a/internal/controller/pkg/revision/imageback.go b/internal/controller/pkg/revision/imageback.go index 460d1d060..d194719b0 100644 --- a/internal/controller/pkg/revision/imageback.go +++ b/internal/controller/pkg/revision/imageback.go @@ -22,10 +22,7 @@ import ( "io" "github.com/google/go-containerregistry/pkg/name" - regv1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/mutate" - "github.com/spf13/afero/tarfs" - corev1 "k8s.io/api/core/v1" "github.com/crossplane/crossplane-runtime/pkg/errors" "github.com/crossplane/crossplane-runtime/pkg/parser" @@ -35,17 +32,23 @@ import ( ) const ( - errPullPolicyNever = "failed to get pre-cached package with pull policy Never" - errBadReference = "package tag is not a valid reference" - errFetchPackage = "failed to fetch package from remote" - errCachePackage = "failed to store package in cache" - errOpenPackageStream = "failed to open package stream file" + errBadReference = "package tag is not a valid reference" + errFetchPackage = "failed to fetch package from remote" + errGetManifest = "failed to get package image manifest from remote" + errFetchLayer = "failed to fetch annotated base layer from remote" + errGetUncompressed = "failed to get uncompressed contents from layer" + errMultipleAnnotatedLayers = "package is invalid due to multiple annotated base layers" + errOpenPackageStream = "failed to open package stream file" +) + +const ( + layerAnnotation = "io.crossplane.xpkg" + baseAnnotationValue = "base" ) // ImageBackend is a backend for parser. type ImageBackend struct { registry string - cache xpkg.Cache fetcher xpkg.Fetcher } @@ -60,9 +63,8 @@ func WithDefaultRegistry(registry string) ImageBackendOption { } // NewImageBackend creates a new image backend. -func NewImageBackend(cache xpkg.Cache, fetcher xpkg.Fetcher, opts ...ImageBackendOption) *ImageBackend { +func NewImageBackend(fetcher xpkg.Fetcher, opts ...ImageBackendOption) *ImageBackend { i := &ImageBackend{ - cache: cache, fetcher: fetcher, } for _, opt := range opts { @@ -72,7 +74,8 @@ func NewImageBackend(cache xpkg.Cache, fetcher xpkg.Fetcher, opts ...ImageBacken } // Init initializes an ImageBackend. -func (i *ImageBackend) Init(ctx context.Context, bo ...parser.BackendOption) (io.ReadCloser, error) { +// NOTE(hasheddan): this method is slightly over our cyclomatic complexity goal +func (i *ImageBackend) Init(ctx context.Context, bo ...parser.BackendOption) (io.ReadCloser, error) { //nolint:gocyclo // NOTE(hasheddan): we use nestedBackend here because simultaneous // reconciles of providers or configurations can lead to the package // revision being overwritten mid-execution in the shared image backend when @@ -85,45 +88,70 @@ func (i *ImageBackend) Init(ctx context.Context, bo ...parser.BackendOption) (io for _, o := range bo { o(n) } - var img regv1.Image - var err error - - pullPolicy := n.pr.GetPackagePullPolicy() - if pullPolicy != nil && *pullPolicy == corev1.PullNever { - // If package is pre-cached we assume there are never multiple tags in - // the same image. - img, err = i.cache.Get("", n.pr.GetSource()) - if err != nil { - return nil, errors.Wrap(err, errPullPolicyNever) + ref, err := name.ParseReference(n.pr.GetSource(), name.WithDefaultRegistry(i.registry)) + if err != nil { + return nil, errors.Wrap(err, errBadReference) + } + // Fetch image from registry. + img, err := i.fetcher.Fetch(ctx, ref, v1.RefNames(n.pr.GetPackagePullSecrets())...) + if err != nil { + return nil, errors.Wrap(err, errFetchPackage) + } + // Get image manifest. + manifest, err := img.Manifest() + if err != nil { + return nil, errors.Wrap(err, errGetManifest) + } + // Determine if the image is using annotated layers. + var tarc io.ReadCloser + foundAnnotated := false + for _, l := range manifest.Layers { + if a, ok := l.Annotations[layerAnnotation]; !ok || a != baseAnnotationValue { + continue + } + // NOTE(hasheddan): the xpkg specification dictates that only one layer + // descriptor may be annotated as xpkg base. Since iterating through all + // descriptors is relatively inexpensive, we opt to do so in order to + // verify that we aren't just using the first layer annotated as xpkg + // base. + if foundAnnotated { + return nil, errors.New(errMultipleAnnotatedLayers) } - } else { - // Ensure source is a valid image reference. - ref, err := name.ParseReference(n.pr.GetSource(), name.WithDefaultRegistry(i.registry)) + foundAnnotated = true + layer, err := img.LayerByDigest(l.Digest) if err != nil { - return nil, errors.Wrap(err, errBadReference) + return nil, errors.Wrap(err, errFetchLayer) } - // Attempt to fetch image from cache. - img, err = i.cache.Get(n.pr.GetSource(), n.pr.GetName()) + tarc, err = layer.Uncompressed() if err != nil { - img, err = i.fetcher.Fetch(ctx, ref, v1.RefNames(n.pr.GetPackagePullSecrets())...) - if err != nil { - return nil, errors.Wrap(err, errFetchPackage) - } - // Cache image. - if err := i.cache.Store(n.pr.GetSource(), n.pr.GetName(), img); err != nil { - return nil, errors.Wrap(err, errCachePackage) - } + return nil, errors.Wrap(err, errGetUncompressed) } } - // Extract package contents from image. - r := mutate.Extract(img) - fs := tarfs.New(tar.NewReader(r)) - f, err := fs.Open(xpkg.StreamFile) - if err != nil { - return nil, errors.Wrap(err, errOpenPackageStream) + // If we still don't have content then we need to flatten image filesystem. + if !foundAnnotated { + tarc = mutate.Extract(img) } - return f, nil + + // The ReadCloser is an uncompressed tarball, either consisting of annotated + // layer contents or flattened filesystem content. Either way, we only want + // the package YAML stream. + t := tar.NewReader(tarc) + for { + h, err := t.Next() + if err != nil { + return nil, errors.Wrap(err, errOpenPackageStream) + } + if h.Name == xpkg.StreamFile { + break + } + } + + // NOTE(hasheddan): we return a JoinedReadCloser such that closing will free + // resources allocated to the underlying ReadCloser. See + // https://github.com/google/go-containerregistry/blob/329563766ce8131011c25fd8758a25d94d9ad81b/pkg/v1/mutate/mutate.go#L222 + // for more info. + return xpkg.JoinedReadCloser(t, tarc), nil } // nestedBackend is a nop parser backend that conforms to the parser backend diff --git a/internal/controller/pkg/revision/imageback_test.go b/internal/controller/pkg/revision/imageback_test.go index a4c87f597..5d3db208b 100644 --- a/internal/controller/pkg/revision/imageback_test.go +++ b/internal/controller/pkg/revision/imageback_test.go @@ -21,9 +21,7 @@ import ( "bytes" "context" "io" - "os" "strings" - "syscall" "testing" "github.com/google/go-cmp/cmp" @@ -32,7 +30,6 @@ import ( "github.com/google/go-containerregistry/pkg/v1/random" "github.com/google/go-containerregistry/pkg/v1/tarball" "github.com/google/go-containerregistry/pkg/v1/types" - corev1 "k8s.io/api/core/v1" "github.com/crossplane/crossplane-runtime/pkg/errors" "github.com/crossplane/crossplane-runtime/pkg/parser" @@ -44,9 +41,20 @@ import ( func TestImageBackend(t *testing.T) { errBoom := errors.New("boom") - pullPolicy := corev1.PullNever randLayer, _ := random.Layer(int64(1000), types.DockerLayer) - randImg, _ := mutate.AppendLayers(empty.Image, randLayer) + randImg, _ := mutate.Append(empty.Image, mutate.Addendum{ + Layer: randLayer, + Annotations: map[string]string{ + layerAnnotation: baseAnnotationValue, + }, + }) + + randImgDup, _ := mutate.Append(randImg, mutate.Addendum{ + Layer: randLayer, + Annotations: map[string]string{ + layerAnnotation: baseAnnotationValue, + }, + }) streamCont := "somestreamofyaml" tarBuf := new(bytes.Buffer) @@ -59,11 +67,14 @@ func TestImageBackend(t *testing.T) { _ = tw.WriteHeader(hdr) _, _ = io.Copy(tw, strings.NewReader(streamCont)) _ = tw.Close() - packLayer, _ := tarball.LayerFromReader(tarBuf) + packLayer, _ := tarball.LayerFromOpener(func() (io.ReadCloser, error) { + // NOTE(hasheddan): we must construct a new reader each time as we + // ingest packImg in multiple tests below. + return io.NopCloser(bytes.NewReader(tarBuf.Bytes())), nil + }) packImg, _ := mutate.AppendLayers(empty.Image, packLayer) type args struct { - c xpkg.Cache f xpkg.Fetcher opts []parser.BackendOption } @@ -84,12 +95,11 @@ func TestImageBackend(t *testing.T) { }, want: errors.Wrap(errors.New("could not parse reference: :test"), errBadReference), }, - "ErrFetchedBadPackage": { - reason: "Should return error if image with contents does not have package.yaml.", + "ErrMultipleAnnotatedLayers": { + reason: "Should return error if image has multiple layers annotated as base.", args: args{ - c: xpkg.NewNopCache(), f: &fake.MockFetcher{ - MockFetch: fake.NewMockFetchFn(randImg, nil), + MockFetch: fake.NewMockFetchFn(randImgDup, nil), }, opts: []parser.BackendOption{PackageRevision(&v1.ProviderRevision{ Spec: v1.PackageRevisionSpec{ @@ -97,14 +107,13 @@ func TestImageBackend(t *testing.T) { }, })}, }, - want: errors.Wrap(&os.PathError{Op: "open", Path: xpkg.StreamFile, Err: syscall.ENOENT}, errOpenPackageStream), + want: errors.New(errMultipleAnnotatedLayers), }, - "ErrEmptyImage": { - reason: "Should return error if image is empty.", + "ErrFetchedBadPackage": { + reason: "Should return error if image with contents does not have package.yaml.", args: args{ - c: xpkg.NewNopCache(), f: &fake.MockFetcher{ - MockFetch: fake.NewMockFetchFn(empty.Image, nil), + MockFetch: fake.NewMockFetchFn(randImg, nil), }, opts: []parser.BackendOption{PackageRevision(&v1.ProviderRevision{ Spec: v1.PackageRevisionSpec{ @@ -112,14 +121,13 @@ func TestImageBackend(t *testing.T) { }, })}, }, - want: errors.Wrap(&os.PathError{Op: "open", Path: "package.yaml", Err: syscall.ENOENT}, errOpenPackageStream), + want: errors.Wrap(io.EOF, errOpenPackageStream), }, - "ErrFetchPackage": { - reason: "Should return error if package is not in cache and we fail to fetch it.", + "ErrEmptyImage": { + reason: "Should return error if image is empty.", args: args{ - c: xpkg.NewNopCache(), f: &fake.MockFetcher{ - MockFetch: fake.NewMockFetchFn(nil, errBoom), + MockFetch: fake.NewMockFetchFn(empty.Image, nil), }, opts: []parser.BackendOption{PackageRevision(&v1.ProviderRevision{ Spec: v1.PackageRevisionSpec{ @@ -127,17 +135,13 @@ func TestImageBackend(t *testing.T) { }, })}, }, - want: errors.Wrap(errBoom, errFetchPackage), + want: errors.Wrap(io.EOF, errOpenPackageStream), }, - "ErrStorePackage": { - reason: "Should return error if package is not in cache, we fetch successfully, but we fail to store it in cache.", + "ErrFetchPackage": { + reason: "Should return error if package is not in cache and we fail to fetch it.", args: args{ - c: &fake.MockCache{ - MockGet: fake.NewMockCacheGetFn(nil, errBoom), - MockStore: fake.NewMockCacheStoreFn(errBoom), - }, f: &fake.MockFetcher{ - MockFetch: fake.NewMockFetchFn(packImg, nil), + MockFetch: fake.NewMockFetchFn(nil, errBoom), }, opts: []parser.BackendOption{PackageRevision(&v1.ProviderRevision{ Spec: v1.PackageRevisionSpec{ @@ -145,12 +149,11 @@ func TestImageBackend(t *testing.T) { }, })}, }, - want: errors.Wrap(errBoom, errCachePackage), + want: errors.Wrap(errBoom, errFetchPackage), }, "SuccessFetchPackage": { reason: "Should not return error is package is not in cache but is fetched successfully.", args: args{ - c: xpkg.NewNopCache(), f: &fake.MockFetcher{ MockFetch: fake.NewMockFetchFn(packImg, nil), }, @@ -161,55 +164,15 @@ func TestImageBackend(t *testing.T) { })}, }, }, - "SuccessCachedPackage": { - reason: "Should not return error is package is in cache and is gotten successfully.", - args: args{ - c: &fake.MockCache{ - MockGet: fake.NewMockCacheGetFn(packImg, nil), - }, - opts: []parser.BackendOption{PackageRevision(&v1.ProviderRevision{ - Spec: v1.PackageRevisionSpec{ - Package: "test/test:latest", - }, - })}, - }, - }, - "ErrorCachedPackageNoPull": { - reason: "Should return error if package is pre-cached and is not gotten successfully.", - args: args{ - c: &fake.MockCache{ - MockGet: fake.NewMockCacheGetFn(nil, errBoom), - }, - opts: []parser.BackendOption{PackageRevision(&v1.ProviderRevision{ - Spec: v1.PackageRevisionSpec{ - Package: "test/test:latest", - PackagePullPolicy: &pullPolicy, - }, - })}, - }, - want: errors.Wrap(errBoom, errPullPolicyNever), - }, - "SuccessCachedPackageNoPull": { - reason: "Should not return error if package is pre-cached and is gotten successfully.", - args: args{ - c: &fake.MockCache{ - MockGet: fake.NewMockCacheGetFn(packImg, nil), - }, - opts: []parser.BackendOption{PackageRevision(&v1.ProviderRevision{ - Spec: v1.PackageRevisionSpec{ - Package: "test/test:latest", - PackagePullPolicy: &pullPolicy, - }, - })}, - }, - }, } for name, tc := range cases { t.Run(name, func(t *testing.T) { - b := NewImageBackend(tc.args.c, tc.args.f) - _, err := b.Init(context.TODO(), tc.args.opts...) - + b := NewImageBackend(tc.args.f) + rc, err := b.Init(context.TODO(), tc.args.opts...) + if err == nil && rc != nil { + _, err = io.ReadAll(rc) + } if diff := cmp.Diff(tc.want, err, test.EquateErrors()); diff != "" { t.Errorf("\n%s\nb.Init(...): -want error, +got error:\n%s", tc.reason, diff) } diff --git a/internal/controller/pkg/revision/reconciler.go b/internal/controller/pkg/revision/reconciler.go index b485c9d2f..a445130a6 100644 --- a/internal/controller/pkg/revision/reconciler.go +++ b/internal/controller/pkg/revision/reconciler.go @@ -18,10 +18,12 @@ package revision import ( "context" + "io" "strings" "time" appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" @@ -58,7 +60,10 @@ const ( errGetPackageRevision = "cannot get package revision" errUpdateStatus = "cannot update package revision status" - errDeleteCache = "cannot remove package image from cache" + errDeleteCache = "cannot remove package contents from cache" + errGetCache = "cannot get package contents from cache" + + errPullPolicyNever = "failed to get pre-cached package with pull policy Never" errAddFinalizer = "cannot add package revision finalizer" errRemoveFinalizer = "cannot remove package revision finalizer" @@ -100,7 +105,7 @@ func WithClientApplicator(ca resource.ClientApplicator) ReconcilerOption { } // WithCache specifies how the Reconcile should cache package contents. -func WithCache(c xpkg.Cache) ReconcilerOption { +func WithCache(c xpkg.PackageCache) ReconcilerOption { return func(r *Reconciler) { r.cache = c } @@ -188,7 +193,7 @@ func WithVersioner(v version.Operations) ReconcilerOption { // Reconciler reconciles packages. type Reconciler struct { client client.Client - cache xpkg.Cache + cache xpkg.PackageCache revision resource.Finalizer lock DependencyManager hook Hooks @@ -233,9 +238,10 @@ func SetupProviderRevision(mgr ctrl.Manager, o controller.Options) error { Client: mgr.GetClient(), Applicator: resource.NewAPIPatchingApplicator(mgr.GetClient()), }, o.Namespace)), + WithEstablisher(NewAPIEstablisher(mgr.GetClient(), o.Namespace)), WithNewPackageRevisionFn(nr), WithParser(parser.New(metaScheme, objScheme)), - WithParserBackend(NewImageBackend(o.Cache, fetcher, WithDefaultRegistry(o.DefaultRegistry))), + WithParserBackend(NewImageBackend(fetcher, WithDefaultRegistry(o.DefaultRegistry))), WithLinter(xpkg.NewProviderLinter()), WithLogger(o.Logger.WithValues("controller", name)), WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))), @@ -280,8 +286,9 @@ func SetupConfigurationRevision(mgr ctrl.Manager, o controller.Options) error { WithDependencyManager(NewPackageDependencyManager(mgr.GetClient(), dag.NewMapDag, v1beta1.ConfigurationPackageType)), WithHooks(NewConfigurationHooks()), WithNewPackageRevisionFn(nr), + WithEstablisher(NewAPIEstablisher(mgr.GetClient(), o.Namespace)), WithParser(parser.New(metaScheme, objScheme)), - WithParserBackend(NewImageBackend(o.Cache, f, WithDefaultRegistry(o.DefaultRegistry))), + WithParserBackend(NewImageBackend(f, WithDefaultRegistry(o.DefaultRegistry))), WithLinter(xpkg.NewConfigurationLinter()), WithLogger(o.Logger.WithValues("controller", name)), WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))), @@ -302,7 +309,7 @@ func NewReconciler(mgr manager.Manager, opts ...ReconcilerOption) *Reconciler { cache: xpkg.NewNopCache(), revision: resource.NewAPIFinalizer(mgr.GetClient(), finalizer), hook: NewNopHooks(), - objects: NewAPIEstablisher(mgr.GetClient()), + objects: NewNopEstablisher(), parser: parser.New(nil, nil), linter: parser.NewPackageLinter(nil, nil, nil), versioner: version.New(), @@ -386,22 +393,87 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco // TODO(negz): Use Unhealthy().WithMessage(...) to supply error context? - // Initialize parser backend to obtain package contents. - reader, err := r.backend.Init(ctx, PackageRevision(pr)) - if err != nil { - pr.SetConditions(v1.Unhealthy()) - _ = r.client.Status().Update(ctx, pr) + pullPolicyNever := false + id := pr.GetName() + // If packagePullPolicy is Never, the identifier is the package source and + // contents must be in the cache. + if pr.GetPackagePullPolicy() != nil && *pr.GetPackagePullPolicy() == corev1.PullNever { + pullPolicyNever = true + id = pr.GetSource() + } + + var rc io.ReadCloser + cacheWrite := make(chan error) - // Requeue because we may be waiting for parent package - // controller to recreate Pod. - log.Debug(errInitParserBackend, "error", err) - err = errors.Wrap(err, errInitParserBackend) + if r.cache.Has(id) { + var err error + rc, err = r.cache.Get(id) + if err != nil { + // If package contents are in the cache, but we cannot access them, + // we clear them and try again. + if err := r.cache.Delete(id); err != nil { + log.Debug(errDeleteCache, "error", err) + } + log.Debug(errInitParserBackend, "error", err) + err = errors.Wrap(err, errGetCache) + r.record.Event(pr, event.Warning(reasonParse, err)) + return reconcile.Result{}, err + } + // If we got content from cache we don't need to wait for it to be + // written. + close(cacheWrite) + } + + // packagePullPolicy is Never and contents are not in the cache so we return + // an error. + if rc == nil && pullPolicyNever { + log.Debug(errPullPolicyNever) + err := errors.New(errPullPolicyNever) r.record.Event(pr, event.Warning(reasonParse, err)) return reconcile.Result{}, err } + // If we didn't get a ReadCloser from cache, we need to get it from image. + if rc == nil { + // Initialize parser backend to obtain package contents. + imgrc, err := r.backend.Init(ctx, PackageRevision(pr)) + if err != nil { + pr.SetConditions(v1.Unhealthy()) + _ = r.client.Status().Update(ctx, pr) + + // Requeue because we may be waiting for parent package + // controller to recreate Pod. + log.Debug(errInitParserBackend, "error", err) + err = errors.Wrap(err, errInitParserBackend) + r.record.Event(pr, event.Warning(reasonParse, err)) + return reconcile.Result{}, err + } + + // Package is not in cache, so we write it to the cache while parsing. + pipeR, pipeW := io.Pipe() + rc = xpkg.TeeReadCloser(imgrc, pipeW) + go func() { + defer pipeR.Close() //nolint:errcheck + if err := r.cache.Store(pr.GetName(), pipeR); err != nil { + _ = pipeR.CloseWithError(err) + cacheWrite <- err + return + } + close(cacheWrite) + }() + } + // Parse package contents. - pkg, err := r.parser.Parse(ctx, reader) + pkg, err := r.parser.Parse(ctx, rc) + // Wait until we finish writing to cache. Parser closes the reader. + if err := <-cacheWrite; err != nil { + // If we failed to cache we want to cleanup, but we don't abort unless + // parsing also failed. Subsequent reconciles will pull image again and + // attempt to cache. + if err := r.cache.Delete(id); err != nil { + log.Debug(errDeleteCache, "error", err) + } + } if err != nil { pr.SetConditions(v1.Unhealthy()) _ = r.client.Status().Update(ctx, pr) diff --git a/internal/controller/pkg/revision/reconciler_test.go b/internal/controller/pkg/revision/reconciler_test.go index 5b65cee07..826d091d1 100644 --- a/internal/controller/pkg/revision/reconciler_test.go +++ b/internal/controller/pkg/revision/reconciler_test.go @@ -17,11 +17,13 @@ limitations under the License. package revision import ( + "bytes" "context" "io" "testing" "github.com/google/go-cmp/cmp" + corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -69,7 +71,7 @@ func NewMockEstablishFn(refs []xpv1.TypedReference, err error) func() ([]xpv1.Ty return func() ([]xpv1.TypedReference, error) { return refs, err } } -func (e *MockEstablisher) Establish(context.Context, []runtime.Object, resource.Object, bool) ([]xpv1.TypedReference, error) { +func (e *MockEstablisher) Establish(context.Context, []runtime.Object, v1.PackageRevision, bool) ([]xpv1.TypedReference, error) { return e.MockEstablish() } @@ -152,6 +154,7 @@ spec: func TestReconcile(t *testing.T) { errBoom := errors.New("boom") now := metav1.Now() + pullPolicy := corev1.PullNever trueVal := true metaScheme, _ := xpkg.BuildMetaScheme() @@ -346,6 +349,136 @@ func TestReconcile(t *testing.T) { err: errors.Wrap(errBoom, errAddFinalizer), }, }, + "ErrGetFromCacheSuccessfulDelete": { + reason: "We should return an error if package content is in cache, we cannot get it, but we remove it successfully.", + args: args{ + mgr: &fake.Manager{}, + req: reconcile.Request{NamespacedName: types.NamespacedName{Name: "test"}}, + rec: []ReconcilerOption{ + WithNewPackageRevisionFn(func() v1.PackageRevision { return &v1.ConfigurationRevision{} }), + WithClientApplicator(resource.ClientApplicator{ + Client: &test.MockClient{ + MockGet: test.NewMockGetFn(nil, func(o client.Object) error { + pr := o.(*v1.ConfigurationRevision) + pr.SetGroupVersionKind(v1.ConfigurationRevisionGroupVersionKind) + pr.SetDesiredState(v1.PackageRevisionActive) + return nil + }), + MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(o client.Object) error { + want := &v1.ConfigurationRevision{} + want.SetGroupVersionKind(v1.ConfigurationRevisionGroupVersionKind) + want.SetDesiredState(v1.PackageRevisionActive) + want.SetConditions(v1.Unhealthy()) + + if diff := cmp.Diff(want, o); diff != "" { + t.Errorf("-want, +got:\n%s", diff) + } + return nil + }), + }, + }), + WithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { + return nil + }}), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(true), + MockGet: xpkgfake.NewMockCacheGetFn(nil, errBoom), + MockDelete: xpkgfake.NewMockCacheDeleteFn(nil), + }), + }, + }, + want: want{ + err: errors.Wrap(errBoom, errGetCache), + }, + }, + "ErrGetFromCacheFailedDelete": { + reason: "We should return an error if package content is in cache, we cannot get it, and we fail to remove it.", + args: args{ + mgr: &fake.Manager{}, + req: reconcile.Request{NamespacedName: types.NamespacedName{Name: "test"}}, + rec: []ReconcilerOption{ + WithNewPackageRevisionFn(func() v1.PackageRevision { return &v1.ConfigurationRevision{} }), + WithClientApplicator(resource.ClientApplicator{ + Client: &test.MockClient{ + MockGet: test.NewMockGetFn(nil, func(o client.Object) error { + pr := o.(*v1.ConfigurationRevision) + pr.SetGroupVersionKind(v1.ConfigurationRevisionGroupVersionKind) + pr.SetDesiredState(v1.PackageRevisionActive) + return nil + }), + MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(o client.Object) error { + want := &v1.ConfigurationRevision{} + want.SetGroupVersionKind(v1.ConfigurationRevisionGroupVersionKind) + want.SetDesiredState(v1.PackageRevisionActive) + want.SetConditions(v1.Unhealthy()) + + if diff := cmp.Diff(want, o); diff != "" { + t.Errorf("-want, +got:\n%s", diff) + } + return nil + }), + }, + }), + WithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { + return nil + }}), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(true), + MockGet: xpkgfake.NewMockCacheGetFn(nil, errBoom), + MockDelete: xpkgfake.NewMockCacheDeleteFn(errBoom), + }), + }, + }, + want: want{ + err: errors.Wrap(errBoom, errGetCache), + }, + }, + "ErrNotInCachePullPolicyNever": { + reason: "We should return an error if package content is not in cache and pull policy is Never.", + args: args{ + mgr: &fake.Manager{}, + req: reconcile.Request{NamespacedName: types.NamespacedName{Name: "test"}}, + rec: []ReconcilerOption{ + WithNewPackageRevisionFn(func() v1.PackageRevision { + return &v1.ConfigurationRevision{ + Spec: v1.PackageRevisionSpec{ + PackagePullPolicy: &pullPolicy, + }, + } + }), + WithClientApplicator(resource.ClientApplicator{ + Client: &test.MockClient{ + MockGet: test.NewMockGetFn(nil, func(o client.Object) error { + pr := o.(*v1.ConfigurationRevision) + pr.SetGroupVersionKind(v1.ConfigurationRevisionGroupVersionKind) + pr.SetDesiredState(v1.PackageRevisionActive) + return nil + }), + MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(o client.Object) error { + want := &v1.ConfigurationRevision{} + want.SetGroupVersionKind(v1.ConfigurationRevisionGroupVersionKind) + want.SetDesiredState(v1.PackageRevisionActive) + want.SetConditions(v1.Unhealthy()) + + if diff := cmp.Diff(want, o); diff != "" { + t.Errorf("-want, +got:\n%s", diff) + } + return nil + }), + }, + }), + WithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { + return nil + }}), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + }), + }, + }, + want: want{ + err: errors.New(errPullPolicyNever), + }, + }, "ErrInitParserBackend": { reason: "We should return an error if we fail to initialize parser backend.", args: args{ @@ -377,6 +510,9 @@ func TestReconcile(t *testing.T) { WithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + }), WithParserBackend(&ErrBackend{err: errBoom}), }, }, @@ -384,8 +520,137 @@ func TestReconcile(t *testing.T) { err: errors.Wrap(errBoom, errInitParserBackend), }, }, - "ErrParse": { - reason: "We should return an error if fail to parse the package.", + "ErrParseFromCache": { + reason: "We should return an error if fail to parse the package from the cache.", + args: args{ + mgr: &fake.Manager{}, + req: reconcile.Request{NamespacedName: types.NamespacedName{Name: "test"}}, + rec: []ReconcilerOption{ + WithNewPackageRevisionFn(func() v1.PackageRevision { return &v1.ConfigurationRevision{} }), + WithClientApplicator(resource.ClientApplicator{ + Client: &test.MockClient{ + MockGet: test.NewMockGetFn(nil, func(o client.Object) error { + pr := o.(*v1.ConfigurationRevision) + pr.SetGroupVersionKind(v1.ConfigurationRevisionGroupVersionKind) + pr.SetDesiredState(v1.PackageRevisionActive) + return nil + }), + MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(o client.Object) error { + want := &v1.ConfigurationRevision{} + want.SetGroupVersionKind(v1.ConfigurationRevisionGroupVersionKind) + want.SetDesiredState(v1.PackageRevisionActive) + want.SetConditions(v1.Unhealthy()) + + if diff := cmp.Diff(want, o); diff != "" { + t.Errorf("-want, +got:\n%s", diff) + } + return nil + }), + }, + }), + WithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { + return nil + }}), + WithParser(MockParseFn(func(_ context.Context, _ io.ReadCloser) (*parser.Package, error) { return nil, errBoom })), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(true), + MockGet: xpkgfake.NewMockCacheGetFn(io.NopCloser(bytes.NewBuffer(providerBytes)), nil), + }), + }, + }, + want: want{ + err: errors.Wrap(errBoom, errParsePackage), + }, + }, + "ErrParseFromImage": { + reason: "We should return an error if we fail to parse the package from the image.", + args: args{ + mgr: &fake.Manager{}, + req: reconcile.Request{NamespacedName: types.NamespacedName{Name: "test"}}, + rec: []ReconcilerOption{ + WithNewPackageRevisionFn(func() v1.PackageRevision { return &v1.ConfigurationRevision{} }), + WithClientApplicator(resource.ClientApplicator{ + Client: &test.MockClient{ + MockGet: test.NewMockGetFn(nil, func(o client.Object) error { + pr := o.(*v1.ConfigurationRevision) + pr.SetGroupVersionKind(v1.ConfigurationRevisionGroupVersionKind) + pr.SetDesiredState(v1.PackageRevisionActive) + return nil + }), + MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(o client.Object) error { + want := &v1.ConfigurationRevision{} + want.SetGroupVersionKind(v1.ConfigurationRevisionGroupVersionKind) + want.SetDesiredState(v1.PackageRevisionActive) + want.SetConditions(v1.Unhealthy()) + + if diff := cmp.Diff(want, o); diff != "" { + t.Errorf("-want, +got:\n%s", diff) + } + return nil + }), + }, + }), + WithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { + return nil + }}), + WithParser(MockParseFn(func(_ context.Context, _ io.ReadCloser) (*parser.Package, error) { return nil, errBoom })), + WithParserBackend(parser.NewEchoBackend(string(providerBytes))), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + MockStore: xpkgfake.NewMockCacheStoreFn(nil), + }), + }, + }, + want: want{ + err: errors.Wrap(errBoom, errParsePackage), + }, + }, + "ErrParseFromImageFailedCache": { + reason: "We should return an error if we fail to parse the package from the image and fail to cache.", + args: args{ + mgr: &fake.Manager{}, + req: reconcile.Request{NamespacedName: types.NamespacedName{Name: "test"}}, + rec: []ReconcilerOption{ + WithNewPackageRevisionFn(func() v1.PackageRevision { return &v1.ConfigurationRevision{} }), + WithClientApplicator(resource.ClientApplicator{ + Client: &test.MockClient{ + MockGet: test.NewMockGetFn(nil, func(o client.Object) error { + pr := o.(*v1.ConfigurationRevision) + pr.SetGroupVersionKind(v1.ConfigurationRevisionGroupVersionKind) + pr.SetDesiredState(v1.PackageRevisionActive) + return nil + }), + MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(o client.Object) error { + want := &v1.ConfigurationRevision{} + want.SetGroupVersionKind(v1.ConfigurationRevisionGroupVersionKind) + want.SetDesiredState(v1.PackageRevisionActive) + want.SetConditions(v1.Unhealthy()) + + if diff := cmp.Diff(want, o); diff != "" { + t.Errorf("-want, +got:\n%s", diff) + } + return nil + }), + }, + }), + WithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { + return nil + }}), + WithParser(MockParseFn(func(_ context.Context, _ io.ReadCloser) (*parser.Package, error) { return nil, errBoom })), + WithParserBackend(parser.NewEchoBackend(string(providerBytes))), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + MockStore: xpkgfake.NewMockCacheStoreFn(errBoom), + MockDelete: xpkgfake.NewMockCacheDeleteFn(nil), + }), + }, + }, + want: want{ + err: errors.Wrap(errBoom, errParsePackage), + }, + }, + "ErrParseFromImageFailedCacheFailedDelete": { + reason: "We should return an error if we fail to parse the package from the image, fail to cache, and fail to delete from cache.", args: args{ mgr: &fake.Manager{}, req: reconcile.Request{NamespacedName: types.NamespacedName{Name: "test"}}, @@ -417,6 +682,11 @@ func TestReconcile(t *testing.T) { }}), WithParser(MockParseFn(func(_ context.Context, _ io.ReadCloser) (*parser.Package, error) { return nil, errBoom })), WithParserBackend(parser.NewEchoBackend(string(providerBytes))), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + MockStore: xpkgfake.NewMockCacheStoreFn(errBoom), + MockDelete: xpkgfake.NewMockCacheDeleteFn(errBoom), + }), }, }, want: want{ @@ -457,6 +727,13 @@ func TestReconcile(t *testing.T) { WithParser(parser.New(metaScheme, objScheme)), WithParserBackend(parser.NewEchoBackend(string(providerBytes))), WithLinter(&MockLinter{MockLint: NewMockLintFn(errBoom)}), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + MockStore: func(s string, rc io.ReadCloser) error { + _, err := io.ReadAll(rc) + return err + }, + }), }, }, want: want{ @@ -507,6 +784,13 @@ func TestReconcile(t *testing.T) { }}), WithParser(parser.New(metaScheme, objScheme)), WithParserBackend(parser.NewEchoBackend(string(providerBytes))), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + MockStore: func(s string, rc io.ReadCloser) error { + _, err := io.ReadAll(rc) + return err + }, + }), WithLinter(&MockLinter{MockLint: NewMockLintFn(nil)}), WithVersioner(&verfake.MockVersioner{ MockInConstraints: verfake.NewMockInConstraintsFn(false, errBoom), @@ -550,7 +834,11 @@ func TestReconcile(t *testing.T) { return nil }}), WithParser(parser.New(metaScheme, objScheme)), - WithParserBackend(parser.NewNopBackend()), + WithParserBackend(parser.NewEchoBackend("")), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + MockStore: xpkgfake.NewMockCacheStoreFn(nil), + }), WithLinter(&MockLinter{MockLint: NewMockLintFn(nil)}), }, }, @@ -593,6 +881,13 @@ func TestReconcile(t *testing.T) { }}), WithParser(parser.New(metaScheme, objScheme)), WithParserBackend(parser.NewEchoBackend(string(providerBytes))), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + MockStore: func(s string, rc io.ReadCloser) error { + _, err := io.ReadAll(rc) + return err + }, + }), WithLinter(&MockLinter{MockLint: NewMockLintFn(nil)}), }, }, @@ -650,6 +945,13 @@ func TestReconcile(t *testing.T) { }}), WithParser(parser.New(metaScheme, objScheme)), WithParserBackend(parser.NewEchoBackend(string(providerBytes))), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + MockStore: func(s string, rc io.ReadCloser) error { + _, err := io.ReadAll(rc) + return err + }, + }), WithLinter(&MockLinter{MockLint: NewMockLintFn(nil)}), WithVersioner(&verfake.MockVersioner{MockInConstraints: verfake.NewMockInConstraintsFn(true, nil)}), }, @@ -705,6 +1007,13 @@ func TestReconcile(t *testing.T) { }), WithParser(parser.New(metaScheme, objScheme)), WithParserBackend(parser.NewEchoBackend(string(providerBytes))), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + MockStore: func(s string, rc io.ReadCloser) error { + _, err := io.ReadAll(rc) + return err + }, + }), WithLinter(&MockLinter{MockLint: NewMockLintFn(nil)}), WithVersioner(&verfake.MockVersioner{MockInConstraints: verfake.NewMockInConstraintsFn(true, nil)}), }, @@ -762,6 +1071,13 @@ func TestReconcile(t *testing.T) { WithEstablisher(NewMockEstablisher()), WithParser(parser.New(metaScheme, objScheme)), WithParserBackend(parser.NewEchoBackend(string(providerBytes))), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + MockStore: func(s string, rc io.ReadCloser) error { + _, err := io.ReadAll(rc) + return err + }, + }), WithLinter(&MockLinter{MockLint: NewMockLintFn(nil)}), WithVersioner(&verfake.MockVersioner{MockInConstraints: verfake.NewMockInConstraintsFn(true, nil)}), }, @@ -818,6 +1134,13 @@ func TestReconcile(t *testing.T) { WithEstablisher(NewMockEstablisher()), WithParser(parser.New(metaScheme, objScheme)), WithParserBackend(parser.NewEchoBackend(string(providerBytes))), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + MockStore: func(s string, rc io.ReadCloser) error { + _, err := io.ReadAll(rc) + return err + }, + }), WithLinter(&MockLinter{MockLint: NewMockLintFn(nil)}), WithVersioner(&verfake.MockVersioner{MockInConstraints: verfake.NewMockInConstraintsFn(true, nil)}), }, @@ -878,6 +1201,13 @@ func TestReconcile(t *testing.T) { WithEstablisher(NewMockEstablisher()), WithParser(parser.New(metaScheme, objScheme)), WithParserBackend(parser.NewEchoBackend(string(providerBytes))), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + MockStore: func(s string, rc io.ReadCloser) error { + _, err := io.ReadAll(rc) + return err + }, + }), WithLinter(&MockLinter{MockLint: NewMockLintFn(nil)}), WithVersioner(&verfake.MockVersioner{MockInConstraints: verfake.NewMockInConstraintsFn(false, nil)}), }, @@ -935,6 +1265,13 @@ func TestReconcile(t *testing.T) { }), WithParser(parser.New(metaScheme, objScheme)), WithParserBackend(parser.NewEchoBackend(string(providerBytes))), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + MockStore: func(s string, rc io.ReadCloser) error { + _, err := io.ReadAll(rc) + return err + }, + }), WithLinter(&MockLinter{MockLint: NewMockLintFn(nil)}), WithVersioner(&verfake.MockVersioner{MockInConstraints: verfake.NewMockInConstraintsFn(true, nil)}), }, @@ -991,6 +1328,13 @@ func TestReconcile(t *testing.T) { WithEstablisher(NewMockEstablisher()), WithParser(parser.New(metaScheme, objScheme)), WithParserBackend(parser.NewEchoBackend(string(providerBytes))), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + MockStore: func(s string, rc io.ReadCloser) error { + _, err := io.ReadAll(rc) + return err + }, + }), WithLinter(&MockLinter{MockLint: NewMockLintFn(nil)}), WithVersioner(&verfake.MockVersioner{MockInConstraints: verfake.NewMockInConstraintsFn(true, nil)}), }, @@ -1048,6 +1392,13 @@ func TestReconcile(t *testing.T) { }), WithParser(parser.New(metaScheme, objScheme)), WithParserBackend(parser.NewEchoBackend(string(providerBytes))), + WithCache(&xpkgfake.MockCache{ + MockHas: xpkgfake.NewMockCacheHasFn(false), + MockStore: func(s string, rc io.ReadCloser) error { + _, err := io.ReadAll(rc) + return err + }, + }), WithLinter(&MockLinter{MockLint: NewMockLintFn(nil)}), WithVersioner(&verfake.MockVersioner{MockInConstraints: verfake.NewMockInConstraintsFn(true, nil)}), }, diff --git a/internal/feature/feature.go b/internal/feature/feature.go deleted file mode 100644 index 1af804797..000000000 --- a/internal/feature/feature.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2019 The Crossplane Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package feature - -import ( - "sync" -) - -// A Flag enables a particular feature. -type Flag int - -// Feature flags. -const ( - // FlagEnableAlphaCompositionRevisions enables alpha support for - // CompositionRevisions. See the below design for more details. - // https://github.com/crossplane/crossplane/blob/ecd9d5/design/one-pager-composition-revisions.md - FlagEnableAlphaCompositionRevisions Flag = iota -) - -// Flags that are enabled. The zero value - i.e. &feature.Flags{} - is usable. -type Flags struct { - m sync.RWMutex - enabled map[Flag]bool -} - -// Enable a feature flag. -func (fs *Flags) Enable(f Flag) { - fs.m.Lock() - if fs.enabled == nil { - fs.enabled = make(map[Flag]bool) - } - fs.enabled[f] = true - fs.m.Unlock() -} - -// Enabled returns true if the supplied feature flag is enabled. -func (fs *Flags) Enabled(f Flag) bool { - fs.m.RLock() - defer fs.m.RUnlock() - return fs.enabled[f] -} diff --git a/internal/feature/feature_test.go b/internal/feature/feature_test.go deleted file mode 100644 index 62ff16dca..000000000 --- a/internal/feature/feature_test.go +++ /dev/null @@ -1,50 +0,0 @@ -/* -Copyright 2019 The Crossplane Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package feature - -import ( - "testing" - - "github.com/google/go-cmp/cmp" -) - -func TestEnable(t *testing.T) { - var cool Flag = -1 - - t.Run("EnableMutatesZeroValue", func(t *testing.T) { - f := &Flags{} - f.Enable(cool) - - want := true - got := f.Enabled(cool) - - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("f.Enabled(...): -want, +got:\n%s", diff) - } - }) - - t.Run("EnabledOnZeroValueReturnsFalse", func(t *testing.T) { - f := &Flags{} - - want := false - got := f.Enabled(cool) - - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("f.Enabled(...): -want, +got:\n%s", diff) - } - }) -} diff --git a/internal/feature/flag_string.go b/internal/feature/flag_string.go deleted file mode 100644 index 3c6ea2231..000000000 --- a/internal/feature/flag_string.go +++ /dev/null @@ -1,23 +0,0 @@ -// Code generated by "stringer -type=Flag"; DO NOT EDIT. - -package feature - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[FlagEnableAlphaCompositionRevisions-0] -} - -const _Flag_name = "FlagEnableAlphaCompositionRevisions" - -var _Flag_index = [...]uint8{0, 35} - -func (i Flag) String() string { - if i < 0 || i >= Flag(len(_Flag_index)-1) { - return "Flag(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _Flag_name[_Flag_index[i]:_Flag_index[i+1]] -} diff --git a/internal/features/features.go b/internal/features/features.go index 7d3ad45ae..e6c255aac 100644 --- a/internal/features/features.go +++ b/internal/features/features.go @@ -24,4 +24,8 @@ const ( // CompositionRevisions. See the below design for more details. // https://github.com/crossplane/crossplane/blob/ecd9d5/design/one-pager-composition-revisions.md EnableAlphaCompositionRevisions feature.Flag = "EnableAlphaCompositionRevisions" + // EnableAlphaExternalSecretStores enables alpha support for + // External Secret Stores. See the below design for more details. + // https://github.com/crossplane/crossplane/blob/390ddd/design/design-doc-external-secret-stores.md + EnableAlphaExternalSecretStores feature.Flag = "EnableAlphaExternalSecretStores" ) diff --git a/internal/initializer/crds.go b/internal/initializer/crds.go index 3270985bc..5640348ac 100644 --- a/internal/initializer/crds.go +++ b/internal/initializer/crds.go @@ -20,8 +20,10 @@ import ( "context" "github.com/spf13/afero" + corev1 "k8s.io/api/core/v1" extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/crossplane/crossplane-runtime/pkg/errors" @@ -29,20 +31,69 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" ) +// Error strings. +const ( + errFmtCRDWithConversionWithoutTLS = "crds with webhook conversion strategy cannot be deployed without webhook tls secret: %s" + errFmtNoTLSCrtInSecret = "cannot find tls.crt key in webhook tls secret %s" +) + +// WithWebhookTLSSecretRef configures CoreCRDs with the TLS Secret name so that +// it can fetch it and inject the CA bundle to CRDs with webhook conversion +// strategy. +func WithWebhookTLSSecretRef(nn types.NamespacedName) CoreCRDsOption { + return func(c *CoreCRDs) { + c.WebhookTLSSecretRef = &nn + } +} + +// WithFs is used to configure the filesystem the CRDs will be read from. Its +// default is afero.OsFs. +func WithFs(fs afero.Fs) CoreCRDsOption { + return func(c *CoreCRDs) { + c.fs = fs + } +} + +// CoreCRDsOption configures CoreCRDs step. +type CoreCRDsOption func(*CoreCRDs) + // NewCoreCRDs returns a new *CoreCRDs. -func NewCoreCRDs(path string, s *runtime.Scheme) *CoreCRDs { - return &CoreCRDs{Path: path, Scheme: s} +func NewCoreCRDs(path string, s *runtime.Scheme, opts ...CoreCRDsOption) *CoreCRDs { + c := &CoreCRDs{ + Path: path, + Scheme: s, + fs: afero.NewOsFs(), + } + for _, f := range opts { + f(c) + } + return c } // CoreCRDs makes sure the CRDs are installed. type CoreCRDs struct { - Path string - Scheme *runtime.Scheme + Path string + Scheme *runtime.Scheme + WebhookTLSSecretRef *types.NamespacedName + + fs afero.Fs } // Run applies all CRDs in the given directory. -func (c *CoreCRDs) Run(ctx context.Context, kube client.Client) error { - r, err := parser.NewFsBackend(afero.NewOsFs(), +func (c *CoreCRDs) Run(ctx context.Context, kube client.Client) error { // nolint:gocyclo + var caBundle []byte + if c.WebhookTLSSecretRef != nil { + s := &corev1.Secret{} + if err := kube.Get(ctx, *c.WebhookTLSSecretRef, s); err != nil { + return errors.Wrap(err, errGetWebhookSecret) + } + if len(s.Data["tls.crt"]) == 0 { + return errors.Errorf(errFmtNoTLSCrtInSecret, c.WebhookTLSSecretRef.String()) + } + caBundle = s.Data["tls.crt"] + } + + r, err := parser.NewFsBackend(c.fs, parser.FsDir(c.Path), parser.FsFilters( parser.SkipDirs(), @@ -65,6 +116,18 @@ func (c *CoreCRDs) Run(ctx context.Context, kube client.Client) error { if !ok { return errors.New("only crds can exist in initialization directory") } + if crd.Spec.Conversion != nil && crd.Spec.Conversion.Strategy == extv1.WebhookConverter { + if len(caBundle) == 0 { + return errors.Errorf(errFmtCRDWithConversionWithoutTLS, crd.Name) + } + if crd.Spec.Conversion.Webhook == nil { + crd.Spec.Conversion.Webhook = &extv1.WebhookConversion{} + } + if crd.Spec.Conversion.Webhook.ClientConfig == nil { + crd.Spec.Conversion.Webhook.ClientConfig = &extv1.WebhookClientConfig{} + } + crd.Spec.Conversion.Webhook.ClientConfig.CABundle = caBundle + } if err := pa.Apply(ctx, crd); err != nil { return errors.Wrap(err, "cannot apply crd") } diff --git a/internal/initializer/crds_test.go b/internal/initializer/crds_test.go new file mode 100644 index 000000000..7987ab6ac --- /dev/null +++ b/internal/initializer/crds_test.go @@ -0,0 +1,235 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package initializer + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/spf13/afero" + corev1 "k8s.io/api/core/v1" + extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/crossplane/crossplane-runtime/pkg/errors" + "github.com/crossplane/crossplane-runtime/pkg/test" +) + +func TestCoreCRDs(t *testing.T) { + type args struct { + kube client.Client + opts []CoreCRDsOption + } + type want struct { + err error + } + fsWithoutConversionCRD := afero.NewMemMapFs() + f, _ := fsWithoutConversionCRD.Create("/crds/nonwebhookcrd.yaml") + _, _ = f.WriteString(nonWebhookCRD) + + fsWithConversionCRD := afero.NewMemMapFs() + f, _ = fsWithConversionCRD.Create("/crds/webhookcrd.yaml") + _, _ = f.WriteString(webhookCRD) + + fsMixedCRDs := afero.NewMemMapFs() + f, _ = fsMixedCRDs.Create("/crds/webhookcrd.yaml") + _, _ = f.WriteString(webhookCRD) + f, _ = fsMixedCRDs.Create("/crds/nonwebhookcrd.yaml") + _, _ = f.WriteString(nonWebhookCRD) + + secret := &corev1.Secret{ + Data: map[string][]byte{"tls.crt": []byte("CABUNDLE")}, + } + s := runtime.NewScheme() + _ = extv1.AddToScheme(s) + cases := map[string]struct { + reason string + args + want + }{ + "SuccessWithoutTLSSecret": { + reason: "If no webhook TLS secret is given, then all operations should succeed if CRDs do not have webhook strategy", + args: args{ + opts: []CoreCRDsOption{ + WithFs(fsWithoutConversionCRD), + }, + kube: &test.MockClient{ + MockGet: func(_ context.Context, _ client.ObjectKey, _ client.Object) error { + return kerrors.NewNotFound(schema.GroupResource{}, "") + }, + MockCreate: func(_ context.Context, _ client.Object, _ ...client.CreateOption) error { + return nil + }, + }, + }, + }, + "CRDWithConversionWithoutTLSSecret": { + reason: "CRDs with webhook conversion requires enabling webhooks, otherwise all apiserver requests will fail.", + args: args{ + opts: []CoreCRDsOption{ + WithFs(fsWithConversionCRD), + }, + }, + want: want{ + err: errors.Errorf(errFmtCRDWithConversionWithoutTLS, "crontabsconverts.stable.example.com"), + }, + }, + "SuccessWithTLSSecret": { + reason: "If TLS Secret is given and populated correctly, then CA bundle should be injected and apply operations should succeed", + args: args{ + opts: []CoreCRDsOption{ + WithFs(fsMixedCRDs), + WithWebhookTLSSecretRef(types.NamespacedName{}), + }, + kube: &test.MockClient{ + MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error { + if s, ok := obj.(*corev1.Secret); ok { + secret.DeepCopyInto(s) + return nil + } + return kerrors.NewNotFound(schema.GroupResource{}, "") + }, + MockCreate: func(_ context.Context, obj client.Object, _ ...client.CreateOption) error { + crd := obj.(*extv1.CustomResourceDefinition) + switch crd.Name { + case "crontabs.stable.example.com": + if crd.Spec.Conversion != nil { + t.Error("\nCA is injected into a non-webhook CRD") + } + return nil + case "crontabsconverts.stable.example.com": + if diff := cmp.Diff(crd.Spec.Conversion.Webhook.ClientConfig.CABundle, []byte("CABUNDLE")); diff != "" { + t.Errorf("\n%s", diff) + } + return nil + } + t.Error("unexpected crd") + return nil + }, + }, + }, + }, + "TLSSecretGivenButNotFound": { + reason: "If TLS secret name is given, then it has to be found", + args: args{ + opts: []CoreCRDsOption{ + WithWebhookTLSSecretRef(types.NamespacedName{}), + }, + kube: &test.MockClient{ + MockGet: test.NewMockGetFn(errBoom), + }, + }, + want: want{ + err: errors.Wrap(errBoom, errGetWebhookSecret), + }, + }, + } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + err := NewCoreCRDs("/crds", s, tc.opts...).Run(context.TODO(), tc.kube) + if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { + t.Errorf("\n%s\nRun(...): -want err, +got err:\n%s", tc.reason, diff) + } + }) + } +} + +const ( + nonWebhookCRD = ` +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: crontabs.stable.example.com +spec: + group: stable.example.com + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + cronSpec: + type: string + scope: Namespaced + names: + plural: crontabs + singular: crontab + kind: CronTab + shortNames: + - ct +` + webhookCRD = ` +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: crontabsconverts.stable.example.com +spec: + preserveUnknownFields: false + group: stable.example.com + versions: + - name: v1beta1 + served: true + storage: false + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + hostPort: + type: string + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + host: + type: string + port: + type: string + conversion: + strategy: Webhook + webhookClientConfig: + service: + namespace: crd-conversion-webhook + name: crd-conversion-webhook + path: /crdconvert + scope: Namespaced + names: + plural: crontabsconverts + singular: crontabsconvert + kind: CronTabsConvert + shortNames: + - ctc +` +) diff --git a/internal/initializer/initializer.go b/internal/initializer/initializer.go index 85c13dd75..268626dd0 100644 --- a/internal/initializer/initializer.go +++ b/internal/initializer/initializer.go @@ -18,18 +18,18 @@ package initializer import ( "context" + "reflect" + + "github.com/crossplane/crossplane-runtime/pkg/logging" "sigs.k8s.io/controller-runtime/pkg/client" ) // New returns a new *Initializer. -func New(kube client.Client, steps ...Step) *Initializer { - return &Initializer{kube: kube, steps: steps} +func New(kube client.Client, log logging.Logger, steps ...Step) *Initializer { + return &Initializer{kube: kube, log: log, steps: steps} } -// TODO(muvaf): We will have some options to inject CA Bundles to those CRDs -// before applying them. - // Step is a blocking step of the initialization process. type Step interface { Run(ctx context.Context, kube client.Client) error @@ -40,6 +40,7 @@ type Step interface { type Initializer struct { steps []Step kube client.Client + log logging.Logger } // Init does all operations necessary for controllers and webhooks to work. @@ -48,6 +49,7 @@ func (c *Initializer) Init(ctx context.Context) error { if err := s.Run(ctx, c.kube); err != nil { return err } + c.log.Info("Step has been completed", "Name", reflect.TypeOf(s).Elem().Name()) } return nil } diff --git a/internal/initializer/installer_test.go b/internal/initializer/installer_test.go index ed95680b5..1a8183e6a 100644 --- a/internal/initializer/installer_test.go +++ b/internal/initializer/installer_test.go @@ -25,12 +25,11 @@ import ( kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/client" "github.com/crossplane/crossplane-runtime/pkg/errors" "github.com/crossplane/crossplane-runtime/pkg/test" v1 "github.com/crossplane/crossplane/apis/pkg/v1" - - "sigs.k8s.io/controller-runtime/pkg/client" ) const ( diff --git a/internal/initializer/storeconfig.go b/internal/initializer/storeconfig.go new file mode 100644 index 000000000..ff22c199c --- /dev/null +++ b/internal/initializer/storeconfig.go @@ -0,0 +1,65 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package initializer + +import ( + "context" + + kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + "github.com/crossplane/crossplane-runtime/pkg/errors" + "github.com/crossplane/crossplane-runtime/pkg/resource" + + scv1alpha1 "github.com/crossplane/crossplane/apis/secrets/v1alpha1" +) + +const ( + errCreateDefaultStoreConfig = "cannot create default store config" +) + +// NewStoreConfigObject returns a new *StoreConfigObject initializer. +func NewStoreConfigObject(ns string) *StoreConfigObject { + return &StoreConfigObject{ + namespace: ns, + } +} + +// StoreConfigObject has the initializer for creating the default secret +// StoreConfig. +type StoreConfigObject struct { + namespace string +} + +// Run makes sure a StoreConfig named as default exists. +func (so *StoreConfigObject) Run(ctx context.Context, kube client.Client) error { + sc := &scv1alpha1.StoreConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + }, + Spec: scv1alpha1.StoreConfigSpec{ + // NOTE(turkenh): We only set required spec and expect optional ones + // will properly be initialized with CRD level default values. + SecretStoreConfig: xpv1.SecretStoreConfig{ + DefaultScope: so.namespace, + }, + }, + } + return errors.Wrap(resource.Ignore(kerrors.IsAlreadyExists, kube.Create(ctx, sc)), errCreateDefaultStoreConfig) +} diff --git a/internal/initializer/storeconfig_test.go b/internal/initializer/storeconfig_test.go new file mode 100644 index 000000000..19ddcd583 --- /dev/null +++ b/internal/initializer/storeconfig_test.go @@ -0,0 +1,82 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package initializer + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + kerrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/crossplane/crossplane-runtime/pkg/errors" + "github.com/crossplane/crossplane-runtime/pkg/test" +) + +func TestStoreConfigObject(t *testing.T) { + type args struct { + kube client.Client + } + type want struct { + err error + } + cases := map[string]struct { + args + want + }{ + "FailedToCreate": { + args: args{ + kube: &test.MockClient{ + MockCreate: func(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { + return errBoom + }, + }, + }, + want: want{ + err: errors.Wrap(errBoom, errCreateDefaultStoreConfig), + }, + }, + "SuccessCreated": { + args: args{ + kube: &test.MockClient{ + MockCreate: func(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { + return nil + }, + }, + }, + }, + "SuccessAlreadyExists": { + args: args{ + kube: &test.MockClient{ + MockCreate: func(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { + return kerrors.NewAlreadyExists(schema.GroupResource{}, "default") + }, + }, + }, + }, + } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + err := NewStoreConfigObject("crossplane-system").Run(context.TODO(), tc.args.kube) + if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { + t.Errorf("\n%s\nRun(...): -want err, +got err:\n%s", name, diff) + } + }) + } +} diff --git a/internal/initializer/webhook_configurations.go b/internal/initializer/webhook_configurations.go new file mode 100644 index 000000000..60f202158 --- /dev/null +++ b/internal/initializer/webhook_configurations.go @@ -0,0 +1,134 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package initializer + +import ( + "context" + "reflect" + + "github.com/spf13/afero" + admv1 "k8s.io/api/admissionregistration/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/crossplane/crossplane-runtime/pkg/errors" + "github.com/crossplane/crossplane-runtime/pkg/parser" + "github.com/crossplane/crossplane-runtime/pkg/resource" +) + +const ( + errApplyWebhookConfiguration = "cannot apply webhook configuration" +) + +// WithWebhookConfigurationsFs is used to configure the filesystem the CRDs will +// be read from. Its default is afero.OsFs. +func WithWebhookConfigurationsFs(fs afero.Fs) WebhookConfigurationsOption { + return func(c *WebhookConfigurations) { + c.fs = fs + } +} + +// WebhookConfigurationsOption configures WebhookConfigurations step. +type WebhookConfigurationsOption func(*WebhookConfigurations) + +// NewWebhookConfigurations returns a new *WebhookConfigurations. +func NewWebhookConfigurations(path string, s *runtime.Scheme, tlsSecretRef types.NamespacedName, svc admv1.ServiceReference, opts ...WebhookConfigurationsOption) *WebhookConfigurations { + c := &WebhookConfigurations{ + Path: path, + Scheme: s, + TLSSecretRef: tlsSecretRef, + ServiceReference: svc, + fs: afero.NewOsFs(), + } + for _, f := range opts { + f(c) + } + return c +} + +// WebhookConfigurations makes sure the ValidatingWebhookConfigurations and +// MutatingWebhookConfiguration are installed. +type WebhookConfigurations struct { + Path string + Scheme *runtime.Scheme + TLSSecretRef types.NamespacedName + ServiceReference admv1.ServiceReference + + fs afero.Fs +} + +// Run applies all webhook ValidatingWebhookConfigurations and +// MutatingWebhookConfiguration in the given directory. +func (c *WebhookConfigurations) Run(ctx context.Context, kube client.Client) error { // nolint:gocyclo + s := &corev1.Secret{} + if err := kube.Get(ctx, c.TLSSecretRef, s); err != nil { + return errors.Wrap(err, errGetWebhookSecret) + } + if len(s.Data["tls.crt"]) == 0 { + return errors.Errorf(errFmtNoTLSCrtInSecret, c.TLSSecretRef.String()) + } + caBundle := s.Data["tls.crt"] + + r, err := parser.NewFsBackend(c.fs, + parser.FsDir(c.Path), + parser.FsFilters( + parser.SkipDirs(), + parser.SkipNotYAML(), + parser.SkipEmpty(), + ), + ).Init(ctx) + if err != nil { + return errors.Wrap(err, "cannot init filesystem") + } + defer func() { _ = r.Close() }() + p := parser.New(runtime.NewScheme(), c.Scheme) + pkg, err := p.Parse(ctx, r) + if err != nil { + return errors.Wrap(err, "cannot parse files") + } + pa := resource.NewAPIPatchingApplicator(kube) + for _, obj := range pkg.GetObjects() { + switch conf := obj.(type) { + case *admv1.ValidatingWebhookConfiguration: + for i := range conf.Webhooks { + conf.Webhooks[i].ClientConfig.CABundle = caBundle + conf.Webhooks[i].ClientConfig.Service.Name = c.ServiceReference.Name + conf.Webhooks[i].ClientConfig.Service.Namespace = c.ServiceReference.Namespace + conf.Webhooks[i].ClientConfig.Service.Port = c.ServiceReference.Port + } + // See https://github.com/kubernetes-sigs/controller-tools/issues/658 + conf.SetName("crossplane") + case *admv1.MutatingWebhookConfiguration: + for i := range conf.Webhooks { + conf.Webhooks[i].ClientConfig.CABundle = caBundle + conf.Webhooks[i].ClientConfig.Service.Name = c.ServiceReference.Name + conf.Webhooks[i].ClientConfig.Service.Namespace = c.ServiceReference.Namespace + conf.Webhooks[i].ClientConfig.Service.Port = c.ServiceReference.Port + } + // See https://github.com/kubernetes-sigs/controller-tools/issues/658 + conf.SetName("crossplane") + default: + return errors.Errorf("only MutatingWebhookConfiguration and ValidatingWebhookConfiguration kinds are accepted, got %s", reflect.TypeOf(obj).String()) + } + if err := pa.Apply(ctx, obj.(client.Object)); err != nil { + return errors.Wrap(err, errApplyWebhookConfiguration) + } + } + return nil +} diff --git a/internal/initializer/webhook_configurations_test.go b/internal/initializer/webhook_configurations_test.go new file mode 100644 index 000000000..a11ee5ff0 --- /dev/null +++ b/internal/initializer/webhook_configurations_test.go @@ -0,0 +1,254 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package initializer + +import ( + "bytes" + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/spf13/afero" + admv1 "k8s.io/api/admissionregistration/v1" + corev1 "k8s.io/api/core/v1" + extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/crossplane/crossplane-runtime/pkg/errors" + "github.com/crossplane/crossplane-runtime/pkg/test" +) + +func TestWebhookConfigurations(t *testing.T) { + type args struct { + kube client.Client + svc admv1.ServiceReference + opts []WebhookConfigurationsOption + } + type want struct { + err error + } + fs := afero.NewMemMapFs() + f, _ := fs.Create("/webhooks/manifests.yaml") + _, _ = f.WriteString(webhookConfigs) + + fsWithMixedTypes := afero.NewMemMapFs() + f, _ = fsWithMixedTypes.Create("/webhooks/manifests.yaml") + _, _ = f.WriteString(webhookCRD) + + secret := &corev1.Secret{ + Data: map[string][]byte{"tls.crt": []byte("CABUNDLE")}, + } + sch := runtime.NewScheme() + _ = admv1.AddToScheme(sch) + _ = extv1.AddToScheme(sch) + var p int32 = 1234 + svc := admv1.ServiceReference{ + Name: "a", + Namespace: "b", + Port: &p, + } + cases := map[string]struct { + reason string + args + want + }{ + "Success": { + reason: "If a proper webhook TLS is given, then webhook configurations should have the configs injected and operations should succeed", + args: args{ + opts: []WebhookConfigurationsOption{ + WithWebhookConfigurationsFs(fs), + }, + svc: svc, + kube: &test.MockClient{ + MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error { + if s, ok := obj.(*corev1.Secret); ok { + secret.DeepCopyInto(s) + return nil + } + return kerrors.NewNotFound(schema.GroupResource{}, "") + }, + MockCreate: func(_ context.Context, obj client.Object, _ ...client.CreateOption) error { + switch c := obj.(type) { + case *admv1.ValidatingWebhookConfiguration: + for _, w := range c.Webhooks { + if !bytes.Equal(w.ClientConfig.CABundle, []byte("CABUNDLE")) { + t.Errorf("unexpected certificate bundle content: %sch", string(w.ClientConfig.CABundle)) + } + } + case *admv1.MutatingWebhookConfiguration: + for _, w := range c.Webhooks { + if !bytes.Equal(w.ClientConfig.CABundle, []byte("CABUNDLE")) { + t.Errorf("unexpected certificate bundle content: %sch", string(w.ClientConfig.CABundle)) + } + } + default: + t.Error("unexpected type") + } + return nil + }, + }, + }, + }, + "CertNotFound": { + reason: "If TLS Secret cannot be found, then it should not proceed", + args: args{ + opts: []WebhookConfigurationsOption{ + WithWebhookConfigurationsFs(fs), + }, + kube: &test.MockClient{ + MockGet: test.NewMockGetFn(errBoom), + }, + }, + want: want{ + err: errors.Wrap(errBoom, errGetWebhookSecret), + }, + }, + "CertKeyEmpty": { + reason: "If the TLS Secret does not have a CA bundle, then it should not proceed", + args: args{ + opts: []WebhookConfigurationsOption{ + WithWebhookConfigurationsFs(fs), + }, + kube: &test.MockClient{ + MockGet: test.NewMockGetFn(nil), + }, + }, + want: want{ + err: errors.Errorf(errFmtNoTLSCrtInSecret, "/"), + }, + }, + "ApplyFailed": { + reason: "If it cannot apply webhook configurations, then it should not proceed", + args: args{ + opts: []WebhookConfigurationsOption{ + WithWebhookConfigurationsFs(fs), + }, + kube: &test.MockClient{ + MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error { + if s, ok := obj.(*corev1.Secret); ok { + secret.DeepCopyInto(s) + return nil + } + return errBoom + }, + }, + svc: svc, + }, + want: want{ + err: errors.Wrap(errors.Wrap(errBoom, "cannot get object"), errApplyWebhookConfiguration), + }, + }, + "NonWebhookType": { + reason: "Only webhook configuration types can be processed", + args: args{ + opts: []WebhookConfigurationsOption{ + WithWebhookConfigurationsFs(fsWithMixedTypes), + }, + kube: &test.MockClient{ + MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error { + if s, ok := obj.(*corev1.Secret); ok { + secret.DeepCopyInto(s) + return nil + } + return errBoom + }, + }, + svc: svc, + }, + want: want{ + err: errors.Errorf("only MutatingWebhookConfiguration and ValidatingWebhookConfiguration kinds are accepted, got %s", "*v1.CustomResourceDefinition"), + }, + }, + } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + err := NewWebhookConfigurations( + "/webhooks", + sch, + types.NamespacedName{}, + tc.args.svc, + tc.opts...).Run(context.TODO(), tc.kube) + if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { + t.Errorf("\n%sch\nRun(...): -want err, +got err:\n%s", tc.reason, diff) + } + }) + } +} + +const ( + webhookConfigs = ` +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate + failurePolicy: Fail + name: compositeresourcedefinitions + rules: + - apiGroups: + - apiextensions.crossplane.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - compositeresourcedefinitions + sideEffects: None +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + creationTimestamp: null + name: validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate + failurePolicy: Fail + name: compositeresourcedefinitions + rules: + - apiGroups: + - apiextensions.crossplane.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - compositeresourcedefinitions + sideEffects: None +` +) diff --git a/internal/initializer/webhook_tls.go b/internal/initializer/webhook_tls.go new file mode 100644 index 000000000..6cc064b7d --- /dev/null +++ b/internal/initializer/webhook_tls.go @@ -0,0 +1,177 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package initializer + +import ( + "bytes" + "context" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "time" + + "github.com/crossplane/crossplane-runtime/pkg/logging" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/crossplane/crossplane-runtime/pkg/errors" +) + +const ( + errGetWebhookSecret = "cannot get webhook tls secret" + errUpdateWebhookSecret = "cannot update webhook tls secret" + errGenerateCertificate = "cannot generate tls certificate" +) + +// CertificateGenerator can return you TLS certificate valid for given domains. +type CertificateGenerator interface { + Generate(domain ...string) (key []byte, crt []byte, err error) +} + +// WebhookCertificateGeneratorOption is used to configure WebhookCertificateGenerator behavior. +type WebhookCertificateGeneratorOption func(*WebhookCertificateGenerator) + +// WithCertificateGenerator sets the CertificateGenerator that +// WebhookCertificateGenerator uses. +func WithCertificateGenerator(cg CertificateGenerator) WebhookCertificateGeneratorOption { + return func(w *WebhookCertificateGenerator) { + w.certificate = cg + } +} + +// NewWebhookCertificateGenerator returns a new WebhookCertificateGenerator. +func NewWebhookCertificateGenerator(nn types.NamespacedName, svcNamespace string, log logging.Logger, opts ...WebhookCertificateGeneratorOption) *WebhookCertificateGenerator { + w := &WebhookCertificateGenerator{ + SecretRef: nn, + ServiceNamespace: svcNamespace, + certificate: NewRootCAGenerator(), + log: log, + } + for _, f := range opts { + f(w) + } + return w +} + +// WebhookCertificateGenerator is an initializer step that will find the given secret +// and fill its tls.crt and tls.key fields with a TLS certificate that is signed +// for *..svc domains so that all webhooks in that namespace can use +// it. +type WebhookCertificateGenerator struct { + SecretRef types.NamespacedName + ServiceNamespace string + + certificate CertificateGenerator + log logging.Logger +} + +// Run generates the TLS certificate valid for *..svc domain and +// updates the given secret. +func (wt *WebhookCertificateGenerator) Run(ctx context.Context, kube client.Client) error { + s := &corev1.Secret{} + if err := kube.Get(ctx, wt.SecretRef, s); err != nil { + return errors.Wrap(err, errGetWebhookSecret) + } + // NOTE(muvaf): After 10 years, user will have to delete either of these + // keys and re-create the pod to have the initializer re-generate the + // certificate. No expiration check is put here. + if len(s.Data["tls.key"]) != 0 && len(s.Data["tls.crt"]) != 0 { + wt.log.Info("Given tls secret is already filled, skipping tls certificate generation") + return nil + } + wt.log.Info("Given tls secret is empty, generating a new tls certificate") + key, crt, err := wt.certificate.Generate(fmt.Sprintf("*.%s.svc", wt.ServiceNamespace)) + if err != nil { + return errors.Wrap(err, errGenerateCertificate) + } + if s.Data == nil { + s.Data = make(map[string][]byte, 2) + } + s.Data["tls.key"] = key + s.Data["tls.crt"] = crt + + return errors.Wrap(kube.Update(ctx, s), errUpdateWebhookSecret) +} + +// NewRootCAGenerator returns a new RootCAGenerator. +func NewRootCAGenerator() *RootCAGenerator { + return &RootCAGenerator{} +} + +// RootCAGenerator generates a root CA and key that can be used by client and +// servers. +type RootCAGenerator struct{} + +// Generate creates TLS Secret with 10 years expiration date that is valid +// for the given domains. +func (*RootCAGenerator) Generate(domains ...string) (key []byte, crt []byte, err error) { + ca := &x509.Certificate{ + SerialNumber: big.NewInt(2022), + Subject: pkix.Name{ + CommonName: "Crossplane", + Organization: []string{"Crossplane"}, + Country: []string{"Earth"}, + Province: []string{"Earth"}, + Locality: []string{"Earth"}, + }, + Issuer: pkix.Name{ + CommonName: "Crossplane", + Organization: []string{"Crossplane"}, + Country: []string{"Earth"}, + Province: []string{"Earth"}, + Locality: []string{"Earth"}, + }, + DNSNames: domains, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(10, 0, 0), + IsCA: true, + KeyUsage: x509.KeyUsageCRLSign | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + } + // NOTE(muvaf): Why 2048 and not 4096? Mainly performance. + // See https://www.fastly.com/blog/key-size-for-tls + caPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, nil, errors.Wrap(err, "cannot generate private key") + } + caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivateKey.PublicKey, caPrivateKey) + if err != nil { + return nil, nil, errors.Wrap(err, "cannot create certificate with key") + } + + caPEM := new(bytes.Buffer) + if err := pem.Encode(caPEM, &pem.Block{ + Type: "CERTIFICATE", + Bytes: caBytes, + }); err != nil { + return nil, nil, errors.Wrap(err, "cannot encode cert into PEM") + } + caPrivateKeyPEM := new(bytes.Buffer) + if err := pem.Encode(caPrivateKeyPEM, &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(caPrivateKey), + }); err != nil { + return nil, nil, errors.Wrap(err, "cannot encode private key into PEM") + } + return caPrivateKeyPEM.Bytes(), caPEM.Bytes(), nil +} diff --git a/internal/initializer/webhook_tls_test.go b/internal/initializer/webhook_tls_test.go new file mode 100644 index 000000000..1ac5c01dc --- /dev/null +++ b/internal/initializer/webhook_tls_test.go @@ -0,0 +1,146 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package initializer + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/crossplane/crossplane-runtime/pkg/errors" + "github.com/crossplane/crossplane-runtime/pkg/logging" + "github.com/crossplane/crossplane-runtime/pkg/test" +) + +// MockCertificateGenerator is used to mock certificate generator because the +// real one takes a few seconds to generate a real certificate. +type MockCertificateGenerator struct { + MockGenerate func(domain ...string) (key []byte, crt []byte, err error) +} + +// Generate calls MockGenerate. +func (m *MockCertificateGenerator) Generate(domain ...string) (key []byte, crt []byte, err error) { + return m.MockGenerate(domain...) +} + +func TestRun(t *testing.T) { + type args struct { + kube client.Client + ca CertificateGenerator + } + type want struct { + err error + } + cases := map[string]struct { + reason string + args + want + }{ + "Success": { + reason: "It should be successful if the TLS certificate is generated and put into the Secret.", + args: args{ + kube: &test.MockClient{ + MockGet: test.NewMockGetFn(nil), + MockUpdate: test.NewMockUpdateFn(nil), + }, + ca: &MockCertificateGenerator{ + MockGenerate: func(_ ...string) ([]byte, []byte, error) { + return nil, nil, nil + }, + }, + }, + }, + "SuccessSecretAlreadyFilled": { + reason: "It should be successful if the Secret is already filled.", + args: args{ + kube: &test.MockClient{ + MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error { + s := &corev1.Secret{ + Data: map[string][]byte{ + "tls.crt": []byte("CRT"), + "tls.key": []byte("KEY"), + }, + } + s.DeepCopyInto(obj.(*corev1.Secret)) + return nil + }, + }, + }, + }, + "SecretNotFound": { + reason: "It should fail if the given secret cannot be fetched.", + args: args{ + kube: &test.MockClient{ + MockGet: test.NewMockGetFn(errBoom), + }, + }, + want: want{ + err: errors.Wrap(errBoom, errGetWebhookSecret), + }, + }, + "CertificateCannotBeGenerated": { + reason: "It should fail if the secret cannot be updated with the new values.", + args: args{ + kube: &test.MockClient{ + MockGet: test.NewMockGetFn(nil), + }, + ca: &MockCertificateGenerator{ + MockGenerate: func(_ ...string) ([]byte, []byte, error) { + return nil, nil, errBoom + }, + }, + }, + want: want{ + err: errors.Wrap(errBoom, errGenerateCertificate), + }, + }, + "UpdateFailed": { + reason: "It should fail if the secret cannot be updated with the new values.", + args: args{ + kube: &test.MockClient{ + MockGet: test.NewMockGetFn(nil), + MockUpdate: test.NewMockUpdateFn(errBoom), + }, + ca: &MockCertificateGenerator{ + MockGenerate: func(_ ...string) ([]byte, []byte, error) { + return []byte("key"), []byte("crt"), nil + }, + }, + }, + want: want{ + err: errors.Wrap(errBoom, errUpdateWebhookSecret), + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + err := NewWebhookCertificateGenerator( + types.NamespacedName{}, + "crossplane-system", + logging.NewNopLogger(), + WithCertificateGenerator(tc.args.ca)).Run(context.TODO(), tc.kube) + if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { + t.Errorf("\n%sch\nRun(...): -want err, +got err:\n%s", tc.reason, diff) + } + }) + } +} diff --git a/internal/xcrd/crd.go b/internal/xcrd/crd.go index cc467bc4c..44a70e50a 100644 --- a/internal/xcrd/crd.go +++ b/internal/xcrd/crd.go @@ -27,6 +27,7 @@ import ( extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" "github.com/crossplane/crossplane-runtime/pkg/errors" "github.com/crossplane/crossplane-runtime/pkg/meta" @@ -73,6 +74,8 @@ func ForCompositeResource(xrd *v1.CompositeResourceDefinition) (*extv1.CustomRes Name: vr.Name, Served: vr.Served, Storage: vr.Referenceable, + Deprecated: pointer.BoolDeref(vr.Deprecated, false), + DeprecationWarning: vr.DeprecationWarning, AdditionalPrinterColumns: append(vr.AdditionalPrinterColumns, CompositeResourcePrinterColumns()...), Schema: &extv1.CustomResourceValidation{ OpenAPIV3Schema: BaseProps(), @@ -144,6 +147,8 @@ func ForCompositeResourceClaim(xrd *v1.CompositeResourceDefinition) (*extv1.Cust Name: vr.Name, Served: vr.Served, Storage: vr.Referenceable, + Deprecated: pointer.BoolDeref(vr.Deprecated, false), + DeprecationWarning: vr.DeprecationWarning, AdditionalPrinterColumns: append(vr.AdditionalPrinterColumns, CompositeResourceClaimPrinterColumns()...), Schema: &extv1.CustomResourceValidation{ OpenAPIV3Schema: BaseProps(), diff --git a/internal/xcrd/crd_test.go b/internal/xcrd/crd_test.go index baa577de4..eaecdc1c7 100644 --- a/internal/xcrd/crd_test.go +++ b/internal/xcrd/crd_test.go @@ -278,6 +278,44 @@ func TestForCompositeResource(t *testing.T) { }, }, }, + "publishConnectionDetailsTo": { + Type: "object", + Required: []string{"name"}, + Properties: map[string]extv1.JSONSchemaProps{ + "name": {Type: "string"}, + "configRef": { + Type: "object", + Default: &extv1.JSON{Raw: []byte(`{"name": "default"}`)}, + Properties: map[string]extv1.JSONSchemaProps{ + "name": { + Type: "string", + }, + }, + }, + "metadata": { + Type: "object", + Properties: map[string]extv1.JSONSchemaProps{ + "labels": { + Type: "object", + AdditionalProperties: &extv1.JSONSchemaPropsOrBool{ + Allows: true, + Schema: &extv1.JSONSchemaProps{Type: "string"}, + }, + }, + "annotations": { + Type: "object", + AdditionalProperties: &extv1.JSONSchemaPropsOrBool{ + Allows: true, + Schema: &extv1.JSONSchemaProps{Type: "string"}, + }, + }, + "type": { + Type: "string", + }, + }, + }, + }, + }, "writeConnectionSecretToRef": { Type: "object", Required: []string{"name", "namespace"}, @@ -637,6 +675,44 @@ func TestForCompositeResourceClaim(t *testing.T) { "name": {Type: "string"}, }, }, + "publishConnectionDetailsTo": { + Type: "object", + Required: []string{"name"}, + Properties: map[string]extv1.JSONSchemaProps{ + "name": {Type: "string"}, + "configRef": { + Type: "object", + Default: &extv1.JSON{Raw: []byte(`{"name": "default"}`)}, + Properties: map[string]extv1.JSONSchemaProps{ + "name": { + Type: "string", + }, + }, + }, + "metadata": { + Type: "object", + Properties: map[string]extv1.JSONSchemaProps{ + "labels": { + Type: "object", + AdditionalProperties: &extv1.JSONSchemaPropsOrBool{ + Allows: true, + Schema: &extv1.JSONSchemaProps{Type: "string"}, + }, + }, + "annotations": { + Type: "object", + AdditionalProperties: &extv1.JSONSchemaPropsOrBool{ + Allows: true, + Schema: &extv1.JSONSchemaProps{Type: "string"}, + }, + }, + "type": { + Type: "string", + }, + }, + }, + }, + }, "writeConnectionSecretToRef": { Type: "object", Required: []string{"name"}, diff --git a/internal/xcrd/schemas.go b/internal/xcrd/schemas.go index 57afa63fc..2ef0d274b 100644 --- a/internal/xcrd/schemas.go +++ b/internal/xcrd/schemas.go @@ -127,6 +127,44 @@ func CompositeResourceSpecProps() map[string]extv1.JSONSchemaProps { }, }, }, + "publishConnectionDetailsTo": { + Type: "object", + Required: []string{"name"}, + Properties: map[string]extv1.JSONSchemaProps{ + "name": {Type: "string"}, + "configRef": { + Type: "object", + Default: &extv1.JSON{Raw: []byte(`{"name": "default"}`)}, + Properties: map[string]extv1.JSONSchemaProps{ + "name": { + Type: "string", + }, + }, + }, + "metadata": { + Type: "object", + Properties: map[string]extv1.JSONSchemaProps{ + "labels": { + Type: "object", + AdditionalProperties: &extv1.JSONSchemaPropsOrBool{ + Allows: true, + Schema: &extv1.JSONSchemaProps{Type: "string"}, + }, + }, + "annotations": { + Type: "object", + AdditionalProperties: &extv1.JSONSchemaPropsOrBool{ + Allows: true, + Schema: &extv1.JSONSchemaProps{Type: "string"}, + }, + }, + "type": { + Type: "string", + }, + }, + }, + }, + }, "writeConnectionSecretToRef": { Type: "object", Required: []string{"name", "namespace"}, @@ -187,6 +225,44 @@ func CompositeResourceClaimSpecProps() map[string]extv1.JSONSchemaProps { "name": {Type: "string"}, }, }, + "publishConnectionDetailsTo": { + Type: "object", + Required: []string{"name"}, + Properties: map[string]extv1.JSONSchemaProps{ + "name": {Type: "string"}, + "configRef": { + Type: "object", + Default: &extv1.JSON{Raw: []byte(`{"name": "default"}`)}, + Properties: map[string]extv1.JSONSchemaProps{ + "name": { + Type: "string", + }, + }, + }, + "metadata": { + Type: "object", + Properties: map[string]extv1.JSONSchemaProps{ + "labels": { + Type: "object", + AdditionalProperties: &extv1.JSONSchemaPropsOrBool{ + Allows: true, + Schema: &extv1.JSONSchemaProps{Type: "string"}, + }, + }, + "annotations": { + Type: "object", + AdditionalProperties: &extv1.JSONSchemaPropsOrBool{ + Allows: true, + Schema: &extv1.JSONSchemaProps{Type: "string"}, + }, + }, + "type": { + Type: "string", + }, + }, + }, + }, + }, "writeConnectionSecretToRef": { Type: "object", Required: []string{"name"}, diff --git a/internal/xpkg/build.go b/internal/xpkg/build.go index 76ef41d0d..d7dc60b0f 100644 --- a/internal/xpkg/build.go +++ b/internal/xpkg/build.go @@ -117,7 +117,14 @@ func Build(ctx context.Context, b parser.Backend, p parser.Parser, l parser.Lint } // Build image layer from tarball. - layer, err := tarball.LayerFromReader(tarBuf) + // TODO(hasheddan): we construct a new reader each time the layer is read, + // once for calculating the digest, which is used in choosing package file + // name if not set, and once for writing the contents to disk. This can be + // optimized in the future, along with the fact that we are copying the full + // package contents into memory above. + layer, err := tarball.LayerFromOpener(func() (io.ReadCloser, error) { + return io.NopCloser(bytes.NewReader(tarBuf.Bytes())), nil + }) if err != nil { return nil, errors.Wrap(err, errLayerFromTar) } diff --git a/internal/xpkg/cache.go b/internal/xpkg/cache.go index 9dcac2e75..903612e9b 100644 --- a/internal/xpkg/cache.go +++ b/internal/xpkg/cache.go @@ -17,95 +17,103 @@ limitations under the License. package xpkg import ( + "compress/gzip" "io" "os" "sync" - "github.com/google/go-containerregistry/pkg/name" - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/google/go-containerregistry/pkg/v1/tarball" "github.com/spf13/afero" "github.com/crossplane/crossplane-runtime/pkg/errors" ) const ( - errGetNopCache = "cannot get an image from a NopCache" + errGetNopCache = "cannot get content from a NopCache" ) -// A Cache caches OCI images. -type Cache interface { - Get(tag string, id string) (v1.Image, error) - Store(tag string, id string, img v1.Image) error +const cacheContentExt = ".gz" + +// A PackageCache caches package content. +type PackageCache interface { + Has(id string) bool + Get(id string) (io.ReadCloser, error) + Store(id string, content io.ReadCloser) error Delete(id string) error } -// ImageCache stores and retrieves OCI images in a filesystem-backed cache in a -// thread-safe manner. -type ImageCache struct { +// FsPackageCache stores and retrieves package content in a filesystem-backed +// cache in a thread-safe manner. +type FsPackageCache struct { dir string fs afero.Fs mu sync.RWMutex } -// NewImageCache creates a new ImageCache. -func NewImageCache(dir string, fs afero.Fs) *ImageCache { - return &ImageCache{ +// NewFsPackageCache creates a new FsPackageCache. +func NewFsPackageCache(dir string, fs afero.Fs) *FsPackageCache { + return &FsPackageCache{ dir: dir, fs: fs, } } -// Get retrieves an image from the ImageCache. -func (c *ImageCache) Get(tag, id string) (v1.Image, error) { +// Has indicates whether an item with the given id is in the cache. +func (c *FsPackageCache) Has(id string) bool { + if fi, err := c.fs.Stat(BuildPath(c.dir, id, cacheContentExt)); err == nil && !fi.IsDir() { + return true + } + return false +} + +// Get retrieves package contents from the cache. +func (c *FsPackageCache) Get(id string) (io.ReadCloser, error) { c.mu.RLock() defer c.mu.RUnlock() - var t *name.Tag - if tag != "" { - nt, err := name.NewTag(tag) - if err != nil { - return nil, err - } - t = &nt + f, err := c.fs.Open(BuildPath(c.dir, id, cacheContentExt)) + if err != nil { + return nil, err } - return tarball.Image(fsOpener(BuildPath(c.dir, id), c.fs), t) + return GzipReadCloser(f) } -// Store saves an image to the ImageCache. -func (c *ImageCache) Store(tag, id string, img v1.Image) error { +// Store saves the package contents to the cache. +func (c *FsPackageCache) Store(id string, content io.ReadCloser) error { c.mu.Lock() defer c.mu.Unlock() - ref, err := name.ParseReference(tag) + cf, err := c.fs.Create(BuildPath(c.dir, id, cacheContentExt)) if err != nil { return err } - cf, err := c.fs.Create(BuildPath(c.dir, id)) + // NOTE(hasheddan): we don't check error on deferred file close as Close() + // is explicitly called in the happy path. + defer cf.Close() //nolint:errcheck + w, err := gzip.NewWriterLevel(cf, gzip.BestSpeed) if err != nil { return err } - if err := tarball.Write(ref, img, cf); err != nil { + _, err = io.Copy(w, content) + if err != nil { + return err + } + // NOTE(hasheddan): gzip writer must be closed to ensure all data is flushed + // to file. + if err := w.Close(); err != nil { return err } return cf.Close() } -// Delete removes an image from the ImageCache. -func (c *ImageCache) Delete(id string) error { +// Delete removes package contents from the cache. +func (c *FsPackageCache) Delete(id string) error { c.mu.Lock() defer c.mu.Unlock() - err := c.fs.Remove(BuildPath(c.dir, id)) + err := c.fs.Remove(BuildPath(c.dir, id, cacheContentExt)) if os.IsNotExist(err) { return nil } return err } -func fsOpener(path string, fs afero.Fs) tarball.Opener { - return func() (io.ReadCloser, error) { - return fs.Open(path) - } -} - // NopCache is a cache implementation that does not store anything and always // returns an error on get. type NopCache struct{} @@ -115,17 +123,22 @@ func NewNopCache() *NopCache { return &NopCache{} } -// Get retrieves an image from the NopCache. -func (c *NopCache) Get(tag, id string) (v1.Image, error) { +// Has indicates whether content is in the NopCache. +func (c *NopCache) Has(string) bool { + return false +} + +// Get retrieves content from the NopCache. +func (c *NopCache) Get(string) (io.ReadCloser, error) { return nil, errors.New(errGetNopCache) } -// Store saves an image to the NopCache. -func (c *NopCache) Store(tag, id string, img v1.Image) error { +// Store saves content to the NopCache. +func (c *NopCache) Store(string, io.ReadCloser) error { return nil } -// Delete removes an image from the NopCache. -func (c *NopCache) Delete(id string) error { +// Delete removes content from the NopCache. +func (c *NopCache) Delete(string) error { return nil } diff --git a/internal/xpkg/cache_test.go b/internal/xpkg/cache_test.go index 408dd650e..d713265d1 100644 --- a/internal/xpkg/cache_test.go +++ b/internal/xpkg/cache_test.go @@ -17,28 +17,84 @@ limitations under the License. package xpkg import ( + "bytes" + "compress/gzip" + "io" "os" "syscall" "testing" "github.com/google/go-cmp/cmp" - "github.com/google/go-containerregistry/pkg/name" - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/google/go-containerregistry/pkg/v1/empty" - "github.com/google/go-containerregistry/pkg/v1/tarball" "github.com/spf13/afero" "github.com/crossplane/crossplane-runtime/pkg/test" ) +var _ PackageCache = &FsPackageCache{} + +func TestHas(t *testing.T) { + fs := afero.NewMemMapFs() + cf, _ := fs.Create("/cache/exists.gz") + _ = fs.Mkdir("/cache/some-dir.gz", os.ModeDir) + defer cf.Close() + + type args struct { + cache PackageCache + id string + } + cases := map[string]struct { + reason string + args args + want bool + }{ + "Success": { + reason: "Should not return an error if package exists at path.", + args: args{ + cache: NewFsPackageCache("/cache", fs), + id: "exists", + }, + want: true, + }, + "ErrNotExist": { + reason: "Should return error if package does not exist at path.", + args: args{ + cache: NewFsPackageCache("/cache", fs), + id: "not-exist", + }, + want: false, + }, + "ErrIsDir": { + reason: "Should return error if path is a directory.", + args: args{ + cache: NewFsPackageCache("/cache", fs), + id: "some-dir.gz", + }, + want: false, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + h := tc.args.cache.Has(tc.args.id) + + if diff := cmp.Diff(tc.want, h); diff != "" { + t.Errorf("\n%s\nHas(...): -want, +got:\n%s", tc.reason, diff) + } + }) + } +} + func TestGet(t *testing.T) { fs := afero.NewMemMapFs() - cf, _ := fs.Create("/cache/exists.xpkg") - _ = tarball.Write(name.Tag{}, empty.Image, cf) + cf, _ := fs.Create("/cache/exists.gz") + // NOTE(hasheddan): valid gzip header. + cf.Write([]byte{31, 139, 8, 0, 0, 0, 0, 0, 0, 0}) + cf, _ = fs.Create("/cache/not-gzip.gz") + cf.Write([]byte("some content")) + defer cf.Close() type args struct { - cache Cache - tag string + cache PackageCache id string } cases := map[string]struct { @@ -49,25 +105,31 @@ func TestGet(t *testing.T) { "Success": { reason: "Should not return an error if package exists at path.", args: args{ - cache: NewImageCache("/cache", fs), - tag: "", + cache: NewFsPackageCache("/cache", fs), id: "exists", }, }, + "ErrNotGzip": { + reason: "Should return error if package does not exist at path.", + args: args{ + cache: NewFsPackageCache("/cache", fs), + id: "not-gzip", + }, + want: gzip.ErrHeader, + }, "ErrNotExist": { reason: "Should return error if package does not exist at path.", args: args{ - cache: NewImageCache("/cache", fs), - tag: "", + cache: NewFsPackageCache("/cache", fs), id: "not-exist", }, - want: &os.PathError{Op: "open", Path: "/cache/not-exist.xpkg", Err: afero.ErrFileNotFound}, + want: &os.PathError{Op: "open", Path: "/cache/not-exist.gz", Err: afero.ErrFileNotFound}, }, } for name, tc := range cases { t.Run(name, func(t *testing.T) { - _, err := tc.args.cache.Get(tc.args.tag, tc.args.id) + _, err := tc.args.cache.Get(tc.args.id) if diff := cmp.Diff(tc.want, err, test.EquateErrors()); diff != "" { t.Errorf("\n%s\nGet(...): -want err, +got err:\n%s", tc.reason, diff) @@ -80,10 +142,8 @@ func TestStore(t *testing.T) { fs := afero.NewMemMapFs() type args struct { - cache Cache - tag string + cache PackageCache id string - img v1.Image } cases := map[string]struct { reason string @@ -93,19 +153,15 @@ func TestStore(t *testing.T) { "Success": { reason: "Should not return an error if package is created at path.", args: args{ - cache: NewImageCache("/cache", fs), - tag: "crossplane/exist-xpkg:latest", + cache: NewFsPackageCache("/cache", fs), id: "exists-1234567", - img: empty.Image, }, }, "ErrFailedCreate": { reason: "Should return an error if file creation fails.", args: args{ - cache: NewImageCache("/cache", afero.NewReadOnlyFs(fs)), - tag: "crossplane/exist-xpkg:latest", + cache: NewFsPackageCache("/cache", afero.NewReadOnlyFs(fs)), id: "exists-1234567", - img: empty.Image, }, want: syscall.EPERM, }, @@ -113,7 +169,7 @@ func TestStore(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { - err := tc.args.cache.Store(tc.args.tag, tc.args.id, tc.args.img) + err := tc.args.cache.Store(tc.args.id, io.NopCloser(new(bytes.Buffer))) if diff := cmp.Diff(tc.want, err, test.EquateErrors()); diff != "" { t.Errorf("\n%s\nStore(...): -want err, +got err:\n%s", tc.reason, diff) @@ -124,11 +180,10 @@ func TestStore(t *testing.T) { func TestDelete(t *testing.T) { fs := afero.NewMemMapFs() - cf, _ := fs.Create("/cache/exists.xpkg") - _ = tarball.Write(name.Tag{}, empty.Image, cf) + _, _ = fs.Create("/cache/exists.xpkg") type args struct { - cache Cache + cache PackageCache id string } cases := map[string]struct { @@ -139,21 +194,21 @@ func TestDelete(t *testing.T) { "Success": { reason: "Should not return an error if package is deleted at path.", args: args{ - cache: NewImageCache("/cache", fs), + cache: NewFsPackageCache("/cache", fs), id: "exists", }, }, "SuccessNotExist": { reason: "Should not return an error if package does not exist.", args: args{ - cache: NewImageCache("/cache", fs), + cache: NewFsPackageCache("/cache", fs), id: "not-exist", }, }, "ErrFailedDelete": { reason: "Should return an error if file deletion fails.", args: args{ - cache: NewImageCache("/cache", afero.NewReadOnlyFs(fs)), + cache: NewFsPackageCache("/cache", afero.NewReadOnlyFs(fs)), id: "exists-1234567", }, want: syscall.EPERM, diff --git a/internal/xpkg/fake/mocks.go b/internal/xpkg/fake/mocks.go index 0b2753bc9..e06fbbd6b 100644 --- a/internal/xpkg/fake/mocks.go +++ b/internal/xpkg/fake/mocks.go @@ -18,6 +18,7 @@ package fake import ( "context" + "io" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" @@ -25,23 +26,29 @@ import ( "github.com/crossplane/crossplane/internal/xpkg" ) -var _ xpkg.Cache = &MockCache{} +var _ xpkg.PackageCache = &MockCache{} // MockCache is a mock Cache. type MockCache struct { - MockGet func() (v1.Image, error) - MockStore func() error + MockHas func() bool + MockGet func() (io.ReadCloser, error) + MockStore func(s string, rc io.ReadCloser) error MockDelete func() error } +// NewMockCacheHasFn creates a new MockGet function for MockCache. +func NewMockCacheHasFn(has bool) func() bool { + return func() bool { return has } +} + // NewMockCacheGetFn creates a new MockGet function for MockCache. -func NewMockCacheGetFn(img v1.Image, err error) func() (v1.Image, error) { - return func() (v1.Image, error) { return img, err } +func NewMockCacheGetFn(rc io.ReadCloser, err error) func() (io.ReadCloser, error) { + return func() (io.ReadCloser, error) { return rc, err } } // NewMockCacheStoreFn creates a new MockStore function for MockCache. -func NewMockCacheStoreFn(err error) func() error { - return func() error { return err } +func NewMockCacheStoreFn(err error) func(s string, rc io.ReadCloser) error { + return func(s string, rc io.ReadCloser) error { return err } } // NewMockCacheDeleteFn creates a new MockDelete function for MockCache. @@ -49,18 +56,23 @@ func NewMockCacheDeleteFn(err error) func() error { return func() error { return err } } +// Has calls the underlying MockHas. +func (c *MockCache) Has(string) bool { + return c.MockHas() +} + // Get calls the underlying MockGet. -func (c *MockCache) Get(source, id string) (v1.Image, error) { +func (c *MockCache) Get(string) (io.ReadCloser, error) { return c.MockGet() } // Store calls the underlying MockStore. -func (c *MockCache) Store(source, id string, img v1.Image) error { - return c.MockStore() +func (c *MockCache) Store(s string, rc io.ReadCloser) error { + return c.MockStore(s, rc) } // Delete calls the underlying MockDelete. -func (c *MockCache) Delete(id string) error { +func (c *MockCache) Delete(string) error { return c.MockDelete() } diff --git a/internal/xpkg/lint.go b/internal/xpkg/lint.go index f7b501df3..89660d233 100644 --- a/internal/xpkg/lint.go +++ b/internal/xpkg/lint.go @@ -18,6 +18,7 @@ package xpkg import ( "github.com/Masterminds/semver" + admv1 "k8s.io/api/admissionregistration/v1" extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" extv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" "k8s.io/apimachinery/pkg/runtime" @@ -30,21 +31,28 @@ import ( ) const ( - errNotExactlyOneMeta = "not exactly one package meta type" - errNotMeta = "meta type is not a package" - errNotMetaProvider = "package meta type is not Provider" - errNotMetaConfiguration = "package meta type is not Configuration" - errNotCRD = "object is not a CRD" - errNotXRD = "object is not an XRD" - errNotComposition = "object is not a Composition" - errBadConstraints = "package version constraints are poorly formatted" - errCrossplaneIncompatibleFmt = "package is not compatible with Crossplane version (%s)" + errNotExactlyOneMeta = "not exactly one package meta type" + errNotMeta = "meta type is not a package" + errNotMetaProvider = "package meta type is not Provider" + errNotMetaConfiguration = "package meta type is not Configuration" + errNotCRD = "object is not a CRD" + errNotXRD = "object is not an XRD" + errNotMutatingWebhookConfiguration = "object is not a MutatingWebhookConfiguration" + errNotValidatingWebhookConfiguration = "object is not an ValidatingWebhookConfiguration" + errNotComposition = "object is not a Composition" + errBadConstraints = "package version constraints are poorly formatted" + errCrossplaneIncompatibleFmt = "package is not compatible with Crossplane version (%s)" ) // NewProviderLinter is a convenience function for creating a package linter for // providers. func NewProviderLinter() parser.Linter { - return parser.NewPackageLinter(parser.PackageLinterFns(OneMeta), parser.ObjectLinterFns(IsProvider, PackageValidSemver), parser.ObjectLinterFns(IsCRD)) + return parser.NewPackageLinter(parser.PackageLinterFns(OneMeta), parser.ObjectLinterFns(IsProvider, PackageValidSemver), + parser.ObjectLinterFns(parser.Or( + IsCRD, + IsValidatingWebhookConfiguration, + IsMutatingWebhookConfiguration, + ))) } // NewConfigurationLinter is a convenience function for creating a package linter for @@ -128,6 +136,22 @@ func IsCRD(o runtime.Object) error { } } +// IsMutatingWebhookConfiguration checks that an object is a MutatingWebhookConfiguration. +func IsMutatingWebhookConfiguration(o runtime.Object) error { + if _, ok := o.(*admv1.MutatingWebhookConfiguration); !ok { + return errors.New(errNotMutatingWebhookConfiguration) + } + return nil +} + +// IsValidatingWebhookConfiguration checks that an object is a ValidatingWebhookConfiguration. +func IsValidatingWebhookConfiguration(o runtime.Object) error { + if _, ok := o.(*admv1.ValidatingWebhookConfiguration); !ok { + return errors.New(errNotValidatingWebhookConfiguration) + } + return nil +} + // IsXRD checks that an object is a CompositeResourceDefinition. func IsXRD(o runtime.Object) error { if _, ok := o.(*v1.CompositeResourceDefinition); !ok { diff --git a/internal/xpkg/name.go b/internal/xpkg/name.go index dddda61a8..c651dea4e 100644 --- a/internal/xpkg/name.go +++ b/internal/xpkg/name.go @@ -82,12 +82,11 @@ func ToDNSLabel(s string) string { // nolint:gocyclo return strings.Trim(cut.String(), "-") } -// BuildPath builds a path for a compiled Crossplane package. If file name has -// extension it will be replaced. -func BuildPath(path, name string) string { +// BuildPath builds a path with the provided extension. +func BuildPath(path, name, ext string) string { full := filepath.Join(path, name) - ext := filepath.Ext(full) - return full[0:len(full)-len(ext)] + XpkgExtension + existExt := filepath.Ext(full) + return full[0:len(full)-len(existExt)] + ext } // ParseNameFromMeta extracts the package name from its meta file. diff --git a/internal/xpkg/name_test.go b/internal/xpkg/name_test.go index 18db0fdd7..1bb6e4a53 100644 --- a/internal/xpkg/name_test.go +++ b/internal/xpkg/name_test.go @@ -184,6 +184,7 @@ func TestBuildPath(t *testing.T) { type args struct { path string name string + ext string } cases := map[string]struct { @@ -196,6 +197,7 @@ func TestBuildPath(t *testing.T) { args: args{ path: "path/to/somewhere", name: "test", + ext: XpkgExtension, }, want: "path/to/somewhere/test.xpkg", }, @@ -204,6 +206,7 @@ func TestBuildPath(t *testing.T) { args: args{ path: "path/to/somewhere", name: "test.tar", + ext: XpkgExtension, }, want: "path/to/somewhere/test.xpkg", }, @@ -212,6 +215,7 @@ func TestBuildPath(t *testing.T) { args: args{ path: "path/to/somewhere.tar", name: "", + ext: XpkgExtension, }, want: "path/to/somewhere.xpkg", }, @@ -219,7 +223,7 @@ func TestBuildPath(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { - full := BuildPath(tc.args.path, tc.args.name) + full := BuildPath(tc.args.path, tc.args.name, tc.args.ext) if diff := cmp.Diff(tc.want, full); diff != "" { t.Errorf("\n%s\nBuildPath(...): -want, +got:\n%s", tc.reason, diff) diff --git a/internal/xpkg/reader.go b/internal/xpkg/reader.go new file mode 100644 index 000000000..1593602ef --- /dev/null +++ b/internal/xpkg/reader.go @@ -0,0 +1,116 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package xpkg + +import ( + "compress/gzip" + "io" +) + +var _ io.ReadCloser = &gzipReadCloser{} + +// gzipReadCloser reads compressed contents from a file. +type gzipReadCloser struct { + rc io.ReadCloser + gzip *gzip.Reader +} + +// GzipReadCloser constructs a new gzipReadCloser from the passed file. +func GzipReadCloser(rc io.ReadCloser) (io.ReadCloser, error) { + r, err := gzip.NewReader(rc) + if err != nil { + return nil, err + } + return &gzipReadCloser{ + rc: rc, + gzip: r, + }, nil +} + +// Read calls the underlying gzip reader's Read method. +func (g *gzipReadCloser) Read(p []byte) (n int, err error) { + return g.gzip.Read(p) +} + +// Close first closes the gzip reader, then closes the underlying closer. +func (g *gzipReadCloser) Close() error { + defer g.rc.Close() //nolint:errcheck + if err := g.gzip.Close(); err != nil { + return err + } + return g.rc.Close() +} + +var _ io.ReadCloser = &teeReadCloser{} + +// teeReadCloser is a TeeReader that also closes the underlying writer. +type teeReadCloser struct { + w io.WriteCloser + r io.ReadCloser + t io.Reader +} + +// TeeReadCloser constructs a teeReadCloser from the passed reader and writer. +func TeeReadCloser(r io.ReadCloser, w io.WriteCloser) io.ReadCloser { + return &teeReadCloser{ + w: w, + r: r, + t: io.TeeReader(r, w), + } +} + +// Read calls the underlying TeeReader Read method. +func (t *teeReadCloser) Read(b []byte) (int, error) { + return t.t.Read(b) +} + +// Close closes the underlying ReadCloser, then the Writer for the TeeReader. +func (t *teeReadCloser) Close() error { + defer t.w.Close() //nolint:errcheck + if err := t.r.Close(); err != nil { + return err + } + return t.w.Close() +} + +var _ io.ReadCloser = &joinedReadCloser{} + +// joinedReadCloster joins a reader and a closer. It is typically used in the +// context of a ReadCloser being wrapped by a Reader. +type joinedReadCloser struct { + r io.Reader + c io.Closer +} + +// JoinedReadCloser constructs a new joinedReadCloser from the passed reader and +// closer. +func JoinedReadCloser(r io.Reader, c io.Closer) io.ReadCloser { + return &joinedReadCloser{ + r: r, + c: c, + } +} + +// Read calls the underlying reader Read method. +func (r *joinedReadCloser) Read(b []byte) (int, error) { + return r.r.Read(b) +} + +// Close closes the closer for the JoinedReadCloser. +func (r *joinedReadCloser) Close() error { + return r.c.Close() +} diff --git a/internal/xpkg/scheme.go b/internal/xpkg/scheme.go index ee15a2ad0..5cf3a935c 100644 --- a/internal/xpkg/scheme.go +++ b/internal/xpkg/scheme.go @@ -17,6 +17,7 @@ limitations under the License. package xpkg import ( + admv1 "k8s.io/api/admissionregistration/v1" extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" extv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" "k8s.io/apimachinery/pkg/runtime" @@ -53,6 +54,9 @@ func BuildObjectScheme() (*runtime.Scheme, error) { if err := extv1.AddToScheme(objScheme); err != nil { return nil, err } + if err := admv1.AddToScheme(objScheme); err != nil { + return nil, err + } return objScheme, nil }