linkerd2/cli/cmd/inject_test.go

396 lines
12 KiB
Go

package cmd
import (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"testing"
)
type injectYAML struct {
inputFileName string
goldenFileName string
reportFileName string
testInjectOptions *injectOptions
}
func testUninjectAndInject(t *testing.T, tc injectYAML) {
file, err := os.Open("testdata/" + tc.inputFileName)
if err != nil {
t.Errorf("error opening test input file: %v\n", err)
}
read := bufio.NewReader(file)
output := new(bytes.Buffer)
report := new(bytes.Buffer)
if exitCode := uninjectAndInject([]io.Reader{read}, report, output, tc.testInjectOptions); exitCode != 0 {
t.Errorf("Unexpected error injecting YAML: %v\n", report)
}
actualOutput := output.String()
expectedOutput := readOptionalTestFile(t, tc.goldenFileName)
if expectedOutput != actualOutput {
t.Errorf("Result mismatch.\nExpected: %s\nActual: %s", expectedOutput, actualOutput)
}
actualReport := report.String()
reportFileName := tc.reportFileName
if verbose {
reportFileName += ".verbose"
}
expectedReport := readOptionalTestFile(t, reportFileName)
if expectedReport != actualReport {
t.Errorf("Result mismatch.\nExpected: %s\nActual: %s", expectedReport, actualReport)
}
}
func TestUninjectAndInject(t *testing.T) {
defaultOptions := newInjectOptions()
defaultOptions.linkerdVersion = "testinjectversion"
tlsOptions := newInjectOptions()
tlsOptions.linkerdVersion = "testinjectversion"
tlsOptions.tls = "optional"
proxyRequestOptions := newInjectOptions()
proxyRequestOptions.linkerdVersion = "testinjectversion"
proxyRequestOptions.proxyCPURequest = "110m"
proxyRequestOptions.proxyMemoryRequest = "100Mi"
noInitContainerOptions := newInjectOptions()
noInitContainerOptions.linkerdVersion = "testinjectversion"
noInitContainerOptions.noInitContainer = true
testCases := []injectYAML{
{
inputFileName: "inject_emojivoto_deployment.input.yml",
goldenFileName: "inject_emojivoto_deployment.golden.yml",
reportFileName: "inject_emojivoto_deployment.report",
testInjectOptions: defaultOptions,
},
{
inputFileName: "inject_emojivoto_list.input.yml",
goldenFileName: "inject_emojivoto_list.golden.yml",
reportFileName: "inject_emojivoto_list.report",
testInjectOptions: defaultOptions,
},
{
inputFileName: "inject_emojivoto_deployment_hostNetwork_false.input.yml",
goldenFileName: "inject_emojivoto_deployment_hostNetwork_false.golden.yml",
reportFileName: "inject_emojivoto_deployment_hostNetwork_false.report",
testInjectOptions: defaultOptions,
},
{
inputFileName: "inject_emojivoto_deployment_hostNetwork_true.input.yml",
goldenFileName: "inject_emojivoto_deployment_hostNetwork_true.input.yml",
reportFileName: "inject_emojivoto_deployment_hostNetwork_true.report",
testInjectOptions: defaultOptions,
},
{
inputFileName: "inject_emojivoto_deployment_injectDisabled.input.yml",
goldenFileName: "inject_emojivoto_deployment_injectDisabled.input.yml",
reportFileName: "inject_emojivoto_deployment_injectDisabled.report",
testInjectOptions: defaultOptions,
},
{
inputFileName: "inject_emojivoto_deployment_controller_name.input.yml",
goldenFileName: "inject_emojivoto_deployment_controller_name.golden.yml",
reportFileName: "inject_emojivoto_deployment_controller_name.report",
testInjectOptions: defaultOptions,
},
{
inputFileName: "inject_emojivoto_statefulset.input.yml",
goldenFileName: "inject_emojivoto_statefulset.golden.yml",
reportFileName: "inject_emojivoto_statefulset.report",
testInjectOptions: defaultOptions,
},
{
inputFileName: "inject_emojivoto_pod.input.yml",
goldenFileName: "inject_emojivoto_pod.golden.yml",
reportFileName: "inject_emojivoto_pod.report",
testInjectOptions: defaultOptions,
},
{
inputFileName: "inject_emojivoto_pod_with_requests.input.yml",
goldenFileName: "inject_emojivoto_pod_with_requests.golden.yml",
reportFileName: "inject_emojivoto_pod_with_requests.report",
testInjectOptions: proxyRequestOptions,
},
{
inputFileName: "inject_emojivoto_deployment.input.yml",
goldenFileName: "inject_emojivoto_deployment_tls.golden.yml",
reportFileName: "inject_emojivoto_deployment.report",
testInjectOptions: tlsOptions,
},
{
inputFileName: "inject_emojivoto_pod.input.yml",
goldenFileName: "inject_emojivoto_pod_tls.golden.yml",
reportFileName: "inject_emojivoto_pod.report",
testInjectOptions: tlsOptions,
},
{
inputFileName: "inject_emojivoto_deployment_udp.input.yml",
goldenFileName: "inject_emojivoto_deployment_udp.golden.yml",
reportFileName: "inject_emojivoto_deployment_udp.report",
testInjectOptions: defaultOptions,
},
{
inputFileName: "inject_emojivoto_already_injected.input.yml",
goldenFileName: "inject_emojivoto_already_injected.golden.yml",
reportFileName: "inject_emojivoto_already_injected.report",
testInjectOptions: defaultOptions,
},
{
inputFileName: "inject_emojivoto_istio.input.yml",
goldenFileName: "inject_emojivoto_istio.input.yml",
reportFileName: "inject_emojivoto_istio.report",
testInjectOptions: defaultOptions,
},
{
inputFileName: "inject_contour.input.yml",
goldenFileName: "inject_contour.input.yml",
reportFileName: "inject_contour.report",
testInjectOptions: defaultOptions,
},
{
inputFileName: "inject_emojivoto_deployment.input.yml",
goldenFileName: "inject_emojivoto_deployment_no_init_container.golden.yml",
reportFileName: "inject_emojivoto_deployment.report",
testInjectOptions: noInitContainerOptions,
},
}
for i, tc := range testCases {
verbose = true
t.Run(fmt.Sprintf("%d: %s --verbose", i, tc.inputFileName), func(t *testing.T) {
testUninjectAndInject(t, tc)
})
verbose = false
t.Run(fmt.Sprintf("%d: %s", i, tc.inputFileName), func(t *testing.T) {
testUninjectAndInject(t, tc)
})
}
}
type injectCmd struct {
inputFileName string
stdErrGoldenFileName string
stdOutGoldenFileName string
exitCode int
}
func testInjectCmd(t *testing.T, tc injectCmd) {
testInjectOptions := newInjectOptions()
testInjectOptions.linkerdVersion = "testinjectversion"
errBuffer := &bytes.Buffer{}
outBuffer := &bytes.Buffer{}
in, err := os.Open(fmt.Sprintf("testdata/%s", tc.inputFileName))
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
exitCode := runInjectCmd([]io.Reader{in}, errBuffer, outBuffer, testInjectOptions)
if exitCode != tc.exitCode {
t.Fatalf("Expected exit code to be %d but got: %d", tc.exitCode, exitCode)
}
actualStdOutResult := outBuffer.String()
expectedStdOutResult := readOptionalTestFile(t, tc.stdOutGoldenFileName)
if expectedStdOutResult != actualStdOutResult {
t.Errorf("Result mismatch.\nExpected: %s\nActual: %s", expectedStdOutResult, actualStdOutResult)
}
actualStdErrResult := errBuffer.String()
stdErrGoldenFileName := tc.stdErrGoldenFileName
if verbose {
stdErrGoldenFileName += ".verbose"
}
expectedStdErrResult := readOptionalTestFile(t, stdErrGoldenFileName)
if expectedStdErrResult != actualStdErrResult {
t.Errorf("Result mismatch.\nExpected: %s\nActual: %s", expectedStdErrResult, actualStdErrResult)
}
}
func TestRunInjectCmd(t *testing.T) {
testCases := []injectCmd{
{
inputFileName: "inject_gettest_deployment.bad.input.yml",
stdErrGoldenFileName: "inject_gettest_deployment.bad.golden",
exitCode: 1,
},
{
inputFileName: "inject_gettest_deployment.good.input.yml",
stdOutGoldenFileName: "inject_gettest_deployment.good.golden.yml",
stdErrGoldenFileName: "inject_gettest_deployment.good.golden.stderr",
exitCode: 0,
},
}
for i, tc := range testCases {
verbose = true
t.Run(fmt.Sprintf("%d: %s --verbose", i, tc.inputFileName), func(t *testing.T) {
testInjectCmd(t, tc)
})
verbose = false
t.Run(fmt.Sprintf("%d: %s", i, tc.inputFileName), func(t *testing.T) {
testInjectCmd(t, tc)
})
}
}
type injectFilePath struct {
resource string
resourceFile string
expectedFile string
stdErrFile string
}
func testInjectFilePath(t *testing.T, tc injectFilePath) {
in, err := read(tc.resourceFile)
if err != nil {
t.Fatal("Unexpected error: ", err)
}
errBuf := &bytes.Buffer{}
actual := &bytes.Buffer{}
if exitCode := runInjectCmd(in, errBuf, actual, newInjectOptions()); exitCode != 0 {
t.Fatal("Unexpected error. Exit code from runInjectCmd: ", exitCode)
}
expected := readOptionalTestFile(t, tc.expectedFile)
if expected != actual.String() {
t.Errorf("Result mismatch.\nExpected: %s\nActual: %s", expected, actual.String())
}
stdErrFile := tc.stdErrFile
if verbose {
stdErrFile += ".verbose"
}
stdErr := readOptionalTestFile(t, stdErrFile)
if stdErr != errBuf.String() {
t.Errorf("Result mismatch.\nExpected: %s\nActual: %s", stdErr, errBuf.String())
}
}
func testReadFromFolder(t *testing.T, resourceFolder string, expectedFolder string) {
in, err := read(resourceFolder)
if err != nil {
t.Fatal("Unexpected error: ", err)
}
errBuf := &bytes.Buffer{}
actual := &bytes.Buffer{}
if exitCode := runInjectCmd(in, errBuf, actual, newInjectOptions()); exitCode != 0 {
t.Fatal("Unexpected error. Exit code from runInjectCmd: ", exitCode)
}
expected := readOptionalTestFile(t, filepath.Join(expectedFolder, "injected_nginx_redis.yaml"))
if expected != actual.String() {
t.Errorf("Result mismatch.\nExpected: %s\nActual: %s", expected, actual.String())
}
stdErrFileName := filepath.Join(expectedFolder, "injected_nginx_redis.stderr")
if verbose {
stdErrFileName += ".verbose"
}
stdErr := readOptionalTestFile(t, stdErrFileName)
if stdErr != errBuf.String() {
t.Errorf("Result mismatch.\nExpected: %s\nActual: %s", stdErr, errBuf.String())
}
}
func TestInjectFilePath(t *testing.T) {
var (
resourceFolder = filepath.Join("testdata", "inject-filepath", "resources")
expectedFolder = filepath.Join("inject-filepath", "expected")
)
t.Run("read from files", func(t *testing.T) {
testCases := []injectFilePath{
{
resource: "nginx",
resourceFile: filepath.Join(resourceFolder, "nginx.yaml"),
expectedFile: filepath.Join(expectedFolder, "injected_nginx.yaml"),
stdErrFile: filepath.Join(expectedFolder, "injected_nginx.stderr"),
},
{
resource: "redis",
resourceFile: filepath.Join(resourceFolder, "db/redis.yaml"),
expectedFile: filepath.Join(expectedFolder, "injected_redis.yaml"),
stdErrFile: filepath.Join(expectedFolder, "injected_redis.stderr"),
},
}
for i, testCase := range testCases {
verbose = true
t.Run(fmt.Sprintf("%d %s", i, testCase.resource), func(t *testing.T) {
testInjectFilePath(t, testCase)
})
verbose = false
t.Run(fmt.Sprintf("%d %s", i, testCase.resource), func(t *testing.T) {
testInjectFilePath(t, testCase)
})
}
})
verbose = true
t.Run("read from folder --verbose", func(t *testing.T) {
testReadFromFolder(t, resourceFolder, expectedFolder)
})
verbose = false
t.Run("read from folder --verbose", func(t *testing.T) {
testReadFromFolder(t, resourceFolder, expectedFolder)
})
}
func TestWalk(t *testing.T) {
// create two data files, one in the root folder and the other in a subfolder.
// walk should be able to read the content of the two data files recursively.
var (
tmpFolderRoot = "linkerd-testdata"
tmpFolderData = filepath.Join(tmpFolderRoot, "data")
)
if err := os.MkdirAll(tmpFolderData, os.ModeDir|os.ModePerm); err != nil {
t.Fatal("Unexpected error: ", err)
}
defer os.RemoveAll(tmpFolderRoot)
var (
data = []byte(readOptionalTestFile(t, "inject_gettest_deployment.bad.input.yml"))
file1 = filepath.Join(tmpFolderRoot, "root.txt")
file2 = filepath.Join(tmpFolderData, "data.txt")
)
if err := ioutil.WriteFile(file1, data, 0666); err != nil {
t.Fatal("Unexpected error: ", err)
}
if err := ioutil.WriteFile(file2, data, 0666); err != nil {
t.Fatal("Unexpected error: ", err)
}
actual, err := walk(tmpFolderRoot)
if err != nil {
t.Fatal("Unexpected error: ", err)
}
for _, r := range actual {
b := make([]byte, len(data))
r.Read(b)
if string(b) != string(data) {
t.Errorf("Content mismatch. Expected %q, but got %q", data, b)
}
}
}