github.com/jenkins-x/jx/v2@v2.1.155/pkg/config/install_requirements_test.go (about)

     1  // +build unit
     2  
     3  package config_test
     4  
     5  import (
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os"
     9  	"path"
    10  	"path/filepath"
    11  	"testing"
    12  
    13  	"github.com/jenkins-x/jx/v2/pkg/cloud/gke"
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"github.com/ghodss/yaml"
    17  	"github.com/jenkins-x/jx-logging/pkg/log"
    18  	"github.com/jenkins-x/jx/v2/pkg/cloud"
    19  	"github.com/jenkins-x/jx/v2/pkg/config"
    20  	"github.com/jenkins-x/jx/v2/pkg/gits"
    21  	"github.com/jenkins-x/jx/v2/pkg/util"
    22  	"github.com/stretchr/testify/assert"
    23  )
    24  
    25  var (
    26  	testDataDir = path.Join("test_data")
    27  )
    28  
    29  func TestRequirementsConfigMarshalExistingFile(t *testing.T) {
    30  	t.Parallel()
    31  
    32  	dir, err := ioutil.TempDir("", "test-requirements-config-")
    33  	assert.NoError(t, err, "should create a temporary config dir")
    34  
    35  	expectedClusterName := "my-cluster"
    36  	expectedSecretStorage := config.SecretStorageTypeVault
    37  	expectedDomain := "cheese.co.uk"
    38  
    39  	file := filepath.Join(dir, config.RequirementsConfigFileName)
    40  	requirements := config.NewRequirementsConfig()
    41  	requirements.SecretStorage = expectedSecretStorage
    42  	requirements.Cluster.ClusterName = expectedClusterName
    43  	requirements.Ingress.Domain = expectedDomain
    44  	requirements.Kaniko = true
    45  
    46  	err = requirements.SaveConfig(file)
    47  	assert.NoError(t, err, "failed to save file %s", file)
    48  
    49  	requirements, fileName, err := config.LoadRequirementsConfig(dir, config.DefaultFailOnValidationError)
    50  	assert.NoError(t, err, "failed to load requirements file in dir %s", dir)
    51  	assert.FileExists(t, fileName)
    52  
    53  	assert.Equal(t, true, requirements.Kaniko, "requirements.Kaniko")
    54  	assert.Equal(t, expectedClusterName, requirements.Cluster.ClusterName, "requirements.ClusterName")
    55  	assert.Equal(t, expectedSecretStorage, requirements.SecretStorage, "requirements.SecretStorage")
    56  	assert.Equal(t, expectedDomain, requirements.Ingress.Domain, "requirements.Domain")
    57  
    58  	// lets check we can load the file from a sub dir
    59  	subDir := filepath.Join(dir, "subdir")
    60  	requirements, fileName, err = config.LoadRequirementsConfig(subDir, config.DefaultFailOnValidationError)
    61  	assert.NoError(t, err, "failed to load requirements file in subDir: %s", subDir)
    62  	assert.FileExists(t, fileName)
    63  
    64  	assert.Equal(t, true, requirements.Kaniko, "requirements.Kaniko")
    65  	assert.Equal(t, expectedClusterName, requirements.Cluster.ClusterName, "requirements.ClusterName")
    66  	assert.Equal(t, expectedSecretStorage, requirements.SecretStorage, "requirements.SecretStorage")
    67  	assert.Equal(t, expectedDomain, requirements.Ingress.Domain, "requirements.Domain")
    68  }
    69  
    70  func Test_OverrideRequirementsFromEnvironment_does_not_initialise_nil_structs(t *testing.T) {
    71  	requirements, fileName, err := config.LoadRequirementsConfig(testDataDir, config.DefaultFailOnValidationError)
    72  	assert.NoError(t, err, "failed to load requirements file in dir %s", testDataDir)
    73  	assert.FileExists(t, fileName)
    74  
    75  	requirements.OverrideRequirementsFromEnvironment(func() gke.GClouder {
    76  		return nil
    77  	})
    78  
    79  	tempDir, err := ioutil.TempDir("", "test-requirements-config")
    80  	assert.NoError(t, err, "should create a temporary config dir")
    81  	defer func() {
    82  		_ = os.RemoveAll(tempDir)
    83  	}()
    84  
    85  	err = requirements.SaveConfig(filepath.Join(tempDir, config.RequirementsConfigFileName))
    86  	assert.NoError(t, err, "failed to save requirements file in dir %s", tempDir)
    87  
    88  	overrideRequirements, fileName, err := config.LoadRequirementsConfig(tempDir, config.DefaultFailOnValidationError)
    89  	assert.NoError(t, err, "failed to load requirements file in dir %s", testDataDir)
    90  	assert.FileExists(t, fileName)
    91  
    92  	assert.Nil(t, overrideRequirements.BuildPacks, "nil values should not be populated")
    93  }
    94  
    95  func Test_OverrideRequirementsFromEnvironment_populate_requirements_from_environment_variables(t *testing.T) {
    96  	var overrideTests = []struct {
    97  		envKey               string
    98  		envValue             string
    99  		expectedRequirements config.RequirementsConfig
   100  	}{
   101  		// RequirementsConfig
   102  		{config.RequirementSecretStorageType, "vault", config.RequirementsConfig{SecretStorage: "vault"}},
   103  		{config.RequirementKaniko, "true", config.RequirementsConfig{Kaniko: true}},
   104  		{config.RequirementKaniko, "false", config.RequirementsConfig{Kaniko: false}},
   105  		{config.RequirementKaniko, "", config.RequirementsConfig{Kaniko: false}},
   106  		{config.RequirementRepository, "bucketrepo", config.RequirementsConfig{Repository: "bucketrepo"}},
   107  		{config.RequirementWebhook, "prow", config.RequirementsConfig{Webhook: "prow"}},
   108  		{config.RequirementGitAppEnabled, "true", config.RequirementsConfig{GithubApp: &config.GithubAppConfig{Enabled: true}}},
   109  		{config.RequirementGitAppEnabled, "false", config.RequirementsConfig{GithubApp: &config.GithubAppConfig{Enabled: false}}},
   110  		{config.RequirementGitAppURL, "https://my-github-app", config.RequirementsConfig{GithubApp: &config.GithubAppConfig{URL: "https://my-github-app"}}},
   111  
   112  		// ClusterConfig
   113  		{config.RequirementClusterName, "my-cluster", config.RequirementsConfig{Cluster: config.ClusterConfig{ClusterName: "my-cluster"}}},
   114  		{config.RequirementProject, "my-project", config.RequirementsConfig{Cluster: config.ClusterConfig{ProjectID: "my-project"}}},
   115  		{config.RequirementZone, "my-zone", config.RequirementsConfig{Cluster: config.ClusterConfig{Zone: "my-zone"}}},
   116  		{config.RequirementChartRepository, "my-chart-museum", config.RequirementsConfig{Cluster: config.ClusterConfig{ChartRepository: "my-chart-museum"}}},
   117  		{config.RequirementRegistry, "my-registry", config.RequirementsConfig{Cluster: config.ClusterConfig{Registry: "my-registry"}}},
   118  		{config.RequirementEnvGitOwner, "john-doe", config.RequirementsConfig{Cluster: config.ClusterConfig{EnvironmentGitOwner: "john-doe"}}},
   119  		{config.RequirementKanikoServiceAccountName, "kaniko-sa", config.RequirementsConfig{Cluster: config.ClusterConfig{KanikoSAName: "kaniko-sa"}}},
   120  		{config.RequirementEnvGitPublic, "true", config.RequirementsConfig{Cluster: config.ClusterConfig{EnvironmentGitPublic: true}}},
   121  		{config.RequirementEnvGitPublic, "false", config.RequirementsConfig{Cluster: config.ClusterConfig{EnvironmentGitPublic: false}}},
   122  		{config.RequirementEnvGitPublic, "", config.RequirementsConfig{Cluster: config.ClusterConfig{EnvironmentGitPublic: false}}},
   123  		{config.RequirementGitPublic, "true", config.RequirementsConfig{Cluster: config.ClusterConfig{GitPublic: true}}},
   124  		{config.RequirementGitPublic, "false", config.RequirementsConfig{Cluster: config.ClusterConfig{GitPublic: false}}},
   125  		{config.RequirementGitPublic, "", config.RequirementsConfig{Cluster: config.ClusterConfig{GitPublic: false}}},
   126  		{config.RequirementExternalDNSServiceAccountName, "externaldns-sa", config.RequirementsConfig{Cluster: config.ClusterConfig{ExternalDNSSAName: "externaldns-sa"}}},
   127  
   128  		// VaultConfig
   129  		{config.RequirementVaultName, "my-vault", config.RequirementsConfig{Vault: config.VaultConfig{Name: "my-vault"}}},
   130  		{config.RequirementVaultServiceAccountName, "my-vault-sa", config.RequirementsConfig{Vault: config.VaultConfig{ServiceAccount: "my-vault-sa"}}},
   131  		{config.RequirementVaultKeyringName, "my-keyring", config.RequirementsConfig{Vault: config.VaultConfig{Keyring: "my-keyring"}}},
   132  		{config.RequirementVaultKeyName, "my-key", config.RequirementsConfig{Vault: config.VaultConfig{Key: "my-key"}}},
   133  		{config.RequirementVaultBucketName, "my-bucket", config.RequirementsConfig{Vault: config.VaultConfig{Bucket: "my-bucket"}}},
   134  		{config.RequirementVaultRecreateBucket, "true", config.RequirementsConfig{Vault: config.VaultConfig{RecreateBucket: true}}},
   135  		{config.RequirementVaultRecreateBucket, "false", config.RequirementsConfig{Vault: config.VaultConfig{RecreateBucket: false}}},
   136  		{config.RequirementVaultRecreateBucket, "", config.RequirementsConfig{Vault: config.VaultConfig{RecreateBucket: false}}},
   137  		{config.RequirementVaultDisableURLDiscovery, "true", config.RequirementsConfig{Vault: config.VaultConfig{DisableURLDiscovery: true}}},
   138  		{config.RequirementVaultDisableURLDiscovery, "false", config.RequirementsConfig{Vault: config.VaultConfig{DisableURLDiscovery: false}}},
   139  		{config.RequirementVaultDisableURLDiscovery, "", config.RequirementsConfig{Vault: config.VaultConfig{DisableURLDiscovery: false}}},
   140  
   141  		// VeleroConfig
   142  		{config.RequirementVeleroServiceAccountName, "my-velero-sa", config.RequirementsConfig{Velero: config.VeleroConfig{ServiceAccount: "my-velero-sa"}}},
   143  		{config.RequirementVeleroTTL, "60", config.RequirementsConfig{Velero: config.VeleroConfig{TimeToLive: "60"}}},
   144  		{config.RequirementVeleroSchedule, "0 * * * *", config.RequirementsConfig{Velero: config.VeleroConfig{Schedule: "0 * * * *"}}},
   145  
   146  		// IngressConfig
   147  		{config.RequirementDomainIssuerURL, "my-issuer-url", config.RequirementsConfig{Ingress: config.IngressConfig{DomainIssuerURL: "my-issuer-url"}}},
   148  
   149  		// Storage
   150  		{config.RequirementStorageBackupEnabled, "true", config.RequirementsConfig{Storage: config.StorageConfig{Backup: config.StorageEntryConfig{Enabled: true}}}},
   151  		{config.RequirementStorageBackupEnabled, "false", config.RequirementsConfig{Storage: config.StorageConfig{Backup: config.StorageEntryConfig{Enabled: false}}}},
   152  		{config.RequirementStorageBackupEnabled, "", config.RequirementsConfig{Storage: config.StorageConfig{Backup: config.StorageEntryConfig{Enabled: false}}}},
   153  		{config.RequirementStorageBackupURL, "gs://my-backup", config.RequirementsConfig{Storage: config.StorageConfig{Backup: config.StorageEntryConfig{URL: "gs://my-backup"}}}},
   154  
   155  		{config.RequirementStorageLogsEnabled, "true", config.RequirementsConfig{Storage: config.StorageConfig{Logs: config.StorageEntryConfig{Enabled: true}}}},
   156  		{config.RequirementStorageLogsEnabled, "false", config.RequirementsConfig{Storage: config.StorageConfig{Logs: config.StorageEntryConfig{Enabled: false}}}},
   157  		{config.RequirementStorageLogsEnabled, "", config.RequirementsConfig{Storage: config.StorageConfig{Logs: config.StorageEntryConfig{Enabled: false}}}},
   158  		{config.RequirementStorageLogsURL, "gs://my-logs", config.RequirementsConfig{Storage: config.StorageConfig{Logs: config.StorageEntryConfig{URL: "gs://my-logs"}}}},
   159  
   160  		{config.RequirementStorageReportsEnabled, "true", config.RequirementsConfig{Storage: config.StorageConfig{Reports: config.StorageEntryConfig{Enabled: true}}}},
   161  		{config.RequirementStorageReportsEnabled, "false", config.RequirementsConfig{Storage: config.StorageConfig{Reports: config.StorageEntryConfig{Enabled: false}}}},
   162  		{config.RequirementStorageReportsEnabled, "", config.RequirementsConfig{Storage: config.StorageConfig{Reports: config.StorageEntryConfig{Enabled: false}}}},
   163  		{config.RequirementStorageReportsURL, "gs://my-reports", config.RequirementsConfig{Storage: config.StorageConfig{Reports: config.StorageEntryConfig{URL: "gs://my-reports"}}}},
   164  
   165  		{config.RequirementStorageRepositoryEnabled, "true", config.RequirementsConfig{Storage: config.StorageConfig{Repository: config.StorageEntryConfig{Enabled: true}}}},
   166  		{config.RequirementStorageRepositoryEnabled, "false", config.RequirementsConfig{Storage: config.StorageConfig{Repository: config.StorageEntryConfig{Enabled: false}}}},
   167  		{config.RequirementStorageRepositoryEnabled, "", config.RequirementsConfig{Storage: config.StorageConfig{Repository: config.StorageEntryConfig{Enabled: false}}}},
   168  		{config.RequirementStorageRepositoryURL, "gs://my-repo", config.RequirementsConfig{Storage: config.StorageConfig{Repository: config.StorageEntryConfig{URL: "gs://my-repo"}}}},
   169  
   170  		// GKEConfig
   171  		{config.RequirementGkeProjectNumber, "my-gke-project", config.RequirementsConfig{Cluster: config.ClusterConfig{GKEConfig: &config.GKEConfig{ProjectNumber: "my-gke-project"}}}},
   172  
   173  		// VersionStreamConfig
   174  		{config.RequirementVersionsGitRef, "v1.0.0", config.RequirementsConfig{VersionStream: config.VersionStreamConfig{Ref: "v1.0.0"}}},
   175  	}
   176  
   177  	for _, overrideTest := range overrideTests {
   178  		origEnvValue, origValueSet := os.LookupEnv(overrideTest.envKey)
   179  		err := os.Setenv(overrideTest.envKey, overrideTest.envValue)
   180  		assert.NoError(t, err)
   181  		resetEnvVariable := func() {
   182  			var err error
   183  			if origValueSet {
   184  				err = os.Setenv(overrideTest.envKey, origEnvValue)
   185  			} else {
   186  				err = os.Unsetenv(overrideTest.envKey)
   187  			}
   188  			if err != nil {
   189  				log.Logger().Warnf("error resetting environment after test: %v", err)
   190  			}
   191  		}
   192  
   193  		t.Run(overrideTest.envKey, func(t *testing.T) {
   194  			actualRequirements := config.RequirementsConfig{}
   195  			actualRequirements.OverrideRequirementsFromEnvironment(func() gke.GClouder {
   196  				return nil
   197  			})
   198  
   199  			assert.Equal(t, overrideTest.expectedRequirements, actualRequirements)
   200  		})
   201  
   202  		resetEnvVariable()
   203  	}
   204  }
   205  
   206  func TestRequirementsConfigMarshalExistingFileKanikoFalse(t *testing.T) {
   207  	t.Parallel()
   208  
   209  	dir, err := ioutil.TempDir("", "test-requirements-config-")
   210  	assert.NoError(t, err, "should create a temporary config dir")
   211  
   212  	file := filepath.Join(dir, config.RequirementsConfigFileName)
   213  	requirements := config.NewRequirementsConfig()
   214  	requirements.Kaniko = false
   215  
   216  	err = requirements.SaveConfig(file)
   217  	assert.NoError(t, err, "failed to save file %s", file)
   218  
   219  	requirements, fileName, err := config.LoadRequirementsConfig(dir, config.DefaultFailOnValidationError)
   220  	assert.NoError(t, err, "failed to load requirements file in dir %s", dir)
   221  	assert.FileExists(t, fileName)
   222  
   223  	assert.Equal(t, false, requirements.Kaniko, "requirements.Kaniko")
   224  
   225  }
   226  
   227  func TestRequirementsConfigMarshalInEmptyDir(t *testing.T) {
   228  	t.Parallel()
   229  
   230  	dir, err := ioutil.TempDir("", "test-requirements-config-empty-")
   231  
   232  	requirements, fileName, err := config.LoadRequirementsConfig(dir, config.DefaultFailOnValidationError)
   233  	assert.Error(t, err)
   234  	assert.Empty(t, fileName)
   235  	assert.Nil(t, requirements)
   236  }
   237  
   238  func TestRequirementsConfigIngressAutoDNS(t *testing.T) {
   239  	t.Parallel()
   240  
   241  	requirements := config.NewRequirementsConfig()
   242  
   243  	requirements.Ingress.Domain = "1.2.3.4.nip.io"
   244  	assert.Equal(t, true, requirements.Ingress.IsAutoDNSDomain(), "requirements.Ingress.IsAutoDNSDomain() for domain %s", requirements.Ingress.Domain)
   245  
   246  	requirements.Ingress.Domain = "foo.bar"
   247  	assert.Equal(t, false, requirements.Ingress.IsAutoDNSDomain(), "requirements.Ingress.IsAutoDNSDomain() for domain %s", requirements.Ingress.Domain)
   248  
   249  	requirements.Ingress.Domain = ""
   250  	assert.Equal(t, false, requirements.Ingress.IsAutoDNSDomain(), "requirements.Ingress.IsAutoDNSDomain() for domain %s", requirements.Ingress.Domain)
   251  }
   252  
   253  func Test_unmarshalling_requirements_config_with_build_pack_configuration_succeeds(t *testing.T) {
   254  	t.Parallel()
   255  
   256  	requirements := config.NewRequirementsConfig()
   257  
   258  	content, err := ioutil.ReadFile(path.Join(testDataDir, "build_pack_library.yaml"))
   259  
   260  	err = yaml.Unmarshal(content, requirements)
   261  	assert.NoError(t, err)
   262  	assert.Equal(t, "Test name", requirements.BuildPacks.BuildPackLibrary.Name, "requirements.buildPacks.BuildPackLibrary.name is not equivalent to test name")
   263  	assert.Equal(t, "github.com", requirements.BuildPacks.BuildPackLibrary.GitURL, "requirements.buildPacks.BuildPackLibrary.gitURL is not equivalent to git url ")
   264  	assert.Equal(t, "master", requirements.BuildPacks.BuildPackLibrary.GitRef, "requirements.buildPacks.BuildPackLibrary.gitRef is not equivalent git Ref")
   265  }
   266  
   267  func Test_marshalling_empty_requirements_config_creates_no_build_pack_configuration(t *testing.T) {
   268  	t.Parallel()
   269  
   270  	requirements := config.NewRequirementsConfig()
   271  	data, err := yaml.Marshal(requirements)
   272  	assert.NoError(t, err)
   273  	assert.NotContains(t, string(data), "buildPacks")
   274  
   275  	err = yaml.Unmarshal(data, requirements)
   276  	assert.NoError(t, err)
   277  	assert.Nil(t, requirements.BuildPacks)
   278  }
   279  
   280  func Test_marshalling_vault_config(t *testing.T) {
   281  	t.Parallel()
   282  
   283  	requirements := config.NewRequirementsConfig()
   284  	requirements.Vault = config.VaultConfig{
   285  		Name:                   "myVault",
   286  		URL:                    "http://myvault",
   287  		ServiceAccount:         "vault-sa",
   288  		Namespace:              "jx",
   289  		KubernetesAuthPath:     "kubernetes",
   290  		SecretEngineMountPoint: "secret",
   291  	}
   292  	data, err := yaml.Marshal(requirements)
   293  	assert.NoError(t, err)
   294  
   295  	assert.Contains(t, string(data), "name: myVault")
   296  	assert.Contains(t, string(data), "url: http://myvault")
   297  	assert.Contains(t, string(data), "serviceAccount: vault-sa")
   298  	assert.Contains(t, string(data), "namespace: jx")
   299  	assert.Contains(t, string(data), "kubernetesAuthPath: kubernetes")
   300  	assert.Contains(t, string(data), "secretEngineMountPoint: secret")
   301  }
   302  
   303  func Test_env_repository_visibility(t *testing.T) {
   304  	t.Parallel()
   305  
   306  	var gitPublicTests = []struct {
   307  		yamlFile          string
   308  		expectedGitPublic bool
   309  	}{
   310  		{"git_public_nil_git_private_true.yaml", false},
   311  		{"git_public_nil_git_private_false.yaml", true},
   312  		{"git_public_false_git_private_nil.yaml", false},
   313  		{"git_public_true_git_private_nil.yaml", true},
   314  	}
   315  
   316  	for _, testCase := range gitPublicTests {
   317  		t.Run(testCase.yamlFile, func(t *testing.T) {
   318  			content, err := ioutil.ReadFile(path.Join(testDataDir, testCase.yamlFile))
   319  			assert.NoError(t, err)
   320  
   321  			config := config.NewRequirementsConfig()
   322  
   323  			_ = log.CaptureOutput(func() {
   324  				err = yaml.Unmarshal(content, config)
   325  				assert.NoError(t, err)
   326  				assert.Equal(t, testCase.expectedGitPublic, config.Cluster.EnvironmentGitPublic, "unexpected value for repository visibility")
   327  			})
   328  		})
   329  	}
   330  }
   331  
   332  func TestMergeSave(t *testing.T) {
   333  	t.Parallel()
   334  	type TestSpec struct {
   335  		Name           string
   336  		Original       *config.RequirementsConfig
   337  		Changed        *config.RequirementsConfig
   338  		ValidationFunc func(orig *config.RequirementsConfig, ch *config.RequirementsConfig)
   339  	}
   340  
   341  	testCases := []TestSpec{
   342  		{
   343  			Name: "Merge Cluster Config Test",
   344  			Original: &config.RequirementsConfig{
   345  				Cluster: config.ClusterConfig{
   346  					EnvironmentGitOwner:  "owner",
   347  					EnvironmentGitPublic: false,
   348  					GitPublic:            false,
   349  					Provider:             cloud.GKE,
   350  					Namespace:            "jx",
   351  					ProjectID:            "project-id",
   352  					ClusterName:          "cluster-name",
   353  					Region:               "region",
   354  					GitKind:              gits.KindGitHub,
   355  					GitServer:            gits.KindGitHub,
   356  				},
   357  			},
   358  			Changed: &config.RequirementsConfig{
   359  				Cluster: config.ClusterConfig{
   360  					EnvironmentGitPublic: true,
   361  					GitPublic:            true,
   362  					Provider:             cloud.GKE,
   363  				},
   364  			},
   365  			ValidationFunc: func(orig *config.RequirementsConfig, ch *config.RequirementsConfig) {
   366  				assert.True(t, orig.Cluster.EnvironmentGitPublic == ch.Cluster.EnvironmentGitPublic &&
   367  					orig.Cluster.GitPublic == ch.Cluster.GitPublic &&
   368  					orig.Cluster.ProjectID != ch.Cluster.ProjectID, "ClusterConfig validation failed")
   369  			},
   370  		},
   371  		{
   372  			Name: "Merge EnvironmentConfig slices Test",
   373  			Original: &config.RequirementsConfig{
   374  				Environments: []config.EnvironmentConfig{
   375  					{
   376  						Key:        "dev",
   377  						Repository: "repo",
   378  					},
   379  					{
   380  						Key: "production",
   381  						Ingress: config.IngressConfig{
   382  							Domain: "domain",
   383  						},
   384  					},
   385  					{
   386  						Key: "staging",
   387  						Ingress: config.IngressConfig{
   388  							Domain: "domainStaging",
   389  							TLS: config.TLSConfig{
   390  								Email: "email",
   391  							},
   392  						},
   393  					},
   394  				},
   395  			},
   396  			Changed: &config.RequirementsConfig{
   397  				Environments: []config.EnvironmentConfig{
   398  					{
   399  						Key:   "dev",
   400  						Owner: "owner",
   401  					},
   402  					{
   403  						Key: "production",
   404  						Ingress: config.IngressConfig{
   405  							CloudDNSSecretName: "secret",
   406  						},
   407  					},
   408  					{
   409  						Key: "staging",
   410  						Ingress: config.IngressConfig{
   411  							Domain:          "newDomain",
   412  							DomainIssuerURL: "issuer",
   413  							TLS: config.TLSConfig{
   414  								Enabled: true,
   415  							},
   416  						},
   417  					},
   418  					{
   419  						Key: "ns2",
   420  					},
   421  				},
   422  			},
   423  			ValidationFunc: func(orig *config.RequirementsConfig, ch *config.RequirementsConfig) {
   424  				assert.True(t, len(orig.Environments) == len(ch.Environments), "the environment slices should be of the same len")
   425  				// -- Dev Environment's asserts
   426  				devOrig, devCh := orig.Environments[0], ch.Environments[0]
   427  				assert.True(t, devOrig.Owner == devCh.Owner && devOrig.Repository != devCh.Repository,
   428  					"the dev environment should've been merged correctly")
   429  				// -- Production Environment's asserts
   430  				prOrig, prCh := orig.Environments[1], ch.Environments[1]
   431  				assert.True(t, prOrig.Ingress.Domain == "domain" &&
   432  					prOrig.Ingress.CloudDNSSecretName == prCh.Ingress.CloudDNSSecretName,
   433  					"the production environment should've been merged correctly")
   434  				// -- Staging Environmnet's asserts
   435  				stgOrig, stgCh := orig.Environments[2], ch.Environments[2]
   436  				assert.True(t, stgOrig.Ingress.Domain == stgCh.Ingress.Domain &&
   437  					stgOrig.Ingress.TLS.Email == "email" && stgOrig.Ingress.TLS.Enabled == stgCh.Ingress.TLS.Enabled,
   438  					"the staging environment should've been merged correctly")
   439  			},
   440  		},
   441  		{
   442  			Name: "Merge StorageConfig test",
   443  			Original: &config.RequirementsConfig{
   444  				Storage: config.StorageConfig{
   445  					Logs: config.StorageEntryConfig{
   446  						Enabled: true,
   447  						URL:     "value1",
   448  					},
   449  					Reports: config.StorageEntryConfig{},
   450  					Repository: config.StorageEntryConfig{
   451  						Enabled: true,
   452  						URL:     "value3",
   453  					},
   454  				},
   455  			},
   456  			Changed: &config.RequirementsConfig{
   457  				Storage: config.StorageConfig{
   458  					Reports: config.StorageEntryConfig{
   459  						Enabled: true,
   460  						URL:     "",
   461  					},
   462  				},
   463  			},
   464  			ValidationFunc: func(orig *config.RequirementsConfig, ch *config.RequirementsConfig) {
   465  				assert.True(t, orig.Storage.Logs.Enabled && orig.Storage.Logs.URL == "value1" &&
   466  					orig.Storage.Repository.Enabled && orig.Storage.Repository.URL == "value3" &&
   467  					orig.Storage.Reports.Enabled == ch.Storage.Reports.Enabled,
   468  					"The storage configuration should've been merged correctly")
   469  			},
   470  		},
   471  	}
   472  	f, err := ioutil.TempFile("", "")
   473  	assert.NoError(t, err)
   474  	defer util.DeleteFile(f.Name())
   475  
   476  	for _, tc := range testCases {
   477  		t.Run(tc.Name, func(t *testing.T) {
   478  			err = tc.Original.MergeSave(tc.Changed, f.Name())
   479  			assert.NoError(t, err, "the merge shouldn't fail for case %s", tc.Name)
   480  			tc.ValidationFunc(tc.Original, tc.Changed)
   481  		})
   482  	}
   483  }
   484  
   485  func Test_EnvironmentGitPublic_and_EnvironmentGitPrivate_specified_together_return_error(t *testing.T) {
   486  	content, err := ioutil.ReadFile(path.Join(testDataDir, "git_public_true_git_private_true.yaml"))
   487  	assert.NoError(t, err)
   488  
   489  	config := config.NewRequirementsConfig()
   490  	err = yaml.Unmarshal(content, config)
   491  	assert.Error(t, err)
   492  	assert.Contains(t, err.Error(), "only EnvironmentGitPublic should be used")
   493  }
   494  
   495  func TestHelmfileRequirementValues(t *testing.T) {
   496  	t.Parallel()
   497  
   498  	dir, err := ioutil.TempDir("", "test-requirements-config-")
   499  	assert.NoError(t, err, "should create a temporary config dir")
   500  
   501  	file := filepath.Join(dir, config.RequirementsConfigFileName)
   502  	requirements := config.NewRequirementsConfig()
   503  	requirements.Cluster.ClusterName = "jx_rocks"
   504  
   505  	err = requirements.SaveConfig(file)
   506  	assert.NoError(t, err, "failed to save file %s", file)
   507  	assert.FileExists(t, file)
   508  	valuesFile := filepath.Join(dir, config.RequirementsValuesFileName)
   509  
   510  	valuesFileExists, err := util.FileExists(valuesFile)
   511  	assert.False(t, valuesFileExists, "file %s should not exist", valuesFile)
   512  
   513  	requirements.Helmfile = true
   514  	err = requirements.SaveConfig(file)
   515  	assert.NoError(t, err, "failed to save file %s", file)
   516  	assert.FileExists(t, file)
   517  	assert.FileExists(t, valuesFile, "file %s should exist", valuesFile)
   518  }
   519  
   520  func Test_LoadRequirementsConfig(t *testing.T) {
   521  	t.Parallel()
   522  
   523  	var gitPublicTests = []struct {
   524  		requirementsPath   string
   525  		createRequirements bool
   526  	}{
   527  		{"a", false},
   528  		{"a/b", false},
   529  		{"a/b/c", false},
   530  		{"e", true},
   531  		{"e/f", true},
   532  		{"e/f/g", true},
   533  	}
   534  
   535  	for _, testCase := range gitPublicTests {
   536  		t.Run(testCase.requirementsPath, func(t *testing.T) {
   537  			dir, err := ioutil.TempDir("", "jx-test-load-requirements-config")
   538  			require.NoError(t, err, "failed to create tmp directory")
   539  			defer func() {
   540  				_ = os.RemoveAll(dir)
   541  			}()
   542  
   543  			testPath := filepath.Join(dir, testCase.requirementsPath)
   544  			err = os.MkdirAll(testPath, 0700)
   545  			require.NoError(t, err, "unable to create test path %s", testPath)
   546  
   547  			var expectedRequirementsFile string
   548  			if testCase.createRequirements {
   549  				expectedRequirementsFile = filepath.Join(testPath, config.RequirementsConfigFileName)
   550  				dummyRequirementsData := []byte("webhook: prow\n")
   551  				err := ioutil.WriteFile(expectedRequirementsFile, dummyRequirementsData, 0600)
   552  				require.NoError(t, err, "unable to write requirements file %s", expectedRequirementsFile)
   553  			}
   554  
   555  			requirements, requirementsFile, err := config.LoadRequirementsConfig(testPath, config.DefaultFailOnValidationError)
   556  			if testCase.createRequirements {
   557  				require.NoError(t, err)
   558  				assert.Equal(t, expectedRequirementsFile, requirementsFile)
   559  				assert.Equal(t, fmt.Sprintf("%s", requirements.Webhook), "prow")
   560  			} else {
   561  				require.Error(t, err)
   562  				assert.Equal(t, "", requirementsFile)
   563  				assert.Nil(t, requirements)
   564  			}
   565  		})
   566  	}
   567  }
   568  
   569  func TestLoadRequirementsConfig_load_invalid_yaml(t *testing.T) {
   570  	testDir := path.Join(testDataDir, "jx-requirements-syntax-error")
   571  
   572  	absolute, err := filepath.Abs(testDir)
   573  	assert.NoError(t, err, "could not find absolute path of dir %s", testDataDir)
   574  
   575  	_, _, err = config.LoadRequirementsConfig(testDir, config.DefaultFailOnValidationError)
   576  	requirementsConfigPath := path.Join(absolute, config.RequirementsConfigFileName)
   577  	assert.EqualError(t, err, fmt.Sprintf("validation failures in YAML file %s:\nenvironments.0: Additional property namespace is not allowed", requirementsConfigPath))
   578  }