...

Source file src/github.com/linkerd/linkerd2/cli/cmd/inject_test.go

Documentation: github.com/linkerd/linkerd2/cli/cmd

     1  package cmd
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"fmt"
     7  	"io"
     8  	"log"
     9  	"os"
    10  	"path/filepath"
    11  	"testing"
    12  
    13  	"github.com/linkerd/linkerd2/pkg/charts/linkerd2"
    14  	"github.com/linkerd/linkerd2/pkg/cmd"
    15  	"github.com/linkerd/linkerd2/pkg/k8s"
    16  	"github.com/linkerd/linkerd2/testutil"
    17  )
    18  
    19  type testCase struct {
    20  	inputFileName          string
    21  	goldenFileName         string
    22  	reportFileName         string
    23  	injectProxy            bool
    24  	testInjectConfig       *linkerd2.Values
    25  	enableDebugSidecarFlag bool
    26  }
    27  
    28  func mkFilename(filename string, verbose bool) string {
    29  	if verbose {
    30  		return fmt.Sprintf("%s.verbose", filename)
    31  	}
    32  	return filename
    33  }
    34  
    35  func testUninjectAndInject(t *testing.T, tc testCase) {
    36  	t.Helper()
    37  
    38  	file, err := os.Open("testdata/" + tc.inputFileName)
    39  	if err != nil {
    40  		t.Errorf("error opening test input file: %v\n", err)
    41  	}
    42  
    43  	read := bufio.NewReader(file)
    44  
    45  	output := new(bytes.Buffer)
    46  	report := new(bytes.Buffer)
    47  	transformer := &resourceTransformerInject{
    48  		injectProxy:         tc.injectProxy,
    49  		values:              tc.testInjectConfig,
    50  		overrideAnnotations: getOverrideAnnotations(tc.testInjectConfig, defaultConfig()),
    51  		enableDebugSidecar:  tc.enableDebugSidecarFlag,
    52  		allowNsInject:       true,
    53  	}
    54  
    55  	if exitCode := uninjectAndInject([]io.Reader{read}, report, output, transformer, "yaml"); exitCode != 0 {
    56  		t.Errorf("Unexpected error injecting YAML: %v", report)
    57  	}
    58  	if err := testDataDiffer.DiffTestYAML(tc.goldenFileName, output.String()); err != nil {
    59  		t.Error(err)
    60  	}
    61  
    62  	reportFileName := mkFilename(tc.reportFileName, verbose)
    63  	testDataDiffer.DiffTestdata(t, reportFileName, report.String())
    64  }
    65  
    66  func defaultConfig() *linkerd2.Values {
    67  	defaultConfig, err := testInstallValues()
    68  	if err != nil {
    69  		log.Fatalf("Unexpected error: %v", err)
    70  	}
    71  	defaultConfig.LinkerdVersion = "test-inject-control-plane-version"
    72  	defaultConfig.Proxy.Image.Version = "test-inject-proxy-version"
    73  	defaultConfig.DebugContainer.Image.Version = "test-inject-debug-version"
    74  
    75  	return defaultConfig
    76  }
    77  
    78  func TestUninjectAndInject(t *testing.T) {
    79  	defaultValues := defaultConfig()
    80  
    81  	overrideConfig := defaultConfig()
    82  	overrideConfig.Proxy.Image.Version = "override"
    83  
    84  	proxyResourceConfig := defaultConfig()
    85  	proxyResourceConfig.Proxy.Resources = &linkerd2.Resources{
    86  		CPU: linkerd2.Constraints{
    87  			Request: "110m",
    88  			Limit:   "160m",
    89  		},
    90  		Memory: linkerd2.Constraints{
    91  			Request: "100Mi",
    92  			Limit:   "150Mi",
    93  		},
    94  	}
    95  
    96  	cniEnabledConfig := defaultConfig()
    97  	cniEnabledConfig.CNIEnabled = true
    98  
    99  	opaquePortsConfig := defaultConfig()
   100  	opaquePortsConfig.Proxy.OpaquePorts = "3000,5000-6000,mysql"
   101  
   102  	ingressConfig := defaultConfig()
   103  	ingressConfig.Proxy.IsIngress = true
   104  
   105  	proxyIgnorePortsConfig := defaultConfig()
   106  	proxyIgnorePortsConfig.ProxyInit.IgnoreInboundPorts = "22,8100-8102"
   107  	proxyIgnorePortsConfig.ProxyInit.IgnoreOutboundPorts = "5432"
   108  
   109  	testCases := []testCase{
   110  		{
   111  			inputFileName:    "inject_emojivoto_deployment.input.yml",
   112  			goldenFileName:   "inject_emojivoto_deployment.golden.yml",
   113  			reportFileName:   "inject_emojivoto_deployment.report",
   114  			injectProxy:      true,
   115  			testInjectConfig: defaultValues,
   116  		},
   117  		{
   118  			inputFileName:  "inject_emojivoto_deployment.input.yml",
   119  			goldenFileName: "inject_emojivoto_deployment_overridden_noinject.golden.yml",
   120  			reportFileName: "inject_emojivoto_deployment.report",
   121  			injectProxy:    false,
   122  			testInjectConfig: func() *linkerd2.Values {
   123  				values := defaultConfig()
   124  				values.Proxy.Ports.Admin = 1234
   125  				return values
   126  			}(),
   127  		},
   128  		{
   129  			inputFileName:  "inject_emojivoto_deployment.input.yml",
   130  			goldenFileName: "inject_emojivoto_deployment_overridden.golden.yml",
   131  			reportFileName: "inject_emojivoto_deployment.report",
   132  			injectProxy:    true,
   133  			testInjectConfig: func() *linkerd2.Values {
   134  				values := defaultConfig()
   135  				values.Proxy.Ports.Admin = 1234
   136  				return values
   137  			}(),
   138  		},
   139  		{
   140  			inputFileName:  "inject_emojivoto_deployment.input.yml",
   141  			goldenFileName: "inject_emojivoto_deployment_access_log.golden.yml",
   142  			reportFileName: "inject_emojivoto_deployment.report",
   143  			injectProxy:    true,
   144  			testInjectConfig: func() *linkerd2.Values {
   145  				values := defaultConfig()
   146  				values.Proxy.AccessLog = "apache"
   147  				return values
   148  			}(),
   149  		},
   150  		{
   151  			inputFileName:    "inject_emojivoto_list.input.yml",
   152  			goldenFileName:   "inject_emojivoto_list.golden.yml",
   153  			reportFileName:   "inject_emojivoto_list.report",
   154  			injectProxy:      true,
   155  			testInjectConfig: defaultValues,
   156  		},
   157  		{
   158  			inputFileName:    "inject_emojivoto_deployment_hostNetwork_false.input.yml",
   159  			goldenFileName:   "inject_emojivoto_deployment_hostNetwork_false.golden.yml",
   160  			reportFileName:   "inject_emojivoto_deployment_hostNetwork_false.report",
   161  			injectProxy:      true,
   162  			testInjectConfig: defaultValues,
   163  		},
   164  		{
   165  			inputFileName:    "inject_emojivoto_deployment_capabilities.input.yml",
   166  			goldenFileName:   "inject_emojivoto_deployment_capabilities.golden.yml",
   167  			reportFileName:   "inject_emojivoto_deployment.report",
   168  			injectProxy:      true,
   169  			testInjectConfig: defaultValues,
   170  		},
   171  		{
   172  			inputFileName:    "inject_emojivoto_deployment_injectDisabled.input.yml",
   173  			goldenFileName:   "inject_emojivoto_deployment_injectDisabled.input.yml",
   174  			reportFileName:   "inject_emojivoto_deployment_injectDisabled.report",
   175  			injectProxy:      true,
   176  			testInjectConfig: defaultValues,
   177  		},
   178  		{
   179  			inputFileName:    "inject_emojivoto_deployment_controller_name.input.yml",
   180  			goldenFileName:   "inject_emojivoto_deployment_controller_name.golden.yml",
   181  			reportFileName:   "inject_emojivoto_deployment_controller_name.report",
   182  			injectProxy:      true,
   183  			testInjectConfig: defaultValues,
   184  		},
   185  		{
   186  			inputFileName:    "inject_emojivoto_statefulset.input.yml",
   187  			goldenFileName:   "inject_emojivoto_statefulset.golden.yml",
   188  			reportFileName:   "inject_emojivoto_statefulset.report",
   189  			injectProxy:      true,
   190  			testInjectConfig: defaultValues,
   191  		},
   192  		{
   193  			inputFileName:    "inject_emojivoto_cronjob.input.yml",
   194  			goldenFileName:   "inject_emojivoto_cronjob.golden.yml",
   195  			reportFileName:   "inject_emojivoto_cronjob.report",
   196  			injectProxy:      false,
   197  			testInjectConfig: defaultValues,
   198  		},
   199  		{
   200  			inputFileName:    "inject_emojivoto_cronjob_nometa.input.yml",
   201  			goldenFileName:   "inject_emojivoto_cronjob_nometa.golden.yml",
   202  			reportFileName:   "inject_emojivoto_cronjob.report",
   203  			injectProxy:      false,
   204  			testInjectConfig: defaultValues,
   205  		},
   206  		{
   207  			inputFileName:    "inject_emojivoto_pod.input.yml",
   208  			goldenFileName:   "inject_emojivoto_pod.golden.yml",
   209  			reportFileName:   "inject_emojivoto_pod.report",
   210  			injectProxy:      true,
   211  			testInjectConfig: defaultValues,
   212  		},
   213  		{
   214  			inputFileName:    "inject_emojivoto_pod_with_requests.input.yml",
   215  			goldenFileName:   "inject_emojivoto_pod_with_requests.golden.yml",
   216  			reportFileName:   "inject_emojivoto_pod_with_requests.report",
   217  			injectProxy:      true,
   218  			testInjectConfig: proxyResourceConfig,
   219  		},
   220  		{
   221  			inputFileName:    "inject_emojivoto_deployment_udp.input.yml",
   222  			goldenFileName:   "inject_emojivoto_deployment_udp.golden.yml",
   223  			reportFileName:   "inject_emojivoto_deployment_udp.report",
   224  			injectProxy:      true,
   225  			testInjectConfig: defaultValues,
   226  		},
   227  		{
   228  			inputFileName:    "inject_emojivoto_already_injected.input.yml",
   229  			goldenFileName:   "inject_emojivoto_already_injected.golden.yml",
   230  			reportFileName:   "inject_emojivoto_already_injected.report",
   231  			injectProxy:      true,
   232  			testInjectConfig: defaultValues,
   233  		},
   234  		{
   235  			inputFileName:    "inject_contour.input.yml",
   236  			goldenFileName:   "inject_contour.golden.yml",
   237  			reportFileName:   "inject_contour.report",
   238  			injectProxy:      true,
   239  			testInjectConfig: defaultValues,
   240  		},
   241  		{
   242  			inputFileName:    "inject_emojivoto_deployment_empty_resources.input.yml",
   243  			goldenFileName:   "inject_emojivoto_deployment_empty_resources.golden.yml",
   244  			reportFileName:   "inject_emojivoto_deployment_empty_resources.report",
   245  			injectProxy:      true,
   246  			testInjectConfig: defaultValues,
   247  		},
   248  		{
   249  			inputFileName:    "inject_emojivoto_list_empty_resources.input.yml",
   250  			goldenFileName:   "inject_emojivoto_list_empty_resources.golden.yml",
   251  			reportFileName:   "inject_emojivoto_list_empty_resources.report",
   252  			injectProxy:      true,
   253  			testInjectConfig: defaultValues,
   254  		},
   255  		{
   256  			inputFileName:    "inject_emojivoto_deployment.input.yml",
   257  			goldenFileName:   "inject_emojivoto_deployment_no_init_container.golden.yml",
   258  			reportFileName:   "inject_emojivoto_deployment.report",
   259  			injectProxy:      true,
   260  			testInjectConfig: cniEnabledConfig,
   261  		},
   262  		{
   263  			inputFileName:    "inject_emojivoto_deployment_config_overrides.input.yml",
   264  			goldenFileName:   "inject_emojivoto_deployment_config_overrides.golden.yml",
   265  			reportFileName:   "inject_emojivoto_deployment.report",
   266  			injectProxy:      true,
   267  			testInjectConfig: overrideConfig,
   268  		},
   269  		{
   270  			inputFileName:          "inject_emojivoto_deployment.input.yml",
   271  			goldenFileName:         "inject_emojivoto_deployment_debug.golden.yml",
   272  			reportFileName:         "inject_emojivoto_deployment.report",
   273  			injectProxy:            true,
   274  			testInjectConfig:       defaultValues,
   275  			enableDebugSidecarFlag: true,
   276  		},
   277  		{
   278  			inputFileName:          "inject_tap_deployment.input.yml",
   279  			goldenFileName:         "inject_tap_deployment_debug.golden.yml",
   280  			reportFileName:         "inject_tap_deployment_debug.report",
   281  			injectProxy:            true,
   282  			testInjectConfig:       defaultValues,
   283  			enableDebugSidecarFlag: true,
   284  		},
   285  		{
   286  			inputFileName:    "inject_emojivoto_namespace_good.input.yml",
   287  			goldenFileName:   "inject_emojivoto_namespace_good.golden.yml",
   288  			reportFileName:   "inject_emojivoto_namespace_good.golden.report",
   289  			injectProxy:      false,
   290  			testInjectConfig: defaultConfig(),
   291  		},
   292  		{
   293  			inputFileName:    "inject_emojivoto_namespace_good.input.yml",
   294  			goldenFileName:   "inject_emojivoto_namespace_overidden_good.golden.yml",
   295  			reportFileName:   "inject_emojivoto_namespace_good.golden.report",
   296  			injectProxy:      false,
   297  			testInjectConfig: defaultConfig(),
   298  		},
   299  		{
   300  			inputFileName:    "inject_emojivoto_deployment.input.yml",
   301  			goldenFileName:   "inject_emojivoto_deployment_proxyignores.golden.yml",
   302  			reportFileName:   "inject_emojivoto_deployment.report",
   303  			injectProxy:      true,
   304  			testInjectConfig: proxyIgnorePortsConfig,
   305  		},
   306  		{
   307  			inputFileName:    "inject_emojivoto_pod.input.yml",
   308  			goldenFileName:   "inject_emojivoto_pod_proxyignores.golden.yml",
   309  			reportFileName:   "inject_emojivoto_pod.report",
   310  			injectProxy:      true,
   311  			testInjectConfig: proxyIgnorePortsConfig,
   312  		},
   313  		{
   314  			inputFileName:    "inject_emojivoto_deployment.input.yml",
   315  			goldenFileName:   "inject_emojivoto_deployment_opaque_ports.golden.yml",
   316  			reportFileName:   "inject_emojivoto_deployment_opaque_ports.report",
   317  			injectProxy:      true,
   318  			testInjectConfig: opaquePortsConfig,
   319  		},
   320  		{
   321  			inputFileName:    "inject_emojivoto_pod.input.yml",
   322  			goldenFileName:   "inject_emojivoto_pod_ingress.golden.yml",
   323  			reportFileName:   "inject_emojivoto_pod_ingress.report",
   324  			injectProxy:      true,
   325  			testInjectConfig: ingressConfig,
   326  		},
   327  		{
   328  			inputFileName:  "inject_emojivoto_deployment.input.yml",
   329  			goldenFileName: "inject_emojivoto_deployment_default_inbound_policy.golden.yml",
   330  			reportFileName: "inject_emojivoto_deployment_default_inbound_policy.golden.report",
   331  			injectProxy:    false,
   332  			testInjectConfig: func() *linkerd2.Values {
   333  				values := defaultConfig()
   334  				values.Proxy.DefaultInboundPolicy = k8s.AllAuthenticated
   335  				return values
   336  			}(),
   337  		},
   338  		{
   339  			inputFileName:  "inject_emojivoto_pod.input.yml",
   340  			goldenFileName: "inject_emojivoto_pod_default_inbound_policy.golden.yml",
   341  			reportFileName: "inject_emojivoto_pod_default_inbound_policy.golden.report",
   342  			injectProxy:    false,
   343  			testInjectConfig: func() *linkerd2.Values {
   344  				values := defaultConfig()
   345  				values.Proxy.DefaultInboundPolicy = k8s.AllAuthenticated
   346  				return values
   347  			}(),
   348  		},
   349  		{
   350  			inputFileName:  "inject_emojivoto_deployment.input.yml",
   351  			goldenFileName: "inject_emojivoto_deployment_native_sidecar.golden.yml",
   352  			reportFileName: "inject_emojivoto_deployment.report",
   353  			injectProxy:    true,
   354  			testInjectConfig: func() *linkerd2.Values {
   355  				values := defaultConfig()
   356  				values.Proxy.NativeSidecar = true
   357  				return values
   358  			}(),
   359  		},
   360  		{
   361  			inputFileName:  "inject_emojivoto_deployment.input.yml",
   362  			goldenFileName: "inject_emojivoto_deployment_params.golden.yml",
   363  			reportFileName: "inject_emojivoto_deployment.report",
   364  			injectProxy:    true,
   365  			testInjectConfig: func() *linkerd2.Values {
   366  				values := defaultConfig()
   367  				values.Proxy.Inbound = linkerd2.ProxyParams{
   368  					"scope": linkerd2.ProxyScopeParams{
   369  						"proto": linkerd2.ProxyProtoParams{
   370  							"appleSauce": "valueA",
   371  							"blueberry":  3.14,
   372  						},
   373  					},
   374  				}
   375  				values.Proxy.Outbound = linkerd2.ProxyParams{
   376  					"scope": linkerd2.ProxyScopeParams{
   377  						"proto": linkerd2.ProxyProtoParams{
   378  							"applesauce": "valueA",
   379  							"blueBerry":  true,
   380  						},
   381  					},
   382  				}
   383  				return values
   384  			}(),
   385  		},
   386  	}
   387  
   388  	for i, tc := range testCases {
   389  		tc := tc // pin
   390  		verbose = true
   391  		t.Run(fmt.Sprintf("%d: %s --verbose", i, tc.inputFileName), func(t *testing.T) {
   392  			testUninjectAndInject(t, tc)
   393  		})
   394  		verbose = false
   395  		t.Run(fmt.Sprintf("%d: %s", i, tc.inputFileName), func(t *testing.T) {
   396  			testUninjectAndInject(t, tc)
   397  		})
   398  	}
   399  }
   400  
   401  type injectCmd struct {
   402  	inputFileName        string
   403  	stdErrGoldenFileName string
   404  	stdOutGoldenFileName string
   405  	exitCode             int
   406  	injectProxy          bool
   407  	values               *linkerd2.Values
   408  }
   409  
   410  func testInjectCmd(t *testing.T, tc injectCmd) {
   411  	t.Helper()
   412  
   413  	testConfig := tc.values
   414  	if testConfig == nil {
   415  		var err error
   416  		testConfig, err = testInstallValues()
   417  		if err != nil {
   418  			t.Fatalf("Unexpected error: %v", err)
   419  		}
   420  	}
   421  	testConfig.Proxy.Image.Version = "testinjectversion"
   422  
   423  	errBuffer := &bytes.Buffer{}
   424  	outBuffer := &bytes.Buffer{}
   425  
   426  	in, err := os.Open(fmt.Sprintf("testdata/%s", tc.inputFileName))
   427  	if err != nil {
   428  		t.Fatalf("Unexpected error: %v", err)
   429  	}
   430  
   431  	transformer := &resourceTransformerInject{
   432  		injectProxy: tc.injectProxy,
   433  		values:      testConfig,
   434  	}
   435  	exitCode := runInjectCmd([]io.Reader{in}, errBuffer, outBuffer, transformer, "yaml")
   436  	if exitCode != tc.exitCode {
   437  		t.Fatalf("Expected exit code to be %d but got: %d", tc.exitCode, exitCode)
   438  	}
   439  	if tc.stdOutGoldenFileName != "" {
   440  		testDataDiffer.DiffTestdata(t, tc.stdOutGoldenFileName, outBuffer.String())
   441  	} else if outBuffer.Len() != 0 {
   442  		t.Fatalf("Expected no standard output, but got: %s", outBuffer)
   443  	}
   444  
   445  	stdErrGoldenFileName := mkFilename(tc.stdErrGoldenFileName, verbose)
   446  	testDataDiffer.DiffTestdata(t, stdErrGoldenFileName, errBuffer.String())
   447  }
   448  
   449  func TestRunInjectCmd(t *testing.T) {
   450  	testCases := []injectCmd{
   451  		{
   452  			inputFileName:        "inject_gettest_deployment.bad.input.yml",
   453  			stdErrGoldenFileName: "inject_gettest_deployment.bad.golden",
   454  			exitCode:             1,
   455  			injectProxy:          true,
   456  		},
   457  		{
   458  			inputFileName:        "inject_tap_deployment.input.yml",
   459  			stdErrGoldenFileName: "inject_tap_deployment.bad.golden",
   460  			exitCode:             1,
   461  			injectProxy:          false,
   462  		},
   463  		{
   464  			inputFileName:        "inject_gettest_deployment.good.input.yml",
   465  			stdOutGoldenFileName: "inject_gettest_deployment.good.golden.yml",
   466  			stdErrGoldenFileName: "inject_gettest_deployment.good.golden.stderr",
   467  			exitCode:             0,
   468  			injectProxy:          true,
   469  		},
   470  		{
   471  			inputFileName:        "inject_emojivoto_deployment_automountServiceAccountToken_false.input.yml",
   472  			stdOutGoldenFileName: "inject_emojivoto_deployment_automountServiceAccountToken_false.golden.yml",
   473  			stdErrGoldenFileName: "inject_emojivoto_deployment_automountServiceAccountToken_false.golden.stderr",
   474  			exitCode:             0,
   475  			injectProxy:          true,
   476  		},
   477  		{
   478  			inputFileName:        "inject_emojivoto_deployment_automountServiceAccountToken_false.input.yml",
   479  			stdOutGoldenFileName: "inject_emojivoto_deployment_automountServiceAccountToken_false_volumeProjection_disabled.golden.yml",
   480  			stdErrGoldenFileName: "inject_emojivoto_deployment_automountServiceAccountToken_false_volumeProjection_disabled.golden.stderr",
   481  			exitCode:             1,
   482  			injectProxy:          false,
   483  			values: func() *linkerd2.Values {
   484  				values, _ := testInstallValues()
   485  				values.Identity.ServiceAccountTokenProjection = false
   486  				return values
   487  			}(),
   488  		},
   489  		{
   490  			inputFileName:        "inject_emojivoto_istio.input.yml",
   491  			stdOutGoldenFileName: "inject_emojivoto_istio.golden.yml",
   492  			stdErrGoldenFileName: "inject_emojivoto_istio.golden.stderr",
   493  			exitCode:             1,
   494  			injectProxy:          true,
   495  		},
   496  		{
   497  			inputFileName:        "inject_emojivoto_deployment_hostNetwork_true.input.yml",
   498  			stdOutGoldenFileName: "inject_emojivoto_deployment_hostNetwork_true.golden.yml",
   499  			stdErrGoldenFileName: "inject_emojivoto_deployment_hostNetwork_true.golden.stderr",
   500  			exitCode:             1,
   501  			injectProxy:          true,
   502  		},
   503  	}
   504  
   505  	for i, tc := range testCases {
   506  		tc := tc // pin
   507  		verbose = true
   508  		t.Run(fmt.Sprintf("%d: %s --verbose", i, tc.inputFileName), func(t *testing.T) {
   509  			testInjectCmd(t, tc)
   510  		})
   511  		verbose = false
   512  		t.Run(fmt.Sprintf("%d: %s", i, tc.inputFileName), func(t *testing.T) {
   513  			testInjectCmd(t, tc)
   514  		})
   515  	}
   516  }
   517  
   518  type injectFilePath struct {
   519  	resource     string
   520  	resourceFile string
   521  	expectedFile string
   522  	stdErrFile   string
   523  }
   524  
   525  func testInjectFilePath(t *testing.T, tc injectFilePath) {
   526  	in, err := read("testdata/" + tc.resourceFile)
   527  	if err != nil {
   528  		t.Fatal("Unexpected error: ", err)
   529  	}
   530  
   531  	errBuf := &bytes.Buffer{}
   532  	actual := &bytes.Buffer{}
   533  	values, err := testInstallValues()
   534  	if err != nil {
   535  		t.Fatalf("Unexpected error: %v", err)
   536  	}
   537  	transformer := &resourceTransformerInject{
   538  		injectProxy: true,
   539  		values:      values,
   540  	}
   541  	if exitCode := runInjectCmd(in, errBuf, actual, transformer, "yaml"); exitCode != 0 {
   542  		t.Fatal("Unexpected error. Exit code from runInjectCmd: ", exitCode)
   543  	}
   544  	if err := testDataDiffer.DiffTestYAML(tc.expectedFile, actual.String()); err != nil {
   545  		t.Error(err)
   546  	}
   547  
   548  	stdErrFile := mkFilename(tc.stdErrFile, verbose)
   549  	testDataDiffer.DiffTestdata(t, stdErrFile, errBuf.String())
   550  }
   551  
   552  func testReadFromFolder(t *testing.T, resourceFolder string, expectedFolder string) {
   553  	in, err := read("testdata/" + resourceFolder)
   554  	if err != nil {
   555  		t.Fatal("Unexpected error: ", err)
   556  	}
   557  
   558  	values, err := testInstallValues()
   559  	if err != nil {
   560  		t.Fatalf("Unexpected error: %v", err)
   561  	}
   562  	errBuf := &bytes.Buffer{}
   563  	actual := &bytes.Buffer{}
   564  	transformer := &resourceTransformerInject{
   565  		injectProxy: true,
   566  		values:      values,
   567  	}
   568  	if exitCode := runInjectCmd(in, errBuf, actual, transformer, "yaml"); exitCode != 0 {
   569  		t.Fatal("Unexpected error. Exit code from runInjectCmd: ", exitCode)
   570  	}
   571  
   572  	expectedFile := filepath.Join(expectedFolder, "injected_nginx_redis.yaml")
   573  	if err := testDataDiffer.DiffTestYAML(expectedFile, actual.String()); err != nil {
   574  		t.Error(err)
   575  	}
   576  
   577  	stdErrFileName := mkFilename(filepath.Join(expectedFolder, "injected_nginx_redis.stderr"), verbose)
   578  	testDataDiffer.DiffTestdata(t, stdErrFileName, errBuf.String())
   579  }
   580  
   581  func TestInjectFilePath(t *testing.T) {
   582  	var (
   583  		resourceFolder = filepath.Join("inject-filepath", "resources")
   584  		expectedFolder = filepath.Join("inject-filepath", "expected")
   585  	)
   586  
   587  	t.Run("read from files", func(t *testing.T) {
   588  		testCases := []injectFilePath{
   589  			{
   590  				resource:     "nginx",
   591  				resourceFile: filepath.Join(resourceFolder, "nginx.yaml"),
   592  				expectedFile: filepath.Join(expectedFolder, "injected_nginx.yaml"),
   593  				stdErrFile:   filepath.Join(expectedFolder, "injected_nginx.stderr"),
   594  			},
   595  			{
   596  				resource:     "redis",
   597  				resourceFile: filepath.Join(resourceFolder, "db/redis.yaml"),
   598  				expectedFile: filepath.Join(expectedFolder, "injected_redis.yaml"),
   599  				stdErrFile:   filepath.Join(expectedFolder, "injected_redis.stderr"),
   600  			},
   601  		}
   602  
   603  		for i, testCase := range testCases {
   604  			testCase := testCase // pin
   605  			verbose = true
   606  			t.Run(fmt.Sprintf("%d %s", i, testCase.resource), func(t *testing.T) {
   607  				testInjectFilePath(t, testCase)
   608  			})
   609  			verbose = false
   610  			t.Run(fmt.Sprintf("%d %s", i, testCase.resource), func(t *testing.T) {
   611  				testInjectFilePath(t, testCase)
   612  			})
   613  		}
   614  	})
   615  
   616  	verbose = true
   617  	t.Run("read from folder --verbose", func(t *testing.T) {
   618  		testReadFromFolder(t, resourceFolder, expectedFolder)
   619  	})
   620  	verbose = false
   621  	t.Run("read from folder --verbose", func(t *testing.T) {
   622  		testReadFromFolder(t, resourceFolder, expectedFolder)
   623  	})
   624  }
   625  
   626  func TestToURL(t *testing.T) {
   627  	// if the string follows a URL pattern, true has to be returned
   628  	// if not false is returned
   629  
   630  	tests := map[string]bool{
   631  		"http://www.linkerd.io":  true,
   632  		"https://www.linkerd.io": true,
   633  		"www.linkerd.io/":        false,
   634  		"~/foo/bar.yaml":         false,
   635  		"./foo/bar.yaml":         false,
   636  		"/foo/bar/baz.yml":       false,
   637  		"../foo/bar/baz.yaml":    false,
   638  		"https//":                false,
   639  	}
   640  
   641  	for url, expectedValue := range tests {
   642  		_, ok := toURL(url)
   643  		if ok != expectedValue {
   644  			t.Errorf("Result mismatch for %s. expected %v, but got %v", url, expectedValue, ok)
   645  		}
   646  	}
   647  
   648  }
   649  
   650  func TestWalk(t *testing.T) {
   651  	// create two data files, one in the root folder and the other in a subfolder.
   652  	// walk should be able to read the content of the two data files recursively.
   653  	var (
   654  		tmpFolderRoot = "linkerd-testdata"
   655  		tmpFolderData = filepath.Join(tmpFolderRoot, "data")
   656  	)
   657  
   658  	if err := os.MkdirAll(tmpFolderData, os.ModeDir|os.ModePerm); err != nil {
   659  		t.Fatal("Unexpected error: ", err)
   660  	}
   661  	defer func() {
   662  		err := os.RemoveAll(tmpFolderRoot)
   663  		if err != nil {
   664  			t.Errorf("failed to remove temp dir %q: %v", tmpFolderRoot, err)
   665  		}
   666  	}()
   667  
   668  	var (
   669  		data  = []byte(testutil.ReadTestdata("inject_gettest_deployment.bad.input.yml"))
   670  		file1 = filepath.Join(tmpFolderRoot, "root.txt")
   671  		file2 = filepath.Join(tmpFolderData, "data.txt")
   672  	)
   673  	if err := os.WriteFile(file1, data, 0600); err != nil {
   674  		t.Fatal("Unexpected error: ", err)
   675  	}
   676  	if err := os.WriteFile(file2, data, 0600); err != nil {
   677  		t.Fatal("Unexpected error: ", err)
   678  	}
   679  
   680  	actual, err := walk(tmpFolderRoot)
   681  	if err != nil {
   682  		t.Fatal("Unexpected error: ", err)
   683  	}
   684  
   685  	for _, r := range actual {
   686  		b := make([]byte, len(data))
   687  		r.Read(b)
   688  
   689  		if string(b) != string(data) {
   690  			t.Errorf("Content mismatch. Expected %q, but got %q", data, b)
   691  		}
   692  	}
   693  }
   694  
   695  func TestProxyConfigurationAnnotations(t *testing.T) {
   696  	baseValues, err := linkerd2.NewValues()
   697  	if err != nil {
   698  		t.Fatal(err)
   699  	}
   700  	values, err := baseValues.DeepCopy()
   701  	if err != nil {
   702  		t.Fatal(err)
   703  	}
   704  	values.ProxyInit.IgnoreInboundPorts = "8500-8505"
   705  	values.ProxyInit.IgnoreOutboundPorts = "3306"
   706  	values.Proxy.Ports.Admin = 1234
   707  	values.Proxy.Ports.Control = 4191
   708  	values.Proxy.Ports.Inbound = 4144
   709  	values.Proxy.Ports.Outbound = 4141
   710  	values.Proxy.UID = 999
   711  	values.Proxy.GID = 999
   712  	values.Proxy.LogLevel = "debug"
   713  	values.Proxy.LogFormat = "cool"
   714  	values.Proxy.EnableExternalProfiles = true
   715  	values.Proxy.Resources.CPU.Request = "10m"
   716  	values.Proxy.Resources.CPU.Limit = "100m"
   717  	values.Proxy.Resources.Memory.Request = "10Mi"
   718  	values.Proxy.Resources.Memory.Limit = "50Mi"
   719  	values.Proxy.WaitBeforeExitSeconds = 10
   720  	values.Proxy.Await = false
   721  	values.Proxy.AccessLog = "apache"
   722  	values.Proxy.ShutdownGracePeriod = "60s"
   723  	values.Proxy.NativeSidecar = true
   724  
   725  	expectedOverrides := map[string]string{
   726  		k8s.ProxyIgnoreInboundPortsAnnotation:  "8500-8505",
   727  		k8s.ProxyIgnoreOutboundPortsAnnotation: "3306",
   728  		k8s.ProxyAdminPortAnnotation:           "1234",
   729  		k8s.ProxyControlPortAnnotation:         "4191",
   730  		k8s.ProxyInboundPortAnnotation:         "4144",
   731  		k8s.ProxyOutboundPortAnnotation:        "4141",
   732  		k8s.ProxyUIDAnnotation:                 "999",
   733  		k8s.ProxyGIDAnnotation:                 "999",
   734  		k8s.ProxyLogLevelAnnotation:            "debug",
   735  		k8s.ProxyLogFormatAnnotation:           "cool",
   736  
   737  		k8s.ProxyEnableExternalProfilesAnnotation: "true",
   738  		k8s.ProxyCPURequestAnnotation:             "10m",
   739  		k8s.ProxyCPULimitAnnotation:               "100m",
   740  		k8s.ProxyMemoryRequestAnnotation:          "10Mi",
   741  		k8s.ProxyMemoryLimitAnnotation:            "50Mi",
   742  		k8s.ProxyWaitBeforeExitSecondsAnnotation:  "10",
   743  		k8s.ProxyAwait:                            "disabled",
   744  		k8s.ProxyAccessLogAnnotation:              "apache",
   745  		k8s.ProxyShutdownGracePeriodAnnotation:    "60s",
   746  		k8s.ProxyEnableNativeSidecarAnnotation:    "true",
   747  	}
   748  
   749  	overrides := getOverrideAnnotations(values, baseValues)
   750  
   751  	diffOverrides(t, expectedOverrides, overrides)
   752  }
   753  
   754  func TestProxyImageAnnotations(t *testing.T) {
   755  	baseValues, err := linkerd2.NewValues()
   756  	if err != nil {
   757  		t.Fatal(err)
   758  	}
   759  	values, err := baseValues.DeepCopy()
   760  	if err != nil {
   761  		t.Fatal(err)
   762  	}
   763  	values.Proxy.Image = &linkerd2.Image{
   764  		Name:       "my.registry/linkerd/proxy",
   765  		Version:    "test-proxy-version",
   766  		PullPolicy: "Always",
   767  	}
   768  
   769  	expectedOverrides := map[string]string{
   770  		k8s.ProxyImageAnnotation:           "my.registry/linkerd/proxy",
   771  		k8s.ProxyVersionOverrideAnnotation: "test-proxy-version",
   772  		k8s.ProxyImagePullPolicyAnnotation: "Always",
   773  	}
   774  
   775  	overrides := getOverrideAnnotations(values, baseValues)
   776  
   777  	diffOverrides(t, expectedOverrides, overrides)
   778  }
   779  
   780  func TestProxyInitImageAnnotations(t *testing.T) {
   781  	baseValues, err := linkerd2.NewValues()
   782  	if err != nil {
   783  		t.Fatal(err)
   784  	}
   785  	values, err := baseValues.DeepCopy()
   786  	if err != nil {
   787  		t.Fatal(err)
   788  	}
   789  	values.ProxyInit.Image = &linkerd2.Image{
   790  		Name:    "my.registry/linkerd/proxy-init",
   791  		Version: "test-proxy-init-version",
   792  	}
   793  
   794  	expectedOverrides := map[string]string{
   795  		k8s.ProxyInitImageAnnotation:        "my.registry/linkerd/proxy-init",
   796  		k8s.ProxyInitImageVersionAnnotation: "test-proxy-init-version",
   797  	}
   798  
   799  	overrides := getOverrideAnnotations(values, baseValues)
   800  
   801  	diffOverrides(t, expectedOverrides, overrides)
   802  }
   803  
   804  func TestNoAnnotations(t *testing.T) {
   805  	baseValues, err := linkerd2.NewValues()
   806  	if err != nil {
   807  		t.Fatal(err)
   808  	}
   809  	values, err := baseValues.DeepCopy()
   810  	if err != nil {
   811  		t.Fatal(err)
   812  	}
   813  
   814  	expectedOverrides := map[string]string{}
   815  
   816  	overrides := getOverrideAnnotations(values, baseValues)
   817  
   818  	diffOverrides(t, expectedOverrides, overrides)
   819  }
   820  
   821  func TestOverwriteRegistry(t *testing.T) {
   822  	testCases := []struct {
   823  		image    string
   824  		registry string
   825  		expected string
   826  	}{
   827  		{
   828  			image:    "cr.l5d.io/linkerd/image",
   829  			registry: "my.custom.registry",
   830  			expected: "my.custom.registry/image",
   831  		},
   832  		{
   833  			image:    "cr.l5d.io/linkerd/image",
   834  			registry: "my.custom.registry/",
   835  			expected: "my.custom.registry/image",
   836  		},
   837  		{
   838  			image:    "my.custom.registry/image",
   839  			registry: "my.custom.registry",
   840  			expected: "my.custom.registry/image",
   841  		},
   842  		{
   843  			image:    "my.custom.registry/image",
   844  			registry: "cr.l5d.io/linkerd",
   845  			expected: "cr.l5d.io/linkerd/image",
   846  		},
   847  		{
   848  			image:    "",
   849  			registry: "my.custom.registry",
   850  			expected: "",
   851  		},
   852  		{
   853  			image:    "cr.l5d.io/linkerd/image",
   854  			registry: "",
   855  			expected: "image",
   856  		},
   857  		{
   858  			image:    "image",
   859  			registry: "cr.l5d.io/linkerd",
   860  			expected: "cr.l5d.io/linkerd/image",
   861  		},
   862  	}
   863  	for i, tc := range testCases {
   864  		tc := tc // pin
   865  		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
   866  			actual := cmd.RegistryOverride(tc.image, tc.registry)
   867  			if actual != tc.expected {
   868  				t.Fatalf("expected %q, but got %q", tc.expected, actual)
   869  			}
   870  		})
   871  	}
   872  }
   873  
   874  func diffOverrides(t *testing.T, expectedOverrides map[string]string, actualOverrides map[string]string) {
   875  	if len(expectedOverrides) != len(actualOverrides) {
   876  		t.Fatalf("expected annotations:\n%s\nbut received:\n%s", expectedOverrides, actualOverrides)
   877  	}
   878  	for key, expected := range expectedOverrides {
   879  		actual := actualOverrides[key]
   880  		if actual != expected {
   881  			t.Fatalf("expected annotation %q with %q, but got %q", key, expected, actual)
   882  		}
   883  	}
   884  }
   885  

View as plain text