github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/e2e/porch_test.go (about)

     1  // Copyright 2022 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  //go:build porch
    16  
    17  package e2e
    18  
    19  import (
    20  	"bytes"
    21  	"errors"
    22  	"io/fs"
    23  	"os"
    24  	"os/exec"
    25  	"path/filepath"
    26  	"strings"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/GoogleContainerTools/kpt/pkg/test/porch"
    31  	"github.com/google/go-cmp/cmp"
    32  	"sigs.k8s.io/kustomize/kyaml/yaml"
    33  )
    34  
    35  const (
    36  	updateGoldenFiles = "UPDATE_GOLDEN_FILES"
    37  	testGitNamespace  = "test-git-namespace"
    38  )
    39  
    40  func TestPorch(t *testing.T) {
    41  	abs, err := filepath.Abs(filepath.Join(".", "testdata", "porch"))
    42  	if err != nil {
    43  		t.Fatalf("Failed to get absolute path to testdata directory: %v", err)
    44  	}
    45  	runTests(t, abs)
    46  }
    47  
    48  func runTests(t *testing.T, path string) {
    49  	gitServerURL := startGitServer(t, path)
    50  	testCases := scanTestCases(t, path)
    51  
    52  	for _, tc := range testCases {
    53  		t.Run(tc.TestCase, func(t *testing.T) {
    54  			if tc.Skip != "" {
    55  				t.Skipf("Skipping test: %s", tc.Skip)
    56  			}
    57  			repoURL := gitServerURL + "/" + strings.ReplaceAll(tc.TestCase, "/", "-")
    58  			runTestCase(t, repoURL, tc)
    59  		})
    60  	}
    61  }
    62  
    63  func runTestCase(t *testing.T, repoURL string, tc porch.TestCaseConfig) {
    64  	porch.KubectlCreateNamespace(t, tc.TestCase)
    65  	t.Cleanup(func() {
    66  		porch.KubectlDeleteNamespace(t, tc.TestCase)
    67  	})
    68  
    69  	if tc.Repository != "" {
    70  		porch.RegisterRepository(t, repoURL, tc.TestCase, tc.Repository)
    71  	}
    72  
    73  	for i := range tc.Commands {
    74  		time.Sleep(5 * time.Second)
    75  		command := &tc.Commands[i]
    76  		cmd := exec.Command("kpt", command.Args...)
    77  
    78  		var stdout, stderr bytes.Buffer
    79  		if command.Stdin != "" {
    80  			cmd.Stdin = strings.NewReader(command.Stdin)
    81  		}
    82  		cmd.Stdout = &stdout
    83  		cmd.Stderr = &stderr
    84  
    85  		t.Logf("running command %v", strings.Join(cmd.Args, " "))
    86  		err := cmd.Run()
    87  
    88  		if command.Yaml {
    89  			reorderYamlStdout(t, &stdout)
    90  		}
    91  
    92  		if os.Getenv(updateGoldenFiles) != "" {
    93  			updateCommand(command, err, stdout.String(), stderr.String())
    94  		}
    95  
    96  		if got, want := exitCode(err), command.ExitCode; got != want {
    97  			t.Errorf("unexpected exit code from 'kpt %s'; got %d, want %d", strings.Join(command.Args, " "), got, want)
    98  		}
    99  		if got, want := stdout.String(), command.Stdout; got != want {
   100  			t.Errorf("unexpected stdout content from 'kpt %s'; (-want, +got) %s", strings.Join(command.Args, " "), cmp.Diff(want, got))
   101  		}
   102  		if got, want := stderr.String(), command.Stderr; got != want {
   103  			t.Errorf("unexpected stderr content from 'kpt %s'; (-want, +got) %s", strings.Join(command.Args, " "), cmp.Diff(want, got))
   104  		}
   105  	}
   106  
   107  	if os.Getenv(updateGoldenFiles) != "" {
   108  		porch.WriteTestCaseConfig(t, &tc)
   109  	}
   110  }
   111  
   112  func reorderYamlStdout(t *testing.T, buf *bytes.Buffer) {
   113  	var data interface{}
   114  	if err := yaml.Unmarshal(buf.Bytes(), &data); err != nil {
   115  		// not yaml.
   116  		return
   117  	}
   118  
   119  	var stable bytes.Buffer
   120  	encoder := yaml.NewEncoder(&stable)
   121  	encoder.SetIndent(2)
   122  	if err := encoder.Encode(data); err != nil {
   123  		t.Fatalf("Failed to re-encode yaml output: %v", err)
   124  	}
   125  	buf.Reset()
   126  	if _, err := buf.Write(stable.Bytes()); err != nil {
   127  		t.Fatalf("Failed to update reordered yaml output: %v", err)
   128  	}
   129  }
   130  
   131  func startGitServer(t *testing.T, path string) string {
   132  	gitServerURL := "http://git-server." + testGitNamespace + ".svc.cluster.local:8080"
   133  
   134  	gitServerImage := porch.GetGitServerImageName(t)
   135  	t.Logf("Git Image: %s", gitServerImage)
   136  
   137  	configFile := filepath.Join(path, "git-server.yaml")
   138  	configBytes, err := os.ReadFile(configFile)
   139  	if err != nil {
   140  		t.Fatalf("Failed to read git server config file %q: %v", configFile, err)
   141  	}
   142  	config := string(configBytes)
   143  	config = strings.ReplaceAll(config, "GIT_SERVER_IMAGE", gitServerImage)
   144  
   145  	t.Cleanup(func() {
   146  		porch.KubectlDeleteNamespace(t, testGitNamespace)
   147  	})
   148  
   149  	porch.KubectlApply(t, config)
   150  	porch.KubectlWaitForDeployment(t, testGitNamespace, "git-server")
   151  	porch.KubectlWaitForService(t, testGitNamespace, "git-server")
   152  	porch.KubectlWaitForGitDNS(t, gitServerURL)
   153  
   154  	return gitServerURL
   155  }
   156  
   157  func scanTestCases(t *testing.T, root string) []porch.TestCaseConfig {
   158  	testCases := []porch.TestCaseConfig{}
   159  
   160  	if err := filepath.Walk(root, func(path string, info fs.FileInfo, err error) error {
   161  		if err != nil {
   162  			return err
   163  		}
   164  		if !info.IsDir() {
   165  			return nil
   166  		}
   167  		if path == root {
   168  			return nil
   169  		}
   170  
   171  		tc := porch.ReadTestCaseConfig(t, info.Name(), path)
   172  		testCases = append(testCases, tc)
   173  
   174  		return nil
   175  	}); err != nil {
   176  		t.Fatalf("Failed to scan test cases: %v", err)
   177  	}
   178  
   179  	return testCases
   180  }
   181  
   182  func updateCommand(command *porch.Command, exit error, stdout, stderr string) {
   183  	command.ExitCode = exitCode(exit)
   184  	command.Stdout = stdout
   185  	command.Stderr = stderr
   186  }
   187  
   188  func exitCode(exit error) int {
   189  	var ee *exec.ExitError
   190  	if errors.As(exit, &ee) {
   191  		return ee.ExitCode()
   192  	}
   193  	return 0
   194  }