diff --git a/.gitignore b/.gitignore index 08900955..17a67ac3 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ terraform-controller-* examples/tf-native/alibaba/cs/kubeconfig bin/manager + +# Secret for git server +examples/git-credentials/git-ssh-auth-secret.yaml \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml index 704b0358..c05796e1 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -44,7 +44,7 @@ linters-settings: gocyclo: # minimal code complexity to report, 30 by default (but we recommend 10-20) - min-complexity: 30 + min-complexity: 32 maligned: # print struct with more effective memory layout or not, false by default @@ -179,6 +179,14 @@ issues: linters: - revive + - text: "package-comments:" + linters: + - revive + + - text: "exported:" + linters: + - revive + # Independently from option `exclude` we use default exclude patterns, # it can be disabled by this option. To list all # excluded by default patterns execute `golangci-lint run --help`. diff --git a/Makefile b/Makefile index 965fd7e6..eea05b2f 100644 --- a/Makefile +++ b/Makefile @@ -90,7 +90,7 @@ else CONTROLLER_GEN=$(shell which controller-gen) endif -GOLANGCILINT_VERSION ?= v1.38.0 +GOLANGCILINT_VERSION ?= v1.50.1 HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]') HOSTARCH := $(shell uname -m) ifeq ($(HOSTARCH),x86_64) diff --git a/api/types/state.go b/api/types/state.go index 762ea34c..5a1072f5 100644 --- a/api/types/state.go +++ b/api/types/state.go @@ -34,6 +34,7 @@ const ( GeneratingOutputs ConfigurationState = "GeneratingTerraformOutputs" InvalidRegion ConfigurationState = "InvalidRegion" TerraformInitError ConfigurationState = "TerraformInitError" + InvalidGitCredentialsSecretReference ConfigurationState = "InvalidGitCredentialsSecretReference" ) // Stage is the Terraform stage diff --git a/api/v1beta1/configuration_types.go b/api/v1beta1/configuration_types.go index 9955b8cb..060cb2a5 100644 --- a/api/v1beta1/configuration_types.go +++ b/api/v1beta1/configuration_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1beta1 import ( + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -47,6 +48,9 @@ type ConfigurationSpec struct { Path string `json:"path,omitempty"` BaseConfigurationSpec `json:",inline"` + + // GitCredentialsSecretReference specifies the reference to the secret containing the git credentials + GitCredentialsSecretReference *v1.SecretReference `json:"gitCredentialsSecretReference,omitempty"` } // BaseConfigurationSpec defines the common fields of a ConfigurationSpec @@ -113,6 +117,7 @@ type Backend struct { // +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="STATE",type="string",JSONPath=".status.apply.state" // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:resource:shortName={conf,terraform-conf} type Configuration struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index e9542cca..21d9e64b 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -23,6 +23,7 @@ package v1beta1 import ( crossplane_runtime "github.com/oam-dev/terraform-controller/api/types/crossplane-runtime" + "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -176,6 +177,11 @@ func (in *ConfigurationSpec) DeepCopyInto(out *ConfigurationSpec) { **out = **in } in.BaseConfigurationSpec.DeepCopyInto(&out.BaseConfigurationSpec) + if in.GitCredentialsSecretReference != nil { + in, out := &in.GitCredentialsSecretReference, &out.GitCredentialsSecretReference + *out = new(v1.SecretReference) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigurationSpec. diff --git a/api/v1beta2/configuration_types.go b/api/v1beta2/configuration_types.go index 80294862..146e8ddf 100644 --- a/api/v1beta2/configuration_types.go +++ b/api/v1beta2/configuration_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1beta2 import ( + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -75,6 +76,9 @@ type ConfigurationSpec struct { // ForceDelete will force delete Configuration no matter which state it is or whether it has provisioned some resources // It will help delete Configuration in unexpected cases. ForceDelete *bool `json:"forceDelete,omitempty"` + + // GitCredentialsSecretReference specifies the reference to the secret containing the git credentials + GitCredentialsSecretReference *v1.SecretReference `json:"gitCredentialsSecretReference,omitempty"` } // ConfigurationStatus defines the observed state of Configuration @@ -150,7 +154,7 @@ type S3BackendConf struct { // +kubebuilder:object:root=true // Configuration is the Schema for the configurations API -//+kubebuilder:storageversion +// +kubebuilder:storageversion // +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="STATE",type="string",JSONPath=".status.apply.state" // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" diff --git a/api/v1beta2/zz_generated.deepcopy.go b/api/v1beta2/zz_generated.deepcopy.go index 1541202c..64795fa0 100644 --- a/api/v1beta2/zz_generated.deepcopy.go +++ b/api/v1beta2/zz_generated.deepcopy.go @@ -23,6 +23,7 @@ package v1beta2 import ( crossplane_runtime "github.com/oam-dev/terraform-controller/api/types/crossplane-runtime" + "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -170,6 +171,11 @@ func (in *ConfigurationSpec) DeepCopyInto(out *ConfigurationSpec) { *out = new(crossplane_runtime.Reference) **out = **in } + if in.JobEnv != nil { + in, out := &in.JobEnv, &out.JobEnv + *out = new(runtime.RawExtension) + (*in).DeepCopyInto(*out) + } if in.DeleteResource != nil { in, out := &in.DeleteResource, &out.DeleteResource *out = new(bool) @@ -180,6 +186,11 @@ func (in *ConfigurationSpec) DeepCopyInto(out *ConfigurationSpec) { *out = new(bool) **out = **in } + if in.GitCredentialsSecretReference != nil { + in, out := &in.GitCredentialsSecretReference, &out.GitCredentialsSecretReference + *out = new(v1.SecretReference) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigurationSpec. diff --git a/chart/crds/terraform.core.oam.dev_configurations.yaml b/chart/crds/terraform.core.oam.dev_configurations.yaml index 9e2640f4..8b964b24 100644 --- a/chart/crds/terraform.core.oam.dev_configurations.yaml +++ b/chart/crds/terraform.core.oam.dev_configurations.yaml @@ -13,6 +13,9 @@ spec: kind: Configuration listKind: ConfigurationList plural: configurations + shortNames: + - conf + - terraform-conf singular: configuration scope: Namespaced versions: @@ -68,6 +71,19 @@ spec: description: DeleteResource will determine whether provisioned cloud resources will be deleted when CR is deleted type: boolean + gitCredentialsSecretReference: + description: GitCredentialsSecretReference specifies the reference + to the secret containing the git credentials + properties: + name: + description: Name is unique within a namespace to reference a + secret resource. + type: string + namespace: + description: Namespace defines the space within which the secret + name must be unique. + type: string + type: object hcl: description: HCL is the Terraform HCL type configuration type: string @@ -187,6 +203,9 @@ spec: spec: description: ConfigurationSpec defines the desired state of Configuration properties: + JobEnv: + type: object + x-kubernetes-preserve-unknown-fields: true backend: description: 'Backend describes the Terraform backend configuration. This field is needed if the users use a git repo to provide the @@ -256,6 +275,19 @@ spec: which state it is or whether it has provisioned some resources It will help delete Configuration in unexpected cases. type: boolean + gitCredentialsSecretReference: + description: GitCredentialsSecretReference specifies the reference + to the secret containing the git credentials + properties: + name: + description: Name is unique within a namespace to reference a + secret resource. + type: string + namespace: + description: Namespace defines the space within which the secret + name must be unique. + type: string + type: object hcl: description: HCL is the Terraform HCL type configuration type: string @@ -282,9 +314,6 @@ spec: required: - name type: object - JobEnv: - type: object - x-kubernetes-preserve-unknown-fields: true remote: description: Remote is a git repo which contains hcl files. Currently, only public git repos are supported. diff --git a/controllers/configuration_controller.go b/controllers/configuration_controller.go index a2ceaa2e..83a2695a 100644 --- a/controllers/configuration_controller.go +++ b/controllers/configuration_controller.go @@ -65,6 +65,10 @@ const ( // terraformContainerName is the name of the container that executes the terraform in the pod terraformContainerName = "terraform-executor" terraformInitContainerName = "terraform-init" + // GitAuthConfigVolumeName is the volume name for git auth configurtaion + GitAuthConfigVolumeName = "git-auth-configuration" + // GitAuthConfigVolumeMountPath is the volume mount path for git auth configurtaion + GitAuthConfigVolumeMountPath = "/root/.ssh" ) const ( @@ -92,6 +96,10 @@ const ( ServiceAccountName = "tf-executor-service-account" ) +const ( + GitCredsKnownHosts = "known_hosts" +) + // ConfigurationReconciler reconciles a Configuration object. type ConfigurationReconciler struct { client.Client @@ -228,26 +236,27 @@ type ResourceQuota struct { // TFConfigurationMeta is all the metadata of a Configuration type TFConfigurationMeta struct { - Name string - Namespace string - ControllerNamespace string - ConfigurationType types.ConfigurationType - CompleteConfiguration string - RemoteGit string - RemoteGitPath string - ConfigurationChanged bool - EnvChanged bool - ConfigurationCMName string - ApplyJobName string - DestroyJobName string - Envs []v1.EnvVar - ProviderReference *crossplane.Reference - VariableSecretName string - VariableSecretData map[string][]byte - DeleteResource bool - Region string - Credentials map[string]string - JobEnv map[string]interface{} + Name string + Namespace string + ControllerNamespace string + ConfigurationType types.ConfigurationType + CompleteConfiguration string + RemoteGit string + RemoteGitPath string + ConfigurationChanged bool + EnvChanged bool + ConfigurationCMName string + ApplyJobName string + DestroyJobName string + Envs []v1.EnvVar + ProviderReference *crossplane.Reference + VariableSecretName string + VariableSecretData map[string][]byte + DeleteResource bool + Region string + Credentials map[string]string + JobEnv map[string]interface{} + GitCredentialsSecretReference *v1.SecretReference Backend backend.Backend // JobNodeSelector Expose the node selector of job to the controller level @@ -309,6 +318,10 @@ func initTFConfigurationMeta(req ctrl.Request, configuration v1beta2.Configurati meta.ProviderReference = tfcfg.GetProviderNamespacedName(configuration) } + if configuration.Spec.GitCredentialsSecretReference != nil { + meta.GitCredentialsSecretReference = configuration.Spec.GitCredentialsSecretReference + } + return meta } @@ -539,12 +552,25 @@ func (r *ConfigurationReconciler) preCheck(ctx context.Context, configuration *v } meta.JobEnv = jobEnv } - if err := meta.getCredentials(ctx, k8sClient, p); err != nil { return err } } + if meta.GitCredentialsSecretReference != nil { + gitCreds, err := GetGitCredentialsSecret(ctx, k8sClient, meta.GitCredentialsSecretReference) + if gitCreds == nil { + msg := string(types.InvalidGitCredentialsSecretReference) + if err != nil { + msg = err.Error() + } + if updateStatusErr := meta.updateApplyStatus(ctx, k8sClient, types.InvalidGitCredentialsSecretReference, msg); updateStatusErr != nil { + return errors.Wrap(updateStatusErr, msg) + } + return errors.New(msg) + } + } + // Render configuration with backend completeConfiguration, backendConf, err := meta.RenderConfiguration(configuration, configurationType) if err != nil { @@ -730,18 +756,33 @@ func (meta *TFConfigurationMeta) assembleTerraformJob(executionType TerraformExe hclPath := filepath.Join(BackendVolumeMountPath, meta.RemoteGitPath) if meta.RemoteGit != "" { + cloneCommand := fmt.Sprintf("git clone %s %s && cp -r %s/* %s", meta.RemoteGit, BackendVolumeMountPath, hclPath, WorkingVolumeMountPath) + + // Check for git credentials, mount the SSH known hosts and private key, add private key into the SSH authentication agent + if meta.GitCredentialsSecretReference != nil { + initContainerVolumeMounts = append(initContainerVolumeMounts, + v1.VolumeMount{ + Name: GitAuthConfigVolumeName, + MountPath: GitAuthConfigVolumeMountPath, + }) + + sshCommand := fmt.Sprintf("eval `ssh-agent` && ssh-add %s/%s", GitAuthConfigVolumeMountPath, v1.SSHAuthPrivateKey) + cloneCommand = fmt.Sprintf("%s && %s", sshCommand, cloneCommand) + } + + command := []string{ + "sh", + "-c", + cloneCommand, + } + initContainers = append(initContainers, v1.Container{ Name: "git-configuration", Image: meta.GitImage, ImagePullPolicy: v1.PullIfNotPresent, - Command: []string{ - "sh", - "-c", - fmt.Sprintf("git clone %s %s && cp -r %s/* %s", meta.RemoteGit, BackendVolumeMountPath, - hclPath, WorkingVolumeMountPath), - }, - VolumeMounts: initContainerVolumeMounts, + Command: command, + VolumeMounts: initContainerVolumeMounts, }) } @@ -855,7 +896,12 @@ func (meta *TFConfigurationMeta) assembleExecutorVolumes() []v1.Volume { workingVolume.EmptyDir = &v1.EmptyDirVolumeSource{} inputTFConfigurationVolume := meta.createConfigurationVolume() tfBackendVolume := meta.createTFBackendVolume() - return []v1.Volume{workingVolume, inputTFConfigurationVolume, tfBackendVolume} + executorVolumes := []v1.Volume{workingVolume, inputTFConfigurationVolume, tfBackendVolume} + if meta.GitCredentialsSecretReference != nil { + gitAuthConfigVolume := meta.createGitAuthConfigVolume() + executorVolumes = append(executorVolumes, gitAuthConfigVolume) + } + return executorVolumes } func (meta *TFConfigurationMeta) createConfigurationVolume() v1.Volume { @@ -873,6 +919,16 @@ func (meta *TFConfigurationMeta) createTFBackendVolume() v1.Volume { return gitVolume } +func (meta *TFConfigurationMeta) createGitAuthConfigVolume() v1.Volume { + var gitSecretDefaultMode int32 = 0400 + gitAuthSecretVolumeSource := v1.SecretVolumeSource{} + gitAuthSecretVolumeSource.SecretName = meta.GitCredentialsSecretReference.Name + gitAuthSecretVolumeSource.DefaultMode = &gitSecretDefaultMode + gitAuthSecretVolume := v1.Volume{Name: GitAuthConfigVolumeName} + gitAuthSecretVolume.Secret = &gitAuthSecretVolumeSource + return gitAuthSecretVolume +} + // TfStateProperty is the tf state property for an output type TfStateProperty struct { Value interface{} `json:"value,omitempty"` @@ -1279,3 +1335,26 @@ func (meta *TFConfigurationMeta) RenderConfiguration(configuration *v1beta2.Conf return "", nil, errors.New("Unsupported Configuration Type") } } + +// GetGitCredentialsSecret will get the secret containing the SSH private key & known_hosts +func GetGitCredentialsSecret(ctx context.Context, k8sClient client.Client, secretRef *v1.SecretReference) (*v1.Secret, error) { + secret := &v1.Secret{} + + namespacedName := client.ObjectKey{Name: secretRef.Name, Namespace: secretRef.Namespace} + err := k8sClient.Get(ctx, namespacedName, secret) + if err != nil { + errMsg := "Failed to get git credentials secret" + klog.ErrorS(err, errMsg, "Name", secretRef.Name, "Namespace", secretRef.Namespace) + return nil, errors.Wrap(err, errMsg) + } + + needSecretKeys := []string{GitCredsKnownHosts, v1.SSHAuthPrivateKey} + for _, key := range needSecretKeys { + if _, ok := secret.Data[key]; !ok { + err := errors.Errorf("'%s' not in git credentials secret", key) + return nil, err + } + } + + return secret, nil +} diff --git a/controllers/configuration_controller_test.go b/controllers/configuration_controller_test.go index 1fae88aa..68ff702e 100644 --- a/controllers/configuration_controller_test.go +++ b/controllers/configuration_controller_test.go @@ -1606,6 +1606,39 @@ func TestAssembleTerraformJobWithResourcesSetting(t *testing.T) { assert.Equal(t, "5Gi", requestsMemory.String()) } +func TestAssembleTerraformJobWithGitCredentialsSecretRef(t *testing.T) { + meta := &TFConfigurationMeta{ + Name: "a", + ConfigurationCMName: "b", + BusyboxImage: "c", + GitImage: "d", + Namespace: "e", + TerraformImage: "f", + RemoteGit: "g", + GitCredentialsSecretReference: &corev1.SecretReference{ + Namespace: "default", + Name: "git-ssh", + }, + } + + job := meta.assembleTerraformJob(TerraformApply) + spec := job.Spec.Template.Spec + + var gitSecretDefaultMode int32 = 0400 + gitAuthSecretVolume := corev1.Volume{Name: GitAuthConfigVolumeName} + gitAuthSecretVolume.Secret = &corev1.SecretVolumeSource{ + SecretName: "git-ssh", + DefaultMode: &gitSecretDefaultMode, + } + + gitSecretVolumeMount := corev1.VolumeMount{ + Name: GitAuthConfigVolumeName, + MountPath: GitAuthConfigVolumeMountPath, + } + assert.Contains(t, spec.InitContainers[1].VolumeMounts, gitSecretVolumeMount) + assert.Contains(t, spec.Volumes, gitAuthSecretVolume) +} + func TestTfStatePropertyToToProperty(t *testing.T) { testcases := []TfStateProperty{ { @@ -2497,3 +2530,131 @@ terraform { }) } } + +func TestCheckGitCredentialsSecretReference(t *testing.T) { + ctx := context.Background() + scheme := runtime.NewScheme() + corev1.AddToScheme(scheme) + k8sClient := fake.NewClientBuilder().WithScheme(scheme).Build() + + privateKey := []byte("aaa") + knownHosts := []byte("zzz") + secret := &corev1.Secret{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "default", + Name: "git-ssh", + }, + Data: map[string][]byte{ + corev1.SSHAuthPrivateKey: privateKey, + "known_hosts": knownHosts, + }, + Type: corev1.SecretTypeSSHAuth, + } + assert.Nil(t, k8sClient.Create(ctx, secret)) + assert.Nil(t, k8sClient.Get(ctx, client.ObjectKeyFromObject(secret), secret)) + + secretNoKnownhost := &corev1.Secret{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "default", + Name: "git-ssh-no-known-hosts", + }, + Data: map[string][]byte{ + corev1.SSHAuthPrivateKey: privateKey, + }, + Type: corev1.SecretTypeSSHAuth, + } + assert.Nil(t, k8sClient.Create(ctx, secretNoKnownhost)) + + secretNoPrivateKey := &corev1.Secret{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "default", + Name: "git-ssh-no-private-key", + }, + Data: map[string][]byte{ + "known_hosts": knownHosts, + }, + } + assert.Nil(t, k8sClient.Create(ctx, secretNoPrivateKey)) + + type args struct { + k8sClient client.Client + GitCredentialsSecretReference *corev1.SecretReference + } + + type want struct { + secret *corev1.Secret + errMsg string + } + + testcases := []struct { + name string + args args + want want + }{ + { + name: "secret not found", + args: args{ + k8sClient: k8sClient, + GitCredentialsSecretReference: &corev1.SecretReference{ + Namespace: "default", + Name: "git-shh", + }, + }, + want: want{ + errMsg: "Failed to get git credentials secret: secrets \"git-shh\" not found", + }, + }, + { + name: "key 'known_hosts' not in git credentials secret", + args: args{ + k8sClient: k8sClient, + GitCredentialsSecretReference: &corev1.SecretReference{ + Namespace: "default", + Name: "git-ssh-no-known-hosts", + }, + }, + want: want{ + errMsg: fmt.Sprintf("'%s' not in git credentials secret", GitCredsKnownHosts), + }, + }, + { + name: "key 'ssh-privatekey' not in git credentials secret", + args: args{ + k8sClient: k8sClient, + GitCredentialsSecretReference: &corev1.SecretReference{ + Namespace: "default", + Name: "git-ssh-no-private-key", + }, + }, + want: want{ + errMsg: fmt.Sprintf("'%s' not in git credentials secret", corev1.SSHAuthPrivateKey), + }, + }, + { + name: "secret exists", + args: args{ + k8sClient: k8sClient, + GitCredentialsSecretReference: &corev1.SecretReference{ + Namespace: "default", + Name: "git-ssh", + }, + }, + want: want{ + secret: secret, + }, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + sec, err := GetGitCredentialsSecret(ctx, tc.args.k8sClient, tc.args.GitCredentialsSecretReference) + + if err != nil { + assert.EqualError(t, err, tc.want.errMsg) + } + if tc.want.secret != nil { + assert.EqualValues(t, sec, tc.want.secret) + } + }) + } +} diff --git a/e2e/normal/configuration_test.go b/e2e/normal/configuration_test.go index b6f0575f..2c31a1e9 100644 --- a/e2e/normal/configuration_test.go +++ b/e2e/normal/configuration_test.go @@ -2,15 +2,18 @@ package normal import ( "context" + "encoding/base64" "fmt" "os" "os/exec" "path/filepath" "strings" "testing" + "text/template" "time" "gotest.tools/assert" + coreV1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -28,9 +31,9 @@ var ( "examples/alibaba/eip/configuration_eip_remote_subdirectory.yaml", "examples/alibaba/oss/configuration_hcl_bucket.yaml", } - testConfigurationsForceDelete = "examples/random/configuration_force_delete.yaml" - - chartNamespace = "terraform" + testConfigurationsForceDelete = "examples/random/configuration_force_delete.yaml" + testConfigurationsGitCredsSecretReference = "../examples/random/configuration_git_ssh.yaml" + chartNamespace = "terraform" ) type ConfigurationAttr struct { @@ -284,6 +287,81 @@ func TestForceDeleteConfiguration(t *testing.T) { } } +func TestGitCredentialsSecretReference(t *testing.T) { + configuration := ConfigurationAttr{ + Name: "random-e2e-git-creds-secret-ref", + YamlPath: testConfigurationsGitCredsSecretReference, + TFConfigMapName: "tf-random-e2e-git-creds-secret-ref", + BackendStateSecretName: "tfstate-default-random-e2e-git-creds-secret-ref", + OutputsSecretName: "random-e2e-git-creds-secret-ref-conn", + VariableSecretName: "variable-random-e2e-git-creds-secret-ref", + } + + clientSet, err := client.Init() + assert.NilError(t, err) + pwd, _ := os.Getwd() + gitServer := filepath.Join(pwd, "..", "../examples/git-credentials") + gitServerApplyCmd := fmt.Sprintf("kubectl apply -f %s", gitServer) + gitServerDeleteCmd := fmt.Sprintf("kubectl delete -f %s", gitServer) + gitSshAuthSecretYaml := filepath.Join(gitServer, "git-ssh-auth-secret.yaml") + + beforeApply := func(ctx *TestContext) { + err = exec.Command("bash", "-c", gitServerApplyCmd).Run() + assert.NilError(t, err) + + klog.Info("- Checking git-server pod status") + for i := 0; i < 120; i++ { + pod, _ := clientSet.CoreV1().Pods("default").Get(ctx, "git-server", v1.GetOptions{}) + conditions := pod.Status.Conditions + var index int + for count, condition := range conditions { + index = count + if condition.Status == "True" && condition.Type == coreV1.PodReady { + klog.Info("- pod=git-server ", condition.Type, "=", condition.Status) + break + } + } + if conditions[index].Status == "True" && conditions[index].Type == coreV1.PodReady { + break + } + if i == 119 { + t.Error("git-server pod is not running") + } + time.Sleep(10 * time.Second) + } + + getKnownHostsCmd := "kubectl exec pod/git-server -- ssh-keyscan git-server" + knownHosts, err := exec.Command("bash", "-c", getKnownHostsCmd).Output() + assert.NilError(t, err) + + gitSshAuthSecretTmpl := filepath.Join(gitServer, "templates/git-ssh-auth-secret.tmpl") + tmpl := template.Must(template.ParseFiles(gitSshAuthSecretTmpl)) + gitSshAuthSecretYamlFile, err := os.Create(gitSshAuthSecretYaml) + assert.NilError(t, err) + err = tmpl.Execute(gitSshAuthSecretYamlFile, base64.StdEncoding.EncodeToString(knownHosts)) + assert.NilError(t, err) + + err = exec.Command("bash", "-c", gitServerApplyCmd).Run() + assert.NilError(t, err) + } + + cleanUp := func(ctx *TestContext) { + err = exec.Command("bash", "-c", gitServerDeleteCmd).Run() + assert.NilError(t, err) + os.Remove(gitSshAuthSecretYaml) + } + + testBase( + t, + configuration, + Injector{ + BeforeApplyConfiguration: beforeApply, + CleanUp: cleanUp, + }, + false, + ) +} + //func TestBasicConfigurationRegression(t *testing.T) { // var retryTimes = 120 // diff --git a/examples/git-credentials/git-push-job.yaml b/examples/git-credentials/git-push-job.yaml new file mode 100644 index 00000000..2963c9b9 --- /dev/null +++ b/examples/git-credentials/git-push-job.yaml @@ -0,0 +1,86 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: simple-terraform-module + namespace: default +data: + main.tf: | + resource "tls_private_key" "private_key" { + algorithm = var.algorithm + } + outputs.tf: | + output "openssh_private_key" { + value = nonsensitive(tls_private_key.private_key.private_key_pem) + } + variables.tf: | + variable "algorithm" { + description = "Encryption algorithm for the private key" + type = string + } +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: git-push + namespace: default + labels: + name: git-push +spec: + template: + spec: + restartPolicy: OnFailure + initContainers: + - name: wait-for-git-server + image: alpine/git + command: + - sh + - -c + - >- + set -x && + until nc -vzw 2 git-server 22; do sleep 10; done + volumeMounts: + - mountPath: /usr/.ssh + name: ssh-keys + containers: + - name: git-push + image: alpine/git + command: + - sh + - -c + - >- + set -x && + eval $(ssh-agent) && + ssh-add /usr/.ssh/id_rsa && + mkdir /root/.ssh && + ssh-keyscan git-server >> /root/.ssh/known_hosts && + mkdir /usr/simple-terraform-module && + cd /usr/simple-terraform-module && + cp /simple-terraform-module/*.tf . && + git config --global init.defaultBranch master && + git init && + git config user.name "john.doe" && + git config user.email "john@doe.com" && + git add . && + git commit -m "initial commit" && + git remote add origin git@git-server:simple-terraform-module.git && + git push --set-upstream origin master + volumeMounts: + - mountPath: /usr/.ssh + name: ssh-keys + - mountPath: /simple-terraform-module + name: simple-terraform-module + resources: + limits: + memory: "128Mi" + cpu: "0.1" + volumes: + - name: ssh-keys + secret: + secretName: ssh-keys + items: + - key: id_rsa + path: id_rsa + defaultMode: 0400 + - name: simple-terraform-module + configMap: + name: simple-terraform-module \ No newline at end of file diff --git a/examples/git-credentials/git-server-pod.yaml b/examples/git-credentials/git-server-pod.yaml new file mode 100644 index 00000000..e09e3ed2 --- /dev/null +++ b/examples/git-credentials/git-server-pod.yaml @@ -0,0 +1,85 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: init-git-server-script + namespace: default +data: + init-git-server.sh: | + #!/bin/sh + set -x + mkdir -p ~/.ssh + chmod 0700 ~/.ssh + touch ~/.ssh/authorized_keys + chmod 0600 ~/.ssh/authorized_keys + mkdir simple-terraform-module.git + git config --global init.defaultBranch master &>/dev/null + git init --bare simple-terraform-module.git +--- +apiVersion: v1 +kind: Pod +metadata: + name: git-server + namespace: default + labels: + name: git-server +spec: + containers: + - name: git-server + image: ubuntu:22.04 + command: + - sh + - -c + - >- + apt update && + apt install git openssh-server -y && + mkdir /var/run/sshd && + useradd -r -m -U -d /home/git -s /bin/bash git && + su - git -c /tmp/scripts/init-git-server.sh && + cat ~/.ssh/authorized_keys >> /home/git/.ssh/authorized_keys && + /usr/sbin/sshd -D + lifecycle: + preStop: + exec: + command: + - sh + - -c + - service ssh stop + volumeMounts: + - mountPath: /root/.ssh + name: ssh-keys + - mountPath: /tmp/scripts + name: init-git-server + ports: + - containerPort: 22 + readinessProbe: + tcpSocket: + port: 22 + initialDelaySeconds: 30 + resources: + limits: + memory: "500Mi" + cpu: "0.2" + volumes: + - name: ssh-keys + secret: + secretName: ssh-keys + items: + - key: id_rsa.pub + path: authorized_keys + - name: init-git-server + configMap: + name: init-git-server-script + defaultMode: 0555 +--- +apiVersion: v1 +kind: Service +metadata: + name: git-server + namespace: default +spec: + ports: + - port: 22 + protocol: TCP + targetPort: 22 + selector: + name: git-server \ No newline at end of file diff --git a/examples/git-credentials/ssh-keys-secret.yaml b/examples/git-credentials/ssh-keys-secret.yaml new file mode 100644 index 00000000..964a9cd6 --- /dev/null +++ b/examples/git-credentials/ssh-keys-secret.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: ssh-keys + namespace: default +type: Opaque +data: + id_rsa: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBbCtDTXIvbXl4eDlrNnJoUTlvbXhEQk1ZemYvMTJwTisvb24zSjJTTTVQUjhndFdOCk1mL2taWmdGQWNHYk55T3o5emI4MFVqTWNKY2lCNXdzNFVBcTJEa2ZzU090dllQbW5Nand2dUJId3p3cmpBcEgKT1ZVN1ozbjQ2VUpya2crUkVoWElVbGRYcXQzellOSllWZXF3MHhTOTBpNFNyR1RLS3VrR1FGQVh1L3Q5YzBERApDNHFGZXk1UXlQYndxMWFhN01uVDlqRHZvdWQxMjhtUnZoSzNSQkxZQjE3SURIQ3o4bURQNllkYVJJLys5MHdTCkh3c0dTdk44K3FtbTdzazFkdkJ2RkhNTllnaDl2S09mbDNuQitOOWlNQ2pOV1BvMFdBZkl1VHVCU0ozMnVHZ3UKdkxSZFJocXVpWlAyenpraEhpSnUzdHlVWDRWWHp2TXMwdSsyY1FJREFRQUJBb0lCQVFDS2ZVZk1iM1NGL2lxWApuZHExOUhocytqejBHeUtrWFRyQUFDNU96WEZzbFVPMFNlYW1ZU0J6UTF2TmJpMks2aE9BcWJOL1kxS0ltRWQvCmlQbWpyRTlsT3pHYTVWM1lJaDUzZVFPT0NoVm1BY2Z6dXF1WHJCQ3ZHcG5PbWJKZFRiU0xPVEdoWStPYyt5YWkKY3l2NXJEZnhRa2lWRDA0WHhSQlVjSWd5dk5Ybm90UHcwbmVEVHhISDN5N3VWL0tLRE9YUjVWdmZ3RzJJelpzRQp0UUNtejRRUHNRYUpLOGdMYk1BTC8xTmcwV0oxUjI0UFhlMzlid3ZqRytnMC9MYmFrcFB4YTJ2Tjc1VmZScEJXCjdaYVd3L2ZGWStwYjdjdENCSGVFQ3E1T1I1L0lOWGJuVEFUMHJKTlM0eEdEeUR6am9hOGtGamR0bGU0bmdKU2YKaG1wbFdxa0JBb0dCQU1Md3pZRDBEMEhxTWswZ3VuWlNsTDBhK3ZzdFF5S1VwOURvTHNSRnFNeEgrakt3c1J4eApWaE1nVzQrTEpSenllV0lPOWJWajkrZUY1SUxsQXo3WithQVNYdjVVdHlVSzJyM1I4N3FCcjFTMDhjS3hlYi9hCjVualBHbkl6aEVTOHNXRExmS2N6MkhYMnF5MWlsL1NNdloyQ2JZMllLYVk4ekNrU1JDQ2FIS2ZGQW9HQkFNZHkKdWhmN29DK0Z6dUtoanUzQ3l0aU1EZGhybDVhWVBmdWxWTHNlamxYR1ZxWU1nZDRXNzg5QTdCbUJZMERzdmh6RwprRnZ0bWhzUEw3RGhEdUZ2bGtvdnVkS25XbTJvUjZqbDFGNkZWSGxzN3lIS3FTQ2R2bjdXL0ZhZSsxL1loOFFSCk8vR2lBY1c2UTJuMHhVOTBVVmwvMnlVZGhHakVKSGN2S3hCcnpCSzlBb0dCQUpqWjdaanVSVVJlMlFBbTZHMjgKaE1uZWJPc2o2MThqQm83RWIxOFFhN0Y1d3BHYWY5VVlmUEJVVDlhMnVPd0FwL0tlWGtUVFZOK2gyYkpVMVgyagp0cHF2clBKcEJJMnovQjRZa0s0dDM0ZGd0YXYrTXNPZlpWVld0cHJURUNSQmZDZTBobElvVWRMMURmVnhPRXJWClRCeEQxNWpOdGVLV0MxTXM4bVJKMHF3dEFvR0FNbFUrcDJ6RitSaEFwS3IyNGdQRm95NTlFLy9iQ3BNekdUMloKQzN2am1idnJCQTZsKzRFNFZjcGhpdkkvTlJSSnlnTkdUUnpDUmsvbnpqQ055OUNZVWZLSFo2VDZTakFzblhBYQp6eHZBdk1BRC9UZ2l4R3RxdHFIVW5wdVNmcGFyZEl5UTN5THVaWkxqRG10S0hBb1R1WTF0cFlrMGNDZ0h0OWc4CmV2RnBWOVVDZ1lBejlmMk9ISzNVR254dDZOOXM0ZVpUak9TVDRSWUVzR0JOZ1pSV0JVc0pHMkRJdkE2Sks3T3QKc3hwTWwzYUpLVWFCWmswS3FReVlrNTkrRTRkeis4UFZjVmN1bGoxQUtYRVR1a3BQaUU3ZFhlTXpjaUxSNUt4YwpvVmFoWVFXSlg0N016aFVTMWxJSVFJcmlsN0JTNmFnbTlnV2NoVkpIVGNHYVY1ZFZBdmI2VXc9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo= + + id_rsa.pub: c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCQVFDWDRJeXYrYkxISDJUcXVGRDJpYkVNRXhqTi8vWGFrMzcraWZjblpJems5SHlDMVkweC8rUmxtQVVCd1pzM0k3UDNOdnpSU014d2x5SUhuQ3poUUNyWU9SK3hJNjI5ZythY3lQQys0RWZEUEN1TUNrYzVWVHRuZWZqcFFtdVNENUVTRmNoU1YxZXEzZk5nMGxoVjZyRFRGTDNTTGhLc1pNb3E2UVpBVUJlNyszMXpRTU1MaW9WN0xsREk5dkNyVnByc3lkUDJNTytpNTNYYnlaRytFcmRFRXRnSFhzZ01jTFB5WU0vcGgxcEVqLzczVEJJZkN3Wks4M3o2cWFidXlUVjI4RzhVY3cxaUNIMjhvNStYZWNINDMySXdLTTFZK2pSWUI4aTVPNEZJbmZhNGFDNjh0RjFHR3E2SmsvYlBPU0VlSW03ZTNKUmZoVmZPOHl6Uzc3WngK diff --git a/examples/git-credentials/templates/git-ssh-auth-secret.tmpl b/examples/git-credentials/templates/git-ssh-auth-secret.tmpl new file mode 100644 index 00000000..2a0d9537 --- /dev/null +++ b/examples/git-credentials/templates/git-ssh-auth-secret.tmpl @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: git-ssh-auth + namespace: default +type: kubernetes.io/ssh-auth +data: + ssh-privatekey: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBbCtDTXIvbXl4eDlrNnJoUTlvbXhEQk1ZemYvMTJwTisvb24zSjJTTTVQUjhndFdOCk1mL2taWmdGQWNHYk55T3o5emI4MFVqTWNKY2lCNXdzNFVBcTJEa2ZzU090dllQbW5Nand2dUJId3p3cmpBcEgKT1ZVN1ozbjQ2VUpya2crUkVoWElVbGRYcXQzellOSllWZXF3MHhTOTBpNFNyR1RLS3VrR1FGQVh1L3Q5YzBERApDNHFGZXk1UXlQYndxMWFhN01uVDlqRHZvdWQxMjhtUnZoSzNSQkxZQjE3SURIQ3o4bURQNllkYVJJLys5MHdTCkh3c0dTdk44K3FtbTdzazFkdkJ2RkhNTllnaDl2S09mbDNuQitOOWlNQ2pOV1BvMFdBZkl1VHVCU0ozMnVHZ3UKdkxSZFJocXVpWlAyenpraEhpSnUzdHlVWDRWWHp2TXMwdSsyY1FJREFRQUJBb0lCQVFDS2ZVZk1iM1NGL2lxWApuZHExOUhocytqejBHeUtrWFRyQUFDNU96WEZzbFVPMFNlYW1ZU0J6UTF2TmJpMks2aE9BcWJOL1kxS0ltRWQvCmlQbWpyRTlsT3pHYTVWM1lJaDUzZVFPT0NoVm1BY2Z6dXF1WHJCQ3ZHcG5PbWJKZFRiU0xPVEdoWStPYyt5YWkKY3l2NXJEZnhRa2lWRDA0WHhSQlVjSWd5dk5Ybm90UHcwbmVEVHhISDN5N3VWL0tLRE9YUjVWdmZ3RzJJelpzRQp0UUNtejRRUHNRYUpLOGdMYk1BTC8xTmcwV0oxUjI0UFhlMzlid3ZqRytnMC9MYmFrcFB4YTJ2Tjc1VmZScEJXCjdaYVd3L2ZGWStwYjdjdENCSGVFQ3E1T1I1L0lOWGJuVEFUMHJKTlM0eEdEeUR6am9hOGtGamR0bGU0bmdKU2YKaG1wbFdxa0JBb0dCQU1Md3pZRDBEMEhxTWswZ3VuWlNsTDBhK3ZzdFF5S1VwOURvTHNSRnFNeEgrakt3c1J4eApWaE1nVzQrTEpSenllV0lPOWJWajkrZUY1SUxsQXo3WithQVNYdjVVdHlVSzJyM1I4N3FCcjFTMDhjS3hlYi9hCjVualBHbkl6aEVTOHNXRExmS2N6MkhYMnF5MWlsL1NNdloyQ2JZMllLYVk4ekNrU1JDQ2FIS2ZGQW9HQkFNZHkKdWhmN29DK0Z6dUtoanUzQ3l0aU1EZGhybDVhWVBmdWxWTHNlamxYR1ZxWU1nZDRXNzg5QTdCbUJZMERzdmh6RwprRnZ0bWhzUEw3RGhEdUZ2bGtvdnVkS25XbTJvUjZqbDFGNkZWSGxzN3lIS3FTQ2R2bjdXL0ZhZSsxL1loOFFSCk8vR2lBY1c2UTJuMHhVOTBVVmwvMnlVZGhHakVKSGN2S3hCcnpCSzlBb0dCQUpqWjdaanVSVVJlMlFBbTZHMjgKaE1uZWJPc2o2MThqQm83RWIxOFFhN0Y1d3BHYWY5VVlmUEJVVDlhMnVPd0FwL0tlWGtUVFZOK2gyYkpVMVgyagp0cHF2clBKcEJJMnovQjRZa0s0dDM0ZGd0YXYrTXNPZlpWVld0cHJURUNSQmZDZTBobElvVWRMMURmVnhPRXJWClRCeEQxNWpOdGVLV0MxTXM4bVJKMHF3dEFvR0FNbFUrcDJ6RitSaEFwS3IyNGdQRm95NTlFLy9iQ3BNekdUMloKQzN2am1idnJCQTZsKzRFNFZjcGhpdkkvTlJSSnlnTkdUUnpDUmsvbnpqQ055OUNZVWZLSFo2VDZTakFzblhBYQp6eHZBdk1BRC9UZ2l4R3RxdHFIVW5wdVNmcGFyZEl5UTN5THVaWkxqRG10S0hBb1R1WTF0cFlrMGNDZ0h0OWc4CmV2RnBWOVVDZ1lBejlmMk9ISzNVR254dDZOOXM0ZVpUak9TVDRSWUVzR0JOZ1pSV0JVc0pHMkRJdkE2Sks3T3QKc3hwTWwzYUpLVWFCWmswS3FReVlrNTkrRTRkeis4UFZjVmN1bGoxQUtYRVR1a3BQaUU3ZFhlTXpjaUxSNUt4YwpvVmFoWVFXSlg0N016aFVTMWxJSVFJcmlsN0JTNmFnbTlnV2NoVkpIVGNHYVY1ZFZBdmI2VXc9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo= + + known_hosts: {{ . }} diff --git a/examples/random/configuration_git_ssh.yaml b/examples/random/configuration_git_ssh.yaml new file mode 100644 index 00000000..1b9359d3 --- /dev/null +++ b/examples/random/configuration_git_ssh.yaml @@ -0,0 +1,15 @@ +apiVersion: terraform.core.oam.dev/v1beta2 +kind: Configuration +metadata: + name: random-e2e-git-creds-secret-ref +spec: + inlineCredentials: true + remote: git@git-server:simple-terraform-module.git + variable: + algorithm: RSA + writeConnectionSecretToRef: + name: random-e2e-git-creds-secret-ref-conn + gitCredentialsSecretReference: + name: git-ssh-auth + namespace: default + diff --git a/go.mod b/go.mod index f000bcbb..b3805299 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/aws/aws-sdk-go v1.44.23 github.com/ghodss/yaml v1.0.0 github.com/go-logr/logr v0.4.0 - github.com/google/go-cmp v0.5.7 + github.com/google/go-cmp v0.5.8 github.com/hashicorp/hcl/v2 v2.12.0 github.com/jinzhu/copier v0.3.5 github.com/onsi/ginkgo v1.16.4 @@ -26,6 +26,11 @@ require ( sigs.k8s.io/yaml v1.2.0 ) +require ( + github.com/kylelemons/godebug v1.1.0 // indirect + golang.org/x/tools v0.2.0 // indirect +) + require ( github.com/agext/levenshtein v1.2.3 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect @@ -39,14 +44,13 @@ require ( github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/gofuzz v1.1.0 // indirect - github.com/google/uuid v1.1.2 // indirect + github.com/google/uuid v1.2.0 // indirect github.com/googleapis/gnostic v0.5.5 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.11 // indirect github.com/kr/pretty v0.2.1 // indirect - github.com/kylelemons/godebug v1.1.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -59,11 +63,11 @@ require ( github.com/prometheus/procfs v0.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/zclconf/go-cty v1.10.0 // indirect - golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect + golang.org/x/net v0.2.0 // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect - golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sys v0.2.0 // indirect + golang.org/x/term v0.2.0 // indirect + golang.org/x/text v0.4.0 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index 3b0fa0da..6c1ec66d 100644 --- a/go.sum +++ b/go.sum @@ -188,8 +188,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/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.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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= @@ -203,8 +203,9 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= @@ -437,6 +438,7 @@ github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgq github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= github.com/zclconf/go-cty v1.10.0 h1:mp9ZXQeIcN8kAwuqorjH+Q+njbJKjLrvB2yIh4q7U+0= @@ -473,6 +475,8 @@ 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-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= 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= @@ -505,6 +509,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -536,10 +542,14 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 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= @@ -554,6 +564,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -602,13 +613,18 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/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-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/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-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 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= @@ -617,8 +633,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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= @@ -666,12 +683,13 @@ golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= 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= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY=