Repository: chef/automate
Pull request #7993 (2023-06-27 07:11:25)

Fixing config path is empty for chef-automate verify cmd
### :nut_and_bolt: Description: What code changed, and why?
Previously chef-automate verify command is expecting to have config file path after deployment
This Pr will make config path as optional after deployment

<!-- /!\ Please ensure that you are NOT disclosing any customer information without their consent /!\ -->

### :chains: Related Resources

### :+1: Definition of Done
Now made config path as optional for chef-automate verify command after deployment

### :athletic_shoe: How to Build and Test the Change

### :white_check_mark: Checklist

**All PRs** must tick these:

- [ ] I have read the [CONTRIBUTING document](https://github.com/chef/automate/blob/master/CONTRIBUTING.md).
- [ ] All commits signed-off for the [Developer Certification of Origin](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#developer-certification-of-origin-dco).

With occasional exceptions, all PRs **from Progress employees** must tick these:

- [ ] Is the code clear? *(complicated code or lots of comments--subdivide and use well-named methods, meaningful variable names, etc.)*
- [ ] Consistency checked? *(user notifications, user prompts, visual patterns, code patterns, variable names)*
- [ ] Repeated code blocks eliminated? *(adapt and reuse existing components, blocks, functions, etc.)*
- [ ] Spelling, grammar, typos checked? *(at a minimum use `make spell` in any component directory)*
- [ ] Code well-formatted? *(indents, line breaks, etc. improve rather than hinder readability)*

All PRs **from Progress employees** should tick these if appropriate:

- [ ] Tests added/updated? (all new code needs new tests)
- [ ] Docs added/updated? (all customer-facing changes)

*Please add a note next to any checkbox above if you are NOT ticking it.*

### :camera: Screenshots, if applicable
Before this change for post deployment:
![image](https://github.com/chef/automate/assets/101619541/e3939124-57b3-4530-a256-136d1b6b2d86)


Config file path empty issue resolved:
![image](https://github.com/chef/automate/assets/101619541/b8866889-4d8c-4710-9a62-65b77a2cbdf6)


netlify[bot]: ### <span aria-hidden="true">👷</span> Deploy Preview for *chef-automate* processing.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | e0dba1b7384045e07da30deb96aeaba307d5ee46 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/chef-automate/deploys/649a8b9febd53e000826bb5c |
sonarqube-for-infrastructure-prod[bot]: **SonarQube Quality Gate**

[![Quality Gate failed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7993)

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7993) [![6.7%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '6.7%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7993) 6.7% Coverage on New Code (is less than 80%)  

[See analysis details on SonarQube](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7993)


sonarqube-for-infrastructure-prod[bot]: **SonarQube Quality Gate**

[![Quality Gate failed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7993)

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7993) [![7.7%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '7.7%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7993) 7.7% Coverage on New Code (is less than 80%)  

[See analysis details on SonarQube](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7993)



Patch:
diff --git a/VERSION b/VERSION
index a273f6724..6c3c5fd48 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-4.7.106
\ No newline at end of file
+4.7.107
\ No newline at end of file
diff --git a/components/automate-cli/cmd/chef-automate/verify.go b/components/automate-cli/cmd/chef-automate/verify.go
index 2ec306887..5b8524e68 100644
--- a/components/automate-cli/cmd/chef-automate/verify.go
+++ b/components/automate-cli/cmd/chef-automate/verify.go
@@ -67,9 +67,15 @@ type verifyCmdFlow struct {
 	sshPort              string
 	sshKeyFile           string
 	sshUserName          string
+	deps                 *verifyCmdDeps
 }
 
-func NewVerifyCmdFlow(client httputils.HTTPClient, createSystemdService verifysystemdcreate.CreateSystemdService, systemdCreateUtils verifysystemdcreate.SystemdCreateUtils, config *config.HaDeployConfig, sshutil sshutils.SSHUtil, writer *cli.Writer) *verifyCmdFlow {
+type verifyCmdDeps struct {
+	getAutomateHAInfraDetails func() (*AutomateHAInfraDetails, error)
+	PopulateHaCommonConfig    func(configPuller PullConfigs) (haDeployConfig *config.HaDeployConfig, err error)
+}
+
+func NewVerifyCmdFlow(client httputils.HTTPClient, createSystemdService verifysystemdcreate.CreateSystemdService, systemdCreateUtils verifysystemdcreate.SystemdCreateUtils, config *config.HaDeployConfig, sshutil sshutils.SSHUtil, writer *cli.Writer, deps *verifyCmdDeps) *verifyCmdFlow {
 	return &verifyCmdFlow{
 		Client:               client,
 		CreateSystemdService: createSystemdService,
@@ -77,6 +83,7 @@ func NewVerifyCmdFlow(client httputils.HTTPClient, createSystemdService verifysy
 		Config:               config,
 		SSHUtil:              sshutil,
 		Writer:               writer,
+		deps:                 deps,
 	}
 }
 
@@ -235,8 +242,11 @@ func verifyCmdFunc(flagsObj *verifyCmdFlags) func(cmd *cobra.Command, args []str
 		if err != nil {
 			return err
 		}
-
-		c := NewVerifyCmdFlow(httputils.NewClient(log), createSystemdServiceWithBinary, verifysystemdcreate.NewSystemdCreateUtilsImpl(), config.NewHaDeployConfig(), sshutils.NewSSHUtilWithCommandExecutor(sshutils.NewSshClient(), log, command.NewExecExecutor()), writer)
+		deps := &verifyCmdDeps{
+			getAutomateHAInfraDetails: getAutomateHAInfraDetails,
+			PopulateHaCommonConfig:    PopulateHaCommonConfig,
+		}
+		c := NewVerifyCmdFlow(httputils.NewClient(log), createSystemdServiceWithBinary, verifysystemdcreate.NewSystemdCreateUtilsImpl(), config.NewHaDeployConfig(), sshutils.NewSSHUtilWithCommandExecutor(sshutils.NewSshClient(), log, command.NewExecExecutor()), writer, deps)
 		return c.runVerifyCmd(cmd, args, flagsObj)
 	}
 }
@@ -251,11 +261,32 @@ func (v *verifyCmdFlow) RunVerify(config string) error {
 	// TODO : config flag is optional for now. Need to handle the default config path
 	if len(strings.TrimSpace(config)) > 0 {
 		configPath = config
-	}
+		err := v.Config.ParseAndVerify(configPath)
+		if err != nil {
+			return err
+		}
+	} else {
+		infra, err := v.deps.getAutomateHAInfraDetails()
+		if err != nil {
+			return err
+		}
+		sshConfig := &SSHConfig{
+			sshUser:    infra.Outputs.SSHUser.Value,
+			sshKeyFile: infra.Outputs.SSHKeyFile.Value,
+			sshPort:    infra.Outputs.SSHPort.Value,
+		}
+		sshUtil := NewSSHUtil(sshConfig)
+		configPuller := NewPullConfigs(infra, sshUtil)
 
-	err := v.Config.ParseAndVerify(configPath)
-	if err != nil {
-		return err
+		config, err := v.deps.PopulateHaCommonConfig(configPuller)
+		if err != nil {
+			return err
+		}
+		v.Config = config
+		err = v.Config.Verify()
+		if err != nil {
+			return err
+		}
 	}
 
 	// Get config required for batch-check API call
@@ -268,7 +299,7 @@ func (v *verifyCmdFlow) RunVerify(config string) error {
 		},
 	}
 
-	err = batchCheckConfig.PopulateWith(v.Config)
+	err := batchCheckConfig.PopulateWith(v.Config)
 	if err != nil {
 		return err
 	}
diff --git a/lib/config/hadeployconfigverify.go b/lib/config/hadeployconfigverify.go
index 5e54ec9be..c6ad1ed19 100644
--- a/lib/config/hadeployconfigverify.go
+++ b/lib/config/hadeployconfigverify.go
@@ -99,7 +99,7 @@ func (c *HaDeployConfig) verifyConfigInitials(configInitials *ConfigInitials) er
 	if err := validateRequiredString(configInitials.SecretsStoreFile, "secrets_store_file"); err != nil {
 		errorList.PushBack(err)
 	}
-	if err := validateRequiredString(configInitials.Architecture, "architecture", "aws", "existing_nodes"); err != nil {
+	if err := validateRequiredString(configInitials.Architecture, "architecture", "aws", "existing_nodes", "deployment"); err != nil {
 		errorList.PushBack(err)
 	}
 	if err := validateRequiredString(configInitials.WorkspacePath, "workspace_path", "/hab/a2_deploy_workspace"); err != nil {


Test patch:
diff --git a/components/automate-cli/cmd/chef-automate/verify_test.go b/components/automate-cli/cmd/chef-automate/verify_test.go
index 5111625bf..fa04f8211 100644
--- a/components/automate-cli/cmd/chef-automate/verify_test.go
+++ b/components/automate-cli/cmd/chef-automate/verify_test.go
@@ -28,6 +28,7 @@ func TestRunVerifyCmd(t *testing.T) {
 		mockCreateSystemdService *verifysystemdcreate.MockCreateSystemdService
 		mockSystemdCreateUtils   *verifysystemdcreate.MockSystemdCreateUtils
 		mockSSHUtil              *sshutils.MockSSHUtilsImpl
+		mockVerifyCmdDeps        *verifyCmdDeps
 		configFile               string
 		wantErr                  error
 	}{
@@ -77,6 +78,14 @@ func TestRunVerifyCmd(t *testing.T) {
 					}
 				},
 			},
+			mockVerifyCmdDeps: &verifyCmdDeps{
+				getAutomateHAInfraDetails: func() (*AutomateHAInfraDetails, error) {
+					return &AutomateHAInfraDetails{}, nil
+				},
+				PopulateHaCommonConfig: func(configPuller PullConfigs) (haDeployConfig *config.HaDeployConfig, err error) {
+					return &config.HaDeployConfig{}, nil
+				},
+			},
 			configFile: CONFIG_TOML_PATH + CONFIG_FILE,
 			wantErr:    nil,
 		},
@@ -123,9 +132,131 @@ func TestRunVerifyCmd(t *testing.T) {
 					}
 				},
 			},
+			mockVerifyCmdDeps: &verifyCmdDeps{
+				getAutomateHAInfraDetails: func() (*AutomateHAInfraDetails, error) {
+					return &AutomateHAInfraDetails{}, nil
+				},
+				PopulateHaCommonConfig: func(configPuller PullConfigs) (haDeployConfig *config.HaDeployConfig, err error) {
+					return &config.HaDeployConfig{}, nil
+				},
+			},
 			configFile: CONFIG_TOML_PATH + CONFIG_FILE,
 			wantErr:    nil,
 		},
+		{
+			description: "Failed to get automate HA infra details",
+			mockHttputils: &httputils.MockHTTPClient{
+				MakeRequestFunc: func(requestMethod, url string, body interface{}) (*http.Response, []byte, error) {
+					if strings.Contains(url, "batch-check") {
+						return &http.Response{
+							StatusCode: http.StatusOK,
+							Body:       nil,
+						}, []byte(BATCH_CHECK_REQUEST), nil
+					}
+					return &http.Response{
+						StatusCode: http.StatusOK,
+						Body:       nil,
+					}, []byte(STATUS_API_RESPONSE), nil
+				},
+			},
+			mockCreateSystemdService: &verifysystemdcreate.MockCreateSystemdService{
+				CreateFun: func() error {
+					return nil
+				},
+			},
+			mockSystemdCreateUtils: &verifysystemdcreate.MockSystemdCreateUtils{
+				GetBinaryPathFunc: func() (string, error) {
+					return "", nil
+				},
+			},
+			mockSSHUtil: &sshutils.MockSSHUtilsImpl{
+				ExecuteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, cmd string, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  nil,
+							Output: "",
+						},
+					}
+				},
+				CopyFileToRemoteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, srcFilePath string, destFileName string, removeFile bool, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  nil,
+							Output: "",
+						},
+					}
+				},
+			},
+			mockVerifyCmdDeps: &verifyCmdDeps{
+				getAutomateHAInfraDetails: func() (*AutomateHAInfraDetails, error) {
+					return nil, errors.New("Unable to get automate HA infra details")
+				},
+				PopulateHaCommonConfig: func(configPuller PullConfigs) (haDeployConfig *config.HaDeployConfig, err error) {
+					return &config.HaDeployConfig{}, nil
+				},
+			},
+			configFile: "",
+			wantErr:    errors.New("Unable to get automate HA infra details"),
+		},
+		{
+			description: "Failed to populate HA common config",
+			mockHttputils: &httputils.MockHTTPClient{
+				MakeRequestFunc: func(requestMethod, url string, body interface{}) (*http.Response, []byte, error) {
+					if strings.Contains(url, "batch-check") {
+						return &http.Response{
+							StatusCode: http.StatusOK,
+							Body:       nil,
+						}, []byte(BATCH_CHECK_REQUEST), nil
+					}
+					return &http.Response{
+						StatusCode: http.StatusOK,
+						Body:       nil,
+					}, []byte(STATUS_API_RESPONSE), nil
+				},
+			},
+			mockCreateSystemdService: &verifysystemdcreate.MockCreateSystemdService{
+				CreateFun: func() error {
+					return nil
+				},
+			},
+			mockSystemdCreateUtils: &verifysystemdcreate.MockSystemdCreateUtils{
+				GetBinaryPathFunc: func() (string, error) {
+					return "", nil
+				},
+			},
+			mockSSHUtil: &sshutils.MockSSHUtilsImpl{
+				ExecuteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, cmd string, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  nil,
+							Output: "",
+						},
+					}
+				},
+				CopyFileToRemoteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, srcFilePath string, destFileName string, removeFile bool, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  nil,
+							Output: "",
+						},
+					}
+				},
+			},
+			mockVerifyCmdDeps: &verifyCmdDeps{
+				getAutomateHAInfraDetails: func() (*AutomateHAInfraDetails, error) {
+					return &AutomateHAInfraDetails{}, nil
+				},
+				PopulateHaCommonConfig: func(configPuller PullConfigs) (haDeployConfig *config.HaDeployConfig, err error) {
+					return nil, errors.New("Failed to populate HA common config")
+				},
+			},
+			configFile: "",
+			wantErr:    errors.New("Failed to populate HA common config"),
+		},
 	}
 
 	for _, tt := range tests {
@@ -137,7 +268,9 @@ func TestRunVerifyCmd(t *testing.T) {
 				tt.mockSystemdCreateUtils,
 				config.NewHaDeployConfig(),
 				tt.mockSSHUtil,
-				cw.CliWriter)
+				cw.CliWriter,
+				tt.mockVerifyCmdDeps,
+			)
 
 			flagsObj := &verifyCmdFlags{
 				config: tt.configFile,
@@ -316,10 +449,20 @@ func newMockVerifyCmdFlow(mockHttputils *httputils.MockHTTPClient, cw *majorupgr
 			}
 		},
 	}
