github.com/darmach/terratest@v0.34.8-0.20210517103231-80931f95e3ff/test/helm_basic_example_template_test.go (about)

     1  // +build kubeall helm
     2  
     3  // **NOTE**: we have build tags to differentiate kubernetes tests from non-kubernetes tests, and further differentiate helm
     4  // tests. This is done because minikube is heavy and can interfere with docker related tests in terratest. Similarly, helm
     5  // can overload the minikube system and thus interfere with the other kubernetes tests. Specifically, many of the tests
     6  // start to fail with `connection refused` errors from `minikube`. To avoid overloading the system, we run the kubernetes
     7  // tests and helm tests separately from the others. This may not be necessary if you have a sufficiently powerful machine.
     8  // We recommend at least 4 cores and 16GB of RAM if you want to run all the tests together.
     9  
    10  package test
    11  
    12  import (
    13  	"path/filepath"
    14  	"strings"
    15  	"testing"
    16  
    17  	"github.com/stretchr/testify/require"
    18  	appsv1 "k8s.io/api/apps/v1"
    19  
    20  	"github.com/gruntwork-io/terratest/modules/helm"
    21  	"github.com/gruntwork-io/terratest/modules/k8s"
    22  	"github.com/gruntwork-io/terratest/modules/logger"
    23  	"github.com/gruntwork-io/terratest/modules/random"
    24  )
    25  
    26  // This file contains examples of how to use terratest to test helm chart template logic by rendering the templates
    27  // using `helm template`, and then reading in the rendered templates.
    28  // There are two tests:
    29  // - TestHelmBasicExampleTemplateRenderedDeployment: An example of how to read in the rendered object and check the
    30  //   computed values.
    31  // - TestHelmBasicExampleTemplateRequiredTemplateArgs: An example of how to check that the required args are indeed
    32  //   required for the template to render.
    33  
    34  // An example of how to verify the rendered template object of a Helm Chart given various inputs.
    35  func TestHelmBasicExampleTemplateRenderedDeployment(t *testing.T) {
    36  	t.Parallel()
    37  
    38  	// Path to the helm chart we will test
    39  	helmChartPath, err := filepath.Abs("../examples/helm-basic-example")
    40  	releaseName := "helm-basic"
    41  	require.NoError(t, err)
    42  
    43  	// Since we aren't deploying any resources, there is no need to setup kubectl authentication or helm home.
    44  
    45  	// Set up the namespace; confirm that the template renders the expected value for the namespace.
    46  	namespaceName := "medieval-" + strings.ToLower(random.UniqueId())
    47  	logger.Logf(t, "Namespace: %s\n", namespaceName)
    48  
    49  	// Setup the args. For this test, we will set the following input values:
    50  	// - containerImageRepo=nginx
    51  	// - containerImageTag=1.15.8
    52  	options := &helm.Options{
    53  		SetValues: map[string]string{
    54  			"containerImageRepo": "nginx",
    55  			"containerImageTag":  "1.15.8",
    56  		},
    57  		KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
    58  	}
    59  
    60  	// Run RenderTemplate to render the template and capture the output. Note that we use the version without `E`, since
    61  	// we want to assert that the template renders without any errors.
    62  	// Additionally, although we know there is only one yaml file in the template, we deliberately path a templateFiles
    63  	// arg to demonstrate how to select individual templates to render.
    64  	output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/deployment.yaml"})
    65  
    66  	// Now we use kubernetes/client-go library to render the template output into the Deployment struct. This will
    67  	// ensure the Deployment resource is rendered correctly.
    68  	var deployment appsv1.Deployment
    69  	helm.UnmarshalK8SYaml(t, output, &deployment)
    70  
    71  	// Verify the namespace matches the expected supplied namespace.
    72  	require.Equal(t, namespaceName, deployment.Namespace)
    73  
    74  	// Finally, we verify the deployment pod template spec is set to the expected container image value
    75  	expectedContainerImage := "nginx:1.15.8"
    76  	deploymentContainers := deployment.Spec.Template.Spec.Containers
    77  	require.Equal(t, len(deploymentContainers), 1)
    78  	require.Equal(t, deploymentContainers[0].Image, expectedContainerImage)
    79  }
    80  
    81  // An example of how to verify required values for a helm chart.
    82  func TestHelmBasicExampleTemplateRequiredTemplateArgs(t *testing.T) {
    83  	t.Parallel()
    84  
    85  	// Path to the helm chart we will test
    86  	helmChartPath, err := filepath.Abs("../examples/helm-basic-example")
    87  	releaseName := "helm-basic"
    88  	require.NoError(t, err)
    89  
    90  	// Since we aren't deploying any resources, there is no need to setup kubectl authentication, helm home, or
    91  	// namespaces
    92  
    93  	// Here, we use a table driven test to iterate through all the required values as subtests. You can learn more about
    94  	// go subtests here: https://blog.golang.org/subtests
    95  	// The struct captures the inputs that we will pass to helm template and a human friendly name so we can identify it
    96  	// in the test output. In this case, each test case will be a complete values input except for one of the required
    97  	// values missing, to test that neglecting a required value will cause the template rendering to fail.
    98  	testCases := []struct {
    99  		name   string
   100  		values map[string]string
   101  	}{
   102  		{
   103  			"MissingContainerImageRepo",
   104  			map[string]string{"containerImageTag": "1.15.8"},
   105  		},
   106  		{
   107  			"MissingContainerImageTag",
   108  			map[string]string{"containerImageRepo": "nginx"},
   109  		},
   110  	}
   111  
   112  	// Now we iterate over each test case and spawn a sub test
   113  	for _, testCase := range testCases {
   114  		// Here, we capture the range variable and force it into the scope of this block. If we don't do this, when the
   115  		// subtest switches contexts (because of t.Parallel), the testCase value will have been updated by the for loop
   116  		// and will be the next testCase!
   117  		testCase := testCase
   118  
   119  		// The actual sub test spawning. We name the sub test using the human friendly name. Note that we name the sub
   120  		// test T struct to subT to make it clear which T struct corresponds to which test. However, in most cases you
   121  		// will not reference the main test T so you can name it the same.
   122  		t.Run(testCase.name, func(subT *testing.T) {
   123  			subT.Parallel()
   124  
   125  			// Now we try rendering the template, but verify we get an error
   126  			options := &helm.Options{SetValues: testCase.values}
   127  			_, err := helm.RenderTemplateE(t, options, helmChartPath, releaseName, []string{})
   128  			require.Error(t, err)
   129  		})
   130  	}
   131  }