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

     1  // +build unit
     2  
     3  package helm_test
     4  
     5  import (
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"testing"
    11  
    12  	kube_test "github.com/jenkins-x/jx/v2/pkg/kube/mocks"
    13  
    14  	"github.com/jenkins-x/jx/v2/pkg/helm"
    15  	"github.com/stretchr/testify/assert"
    16  
    17  	mocks "github.com/jenkins-x/jx/v2/pkg/util/mocks"
    18  	. "github.com/petergtz/pegomock"
    19  )
    20  
    21  const binary = "helm"
    22  const binaryV3 = "helm3"
    23  const cwd = "test"
    24  const repo = "test-repo"
    25  const repoURL = "http://test-repo"
    26  const serviceAccount = "test-sa"
    27  const namespace = "test-namespace"
    28  const chart = "test-chart"
    29  const releaseName = "test-release"
    30  const listRepoOutput = `
    31  NAME            URL
    32  stable          https://kubernetes-charts.storage.googleapis.com
    33  local           http://127.0.0.1:8879/charts
    34  jenkins-x       https://storage.googleapis.com/chartmuseum.jenkins-x.io
    35  	`
    36  const searchVersionOutput = `
    37  NAME                            		CHART VERSION	APP VERSION		DESCRIPTION
    38  jenkins-x/jenkins-x-platform        	0.0.1481						Jenkins X
    39  jenkins-x/jenkins-x-platform        	0.0.1480						Jenkins X
    40  jenkins-x/jenkins-x-platform        	0.0.1479 						Jenkins X
    41  `
    42  const listReleasesOutput = `
    43  NAME                            REVISION        UPDATED                         STATUS          CHART                           NAMESPACE
    44  jenkins-x                       1               Mon Jul  2 16:16:20 2018        DEPLOYED        jenkins-x-platform-0.0.1655     jx
    45  jx-production                   1               Mon Jul  2 16:22:47 2018        DEPLOYED        env-0.0.1                       jx-production
    46  jx-staging                      1               Mon Jul  2 16:21:06 2018        DEPLOYED        env-0.0.1                       jx-staging
    47  jxing                           1               Wed Jun  6 14:24:42 2018        DEPLOYED        nginx-ingress-0.20.1            kube-system
    48  vault-operator                  1               Mon Jun 25 16:09:28 2018        DEPLOYED        vault-operator-0.1.0            jx
    49  `
    50  
    51  const listReleasesOutputHelm3 = `
    52  NAME 	NAMESPACE	REVISION	UPDATED                             	STATUS  	CHART
    53  jxing	jx       	2       	2019-05-17 15:30:07.629472 +0100 BST	deployed	nginx-ingress-1.3.1`
    54  
    55  func createHelm(t *testing.T, expectedError error, expectedOutput string) (*helm.HelmCLI, *mocks.MockCommander) {
    56  	return createHelmWithCwdAndHelmVersion(t, helm.V2, cwd, expectedError, expectedOutput)
    57  }
    58  
    59  func createHelmWithVersion(t *testing.T, version helm.Version, expectedError error, expectedOutput string) (*helm.HelmCLI, *mocks.MockCommander) {
    60  	return createHelmWithCwdAndHelmVersion(t, version, cwd, expectedError, expectedOutput)
    61  }
    62  
    63  func createHelmWithCwdAndHelmVersion(t *testing.T, version helm.Version, dir string, expectedError error, expectedOutput string) (*helm.HelmCLI, *mocks.MockCommander) {
    64  	RegisterMockTestingT(t)
    65  	runner := mocks.NewMockCommander()
    66  	When(runner.RunWithoutRetry()).ThenReturn(expectedOutput, expectedError)
    67  	helmBinary := binary
    68  	if version == helm.V3 {
    69  		helmBinary = binaryV3
    70  	}
    71  	mockKuber := kube_test.NewMockKuber()
    72  	cli := helm.NewHelmCLIWithRunner(runner, helmBinary, version, dir, true, mockKuber)
    73  	return cli, runner
    74  }
    75  
    76  func verifyArgs(t *testing.T, cli *helm.HelmCLI, runner *mocks.MockCommander, expectedArgs ...string) {
    77  	runner.VerifyWasCalledOnce().SetArgs(expectedArgs)
    78  }
    79  
    80  func TestNewHelmCLI(t *testing.T) {
    81  	cli := helm.NewHelmCLI(binary, helm.V2, cwd, true, "arg1 arg2 arg3")
    82  	assert.Equal(t, binary, cli.Binary)
    83  	assert.Equal(t, cwd, cli.CWD)
    84  	assert.Equal(t, helm.V2, cli.BinVersion)
    85  	assert.Equal(t, true, cli.Debug)
    86  	assert.NotNil(t, cli.Runner)
    87  	assert.Equal(t, []string{"arg1", "arg2", "arg3"}, cli.Runner.CurrentArgs())
    88  	assert.Equal(t, binary, cli.Runner.CurrentName())
    89  	assert.Equal(t, cwd, cli.Runner.CurrentDir())
    90  }
    91  
    92  func TestInit(t *testing.T) {
    93  	expectedArgs := []string{"init", "--client-only", "--service-account", serviceAccount,
    94  		"--tiller-namespace", namespace, "--upgrade", "--wait", "--force-upgrade"}
    95  	helm, runner := createHelm(t, nil, "")
    96  
    97  	err := helm.Init(true, serviceAccount, namespace, true)
    98  
    99  	assert.NoError(t, err, "should init helm without any error")
   100  	verifyArgs(t, helm, runner, expectedArgs...)
   101  }
   102  
   103  func TestAddRepo(t *testing.T) {
   104  	expectedArgs := []string{"repo", "add", repo, repoURL}
   105  	helm, runner := createHelm(t, nil, "")
   106  
   107  	err := helm.AddRepo(repo, repoURL, "", "")
   108  
   109  	assert.NoError(t, err, "should add helm repo without any error")
   110  	verifyArgs(t, helm, runner, expectedArgs...)
   111  }
   112  
   113  func TestRemoveRepo(t *testing.T) {
   114  	expectedArgs := []string{"repo", "remove", repo}
   115  	helm, runner := createHelm(t, nil, "")
   116  
   117  	err := helm.RemoveRepo(repo)
   118  
   119  	assert.NoError(t, err, "should remove helm repo without any error")
   120  	verifyArgs(t, helm, runner, expectedArgs...)
   121  }
   122  
   123  func TestListRepos(t *testing.T) {
   124  	expectedArgs := []string{"repo", "list"}
   125  	helm, runner := createHelm(t, nil, listRepoOutput)
   126  
   127  	repos, err := helm.ListRepos()
   128  	assert.NoError(t, err, "should list helm repos without any error")
   129  	verifyArgs(t, helm, runner, expectedArgs...)
   130  	expectedRepos := map[string]string{
   131  		"stable":    "https://kubernetes-charts.storage.googleapis.com",
   132  		"local":     "http://127.0.0.1:8879/charts",
   133  		"jenkins-x": "https://storage.googleapis.com/chartmuseum.jenkins-x.io",
   134  	}
   135  	assert.Equal(t, len(expectedRepos), len(repos), "should list the same number of repos")
   136  	for k, v := range repos {
   137  		assert.Equal(t, expectedRepos[k], v, "should parse correctly the repo URL")
   138  	}
   139  }
   140  
   141  func TestIsRepoMissing(t *testing.T) {
   142  	expectedArgs := []string{"repo", "list"}
   143  	helm, runner := createHelm(t, nil, listRepoOutput)
   144  
   145  	url := "https://storage.googleapis.com/chartmuseum.jenkins-x.io"
   146  	missing, _, err := helm.IsRepoMissing(url)
   147  
   148  	assert.NoError(t, err, "should search missing repos without any error")
   149  	verifyArgs(t, helm, runner, expectedArgs...)
   150  	assert.False(t, missing, "should find url '%s'", url)
   151  
   152  	url = "https://test"
   153  	missing, _, err = helm.IsRepoMissing(url)
   154  
   155  	assert.NoError(t, err, "search missing repos should not return an error")
   156  	assert.True(t, missing, "should not find url '%s'", url)
   157  
   158  	url = "http://127.0.0.1:8879/chartsv2"
   159  	missing, _, err = helm.IsRepoMissing(url)
   160  
   161  	assert.NoError(t, err, "search missing repos should not return an error")
   162  	assert.True(t, missing, "should not find url '%s'", url)
   163  }
   164  
   165  func TestUpdateRepo(t *testing.T) {
   166  	expectedArgs := []string{"repo", "update"}
   167  	helm, runner := createHelm(t, nil, "")
   168  
   169  	err := helm.UpdateRepo()
   170  
   171  	assert.NoError(t, err, "should update  helm repo without any error")
   172  	verifyArgs(t, helm, runner, expectedArgs...)
   173  }
   174  
   175  func TestRemoveRequirementsLock(t *testing.T) {
   176  	dir, err := ioutil.TempDir("", "reqtest")
   177  	assert.NoError(t, err, "should be able to create a temporary dir")
   178  	defer os.RemoveAll(dir)
   179  	path := filepath.Join(dir, "requirements.lock")
   180  	ioutil.WriteFile(path, []byte("test"), 0600)
   181  
   182  	helm, _ := createHelmWithCwdAndHelmVersion(t, helm.V2, dir, nil, "")
   183  
   184  	err = helm.RemoveRequirementsLock()
   185  	assert.NoError(t, err, "should remove requirements.lock file")
   186  }
   187  
   188  func TestBuildDependency(t *testing.T) {
   189  	expectedArgs := []string{"dependency", "build"}
   190  	helm, runner := createHelm(t, nil, "")
   191  
   192  	err := helm.BuildDependency()
   193  	assert.NoError(t, err, "should build helm repo dependencies without any error")
   194  	verifyArgs(t, helm, runner, expectedArgs...)
   195  }
   196  
   197  func TestInstallChart(t *testing.T) {
   198  	value := []string{"test=true"}
   199  	valueString := []string{"context=test"}
   200  	valueFile := []string{"./myvalues.yaml"}
   201  	expectedArgs := []string{"install", "--wait", "--name", releaseName, "--namespace", namespace,
   202  		chart, "--set", value[0], "--set-string", valueString[0], "--values", valueFile[0]}
   203  	helm, runner := createHelm(t, nil, "")
   204  
   205  	err := helm.InstallChart(chart, releaseName, namespace, "", -1, value, valueString, valueFile, "", "", "")
   206  	assert.NoError(t, err, "should install the chart without any error")
   207  	verifyArgs(t, helm, runner, expectedArgs...)
   208  }
   209  
   210  func TestUpgradeChart(t *testing.T) {
   211  	value := []string{"test=true"}
   212  	valueString := []string{"context=test"}
   213  	valueFile := []string{"./myvalues.yaml"}
   214  	version := "0.0.1"
   215  	timeout := 600
   216  	expectedArgs := []string{"upgrade", "--namespace", namespace, "--install", "--wait", "--force",
   217  		"--timeout", fmt.Sprintf("%d", timeout), "--version", version, "--set", value[0], "--set-string", valueString[0], "--values", valueFile[0], releaseName, chart}
   218  	helm, runner := createHelm(t, nil, "")
   219  
   220  	err := helm.UpgradeChart(chart, releaseName, namespace, version, true, timeout, true, true, value, valueString, valueFile, "", "", "")
   221  
   222  	assert.NoError(t, err, "should upgrade the chart without any error")
   223  	verifyArgs(t, helm, runner, expectedArgs...)
   224  }
   225  
   226  func TestDeleteRelaese(t *testing.T) {
   227  	expectedArgs := []string{"delete", "--purge", releaseName}
   228  	helm, runner := createHelm(t, nil, "")
   229  	ns := "default"
   230  
   231  	err := helm.DeleteRelease(ns, releaseName, true)
   232  
   233  	assert.NoError(t, err, "should delete helm chart release without any error")
   234  	verifyArgs(t, helm, runner, expectedArgs...)
   235  }
   236  
   237  func TestStatusRelease(t *testing.T) {
   238  	expectedArgs := []string{"status", releaseName}
   239  	helm, runner := createHelm(t, nil, "")
   240  	ns := "default"
   241  
   242  	err := helm.StatusRelease(ns, releaseName)
   243  
   244  	assert.NoError(t, err, "should get the status of a helm chart release without any error")
   245  	verifyArgs(t, helm, runner, expectedArgs...)
   246  }
   247  
   248  func TestStatusReleaseWithOutputNoFormat(t *testing.T) {
   249  	expectedArgs := []string{"status", releaseName}
   250  	helm, runner := createHelm(t, nil, "")
   251  	ns := "default"
   252  
   253  	_, err := helm.StatusReleaseWithOutput(ns, releaseName, "")
   254  
   255  	assert.NoErrorf(t, err, "should return the status of the helm chart without format")
   256  	verifyArgs(t, helm, runner, expectedArgs...)
   257  }
   258  
   259  func TestStatusReleaseWithOutputWithFormat(t *testing.T) {
   260  	expectedArgs := []string{"status", releaseName, "--output", "json"}
   261  	helm, runner := createHelm(t, nil, "")
   262  	ns := "default"
   263  
   264  	_, err := helm.StatusReleaseWithOutput(ns, releaseName, "json")
   265  
   266  	assert.NoErrorf(t, err, "should return the status of the helm chart without in Json format")
   267  	verifyArgs(t, helm, runner, expectedArgs...)
   268  }
   269  
   270  func TestStatusReleases(t *testing.T) {
   271  	expectedArgs := []string{"list", "--all", "--namespace", "default"}
   272  	expectedStatusMap := map[string]string{
   273  		"jenkins-x":      "DEPLOYED",
   274  		"jx-production":  "DEPLOYED",
   275  		"jx-staging":     "DEPLOYED",
   276  		"jxing":          "DEPLOYED",
   277  		"vault-operator": "DEPLOYED",
   278  	}
   279  	helm, runner := createHelm(t, nil, listReleasesOutput)
   280  	ns := "default"
   281  
   282  	releaseMap, _, err := helm.ListReleases(ns)
   283  
   284  	assert.NoError(t, err, "should list the release statuses without any error")
   285  	verifyArgs(t, helm, runner, expectedArgs...)
   286  	for release, details := range releaseMap {
   287  		assert.Equal(t, expectedStatusMap[release], details.Status, "expected details '%s', got '%s'",
   288  			expectedStatusMap[release], details)
   289  	}
   290  }
   291  
   292  func TestStatusReleasesForHelm3(t *testing.T) {
   293  	expectedArgs := []string{"list", "--all", "--namespace", "default"}
   294  	expectedStatusMap := map[string]string{
   295  		"jxing": "DEPLOYED",
   296  	}
   297  	helm, runner := createHelmWithVersion(t, helm.V3, nil, listReleasesOutputHelm3)
   298  	ns := "default"
   299  
   300  	releaseMap, _, err := helm.ListReleases(ns)
   301  
   302  	assert.NoError(t, err, "should list the release statuses without any error")
   303  	verifyArgs(t, helm, runner, expectedArgs...)
   304  	for release, details := range releaseMap {
   305  		assert.Equal(t, expectedStatusMap[release], details.Status, "expected details '%s', got '%s'",
   306  			expectedStatusMap[release], details)
   307  	}
   308  }
   309  
   310  func TestLint(t *testing.T) {
   311  	expectedArgs := []string{"lint",
   312  		"--set", "tags.jx-lint=true",
   313  		"--set", "global.jxLint=true",
   314  		"--set-string", "global.jxTypeEnv=lint",
   315  	}
   316  	expectedOutput := "test"
   317  	helm, runner := createHelm(t, nil, expectedOutput)
   318  
   319  	output, err := helm.Lint(nil)
   320  
   321  	assert.NoError(t, err, "should lint the chart without any error")
   322  	verifyArgs(t, helm, runner, expectedArgs...)
   323  	assert.Equal(t, "test", output)
   324  }
   325  
   326  func TestVersion(t *testing.T) {
   327  	var versionTests = []struct {
   328  		versionString           string
   329  		expectedSemanticVersion string
   330  		shouldError             bool
   331  	}{
   332  		{"Client: v2.16.3+g1ee0254", "2.16.3", false},
   333  		{"v3.0.3+gac925eb", "3.0.3", false},
   334  		{"3.0.3+gac925eb", "3.0.3", false},
   335  		{"", "", true},
   336  		{"foo", "", true},
   337  	}
   338  
   339  	for _, versionTest := range versionTests {
   340  		t.Run(versionTest.versionString, func(t *testing.T) {
   341  			helm, runner := createHelm(t, nil, versionTest.versionString)
   342  			actualVersion, err := helm.Version(false)
   343  
   344  			if versionTest.shouldError {
   345  				assert.Error(t, err, "should not be able to get semantic version from version output")
   346  				assert.Equal(t, versionTest.expectedSemanticVersion, actualVersion)
   347  			} else {
   348  				assert.NoError(t, err, "should get the version without any error")
   349  				verifyArgs(t, helm, runner, "version", "--short", "--client")
   350  				assert.Equal(t, versionTest.expectedSemanticVersion, actualVersion)
   351  			}
   352  		})
   353  	}
   354  }
   355  
   356  func TestSearchChartVersions(t *testing.T) {
   357  	expectedOutput := searchVersionOutput
   358  	expectedArgs := []string{"search", chart, "--versions"}
   359  	helm, runner := createHelm(t, nil, expectedOutput)
   360  
   361  	versions, err := helm.SearchCharts(chart, true)
   362  
   363  	assert.NoError(t, err, "should search chart versions without any error")
   364  	verifyArgs(t, helm, runner, expectedArgs...)
   365  	expectedVersions := []string{"0.0.1481", "0.0.1480", "0.0.1479"}
   366  	for i, version := range versions {
   367  		assert.Equal(t, expectedVersions[i], version.ChartVersion, "should parse the version '%s'", version)
   368  	}
   369  }
   370  
   371  func TestFindChart(t *testing.T) {
   372  	dir, err := ioutil.TempDir("", "charttest")
   373  	assert.NoError(t, err, "should be able to create a temporary dir")
   374  	defer os.RemoveAll(dir)
   375  	path := filepath.Join(dir, helm.ChartFileName)
   376  	ioutil.WriteFile(path, []byte("test"), 0600)
   377  	helm, _ := createHelmWithCwdAndHelmVersion(t, helm.V2, dir, nil, "")
   378  
   379  	chartFile, err := helm.FindChart()
   380  
   381  	assert.NoError(t, err, "should find the chart file")
   382  	assert.Equal(t, path, chartFile, "should find chart file '%s'", path)
   383  }
   384  
   385  func TestPackage(t *testing.T) {
   386  	expectedArgs := []string{"package", cwd}
   387  	helm, runner := createHelm(t, nil, "")
   388  
   389  	err := helm.PackageChart()
   390  
   391  	assert.NoError(t, err, "should package chart without any error")
   392  	verifyArgs(t, helm, runner, expectedArgs...)
   393  }