+	mockVerifyCmdDeps := &verifyCmdDeps{
+		getAutomateHAInfraDetails: func() (*AutomateHAInfraDetails, error) {
+			return &AutomateHAInfraDetails{}, nil
+		},
+		PopulateHaCommonConfig: func(configPuller PullConfigs) (haDeployConfig *config.HaDeployConfig, err error) {
+			return &config.HaDeployConfig{}, nil
+		},
+	}
 	return NewVerifyCmdFlow(mockHttputils,
 		mockCreateSystemdService,
 		mockSystemdCreateUtils,
 		config.NewHaDeployConfig(),
 		mockSSHUtil,
-		cw.CliWriter)
+		cw.CliWriter,
+		mockVerifyCmdDeps,
+	)
 }
diff --git a/lib/config/hadeployconfigverify_test.go b/lib/config/hadeployconfigverify_test.go
index 10e22749f..8486f4bac 100644
--- a/lib/config/hadeployconfigverify_test.go
+++ b/lib/config/hadeployconfigverify_test.go
@@ -57,7 +57,7 @@ func TestParseAndVerify(t *testing.T) {
 			name:    "Verify Failed Ha Aws Managed Config",
 			args:    args{configFile: "./testdata/HaAwsManagedFailed.toml"},
 			wantErr: true,
-			err:     errors.New("invalid or empty: secrets_key_file\ninvalid or empty: secrets_store_file\ninvalid value 'awss' for field 'architecture'. Expected values are: aws, existing_nodes\ninvalid value '/hab/a3_deploy_workspace' for field 'workspace_path'. Expected values are: /hab/a2_deploy_workspace\ninvalid or empty: ssh_user\ninvalid or empty: ssh_key_file\ninvalid or empty s3_bucketName\ninvalid or empty: aws profile name\ninvalid or empty: aws region\ninvalid or empty: aws private_custom_subnets\ninvalid or empty: aws public_custom_subnets\ninvalid or empty: aws ssh_key_pair_name\ninvalid value 'aws lb_access_logs' for field 'fals'\ninvalid or empty: aws ami_id\ninvalid or empty: aws automate_server_instance_type\ninvalid or empty: aws automate_lb_certificate_arn\nempty value: aws automate_ebs_volume_iops\nempty value: aws automate_ebs_volume_size\ninvalid or empty: aws automate_ebs_volume_type\nempty value: aws chef_ebs_volume_iops\nempty value: aws chef_ebs_volume_size\ninvalid or empty: aws chef_ebs_volume_type\ninvalid or empty: aws chef_server_instance_type\ninvalid or empty: aws chef_server_lb_certificate_arn\ninvalid or empty: aws opensearch_server_instance_type\ninvalid or empty: aws postgresql_server_instance_type\ninvalid or empty: aws managed_opensearch_domain_name\ninvalid or empty URL: aws managed_opensearch_domain_url\ninvalid or empty: aws managed_opensearch_user_password\ninvalid or empty: aws managed_opensearch_username\ninvalid or empty: aws managed_rds_dbuser_password\ninvalid or empty: aws managed_rds_dbuser_username\ninvalid or empty URL: aws managed_rds_instance_url\ninvalid or empty: aws managed_rds_superuser_password\ninvalid or empty: aws managed_rds_superuser_username\ninvalid or empty URL: automate fqdn\nempty value: automate instance_count\nempty value: chef server instance_count\nempty value: opensearch instance_count\nempty value: postgresql instance_count"),
+			err:     errors.New("invalid or empty: secrets_key_file\ninvalid or empty: secrets_store_file\ninvalid value 'awss' for field 'architecture'. Expected values are: aws, existing_nodes, deployment\ninvalid value '/hab/a3_deploy_workspace' for field 'workspace_path'. Expected values are: /hab/a2_deploy_workspace\ninvalid or empty: ssh_user\ninvalid or empty: ssh_key_file\ninvalid or empty s3_bucketName\ninvalid or empty: aws profile name\ninvalid or empty: aws region\ninvalid or empty: aws private_custom_subnets\ninvalid or empty: aws public_custom_subnets\ninvalid or empty: aws ssh_key_pair_name\ninvalid value 'aws lb_access_logs' for field 'fals'\ninvalid or empty: aws ami_id\ninvalid or empty: aws automate_server_instance_type\ninvalid or empty: aws automate_lb_certificate_arn\nempty value: aws automate_ebs_volume_iops\nempty value: aws automate_ebs_volume_size\ninvalid or empty: aws automate_ebs_volume_type\nempty value: aws chef_ebs_volume_iops\nempty value: aws chef_ebs_volume_size\ninvalid or empty: aws chef_ebs_volume_type\ninvalid or empty: aws chef_server_instance_type\ninvalid or empty: aws chef_server_lb_certificate_arn\ninvalid or empty: aws opensearch_server_instance_type\ninvalid or empty: aws postgresql_server_instance_type\ninvalid or empty: aws managed_opensearch_domain_name\ninvalid or empty URL: aws managed_opensearch_domain_url\ninvalid or empty: aws managed_opensearch_user_password\ninvalid or empty: aws managed_opensearch_username\ninvalid or empty: aws managed_rds_dbuser_password\ninvalid or empty: aws managed_rds_dbuser_username\ninvalid or empty URL: aws managed_rds_instance_url\ninvalid or empty: aws managed_rds_superuser_password\ninvalid or empty: aws managed_rds_superuser_username\ninvalid or empty URL: automate fqdn\nempty value: automate instance_count\nempty value: chef server instance_count\nempty value: opensearch instance_count\nempty value: postgresql instance_count"),
 		},
 		{
 			name:    "Verify Failed Ha On Prem Aws Managed Config",