package cmd import ( "bytes" "io" "os" "path/filepath" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/vespa-engine/vespa/client/go/internal/mock" "github.com/vespa-engine/vespa/client/go/internal/util" "github.com/vespa-engine/vespa/client/go/internal/vespa" ) func TestProdInit(t *testing.T) { pkgDir := filepath.Join(t.TempDir(), "app") createApplication(t, pkgDir, false, false) answers := []string{ // Regions "invalid input", "aws-us-west-2a,aws-eu-west-1a", // Node count: qrs "invalid input", "4", // Node resources: qrs "invalid input", "auto", // Node count: music "invalid input", "6", // Node resources: music "invalid input", "vcpu=16,memory=64Gb,disk=100Gb", } var buf bytes.Buffer buf.WriteString(strings.Join(answers, "\n") + "\n") cli, _, _ := newTestCLI(t) cli.Stdin = &buf assert.Nil(t, cli.Run("prod", "init", pkgDir)) // Verify contents deploymentPath := filepath.Join(pkgDir, "src", "main", "application", "deployment.xml") deploymentXML := readFileString(t, deploymentPath) assert.Contains(t, deploymentXML, `aws-us-west-2a`) assert.Contains(t, deploymentXML, `aws-eu-west-1a`) servicesPath := filepath.Join(pkgDir, "src", "main", "application", "services.xml") servicesXML := readFileString(t, servicesPath) containerFragment := ` ` assert.Contains(t, servicesXML, containerFragment) contentFragment := ` 2 ` assert.Contains(t, servicesXML, contentFragment) // Backups are created assert.True(t, util.PathExists(deploymentPath+".1.bak")) assert.True(t, util.PathExists(servicesPath+".1.bak")) } func readFileString(t *testing.T, filename string) string { content, err := os.ReadFile(filename) if err != nil { t.Fatal(err) } return string(content) } func createApplication(t *testing.T, pkgDir string, java bool, skipTests bool) { appDir := filepath.Join(pkgDir, "src", "main", "application") targetDir := filepath.Join(pkgDir, "target") if err := os.MkdirAll(appDir, 0755); err != nil { t.Fatal(err) } deploymentXML := ` aws-us-east-1c ` if err := os.WriteFile(filepath.Join(appDir, "deployment.xml"), []byte(deploymentXML), 0644); err != nil { t.Fatal(err) } servicesXML := ` 2 ` if err := os.WriteFile(filepath.Join(appDir, "services.xml"), []byte(servicesXML), 0644); err != nil { t.Fatal(err) } if err := os.MkdirAll(targetDir, 0755); err != nil { t.Fatal(err) } if java { if skipTests { t.Fatalf("skipTests=%t has no effect when java=%t", skipTests, java) } if err := os.WriteFile(filepath.Join(pkgDir, "pom.xml"), []byte(""), 0644); err != nil { t.Fatal(err) } } else if !skipTests { testsDir := filepath.Join(pkgDir, "src", "test", "application", "tests") testBytes, _ := io.ReadAll(strings.NewReader("{\"steps\":[{}]}")) writeTest(filepath.Join(testsDir, "system-test", "test.json"), testBytes, t) writeTest(filepath.Join(testsDir, "staging-setup", "test.json"), testBytes, t) writeTest(filepath.Join(testsDir, "staging-test", "test.json"), testBytes, t) } } func writeTest(path string, content []byte, t *testing.T) { if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(path, content, 0644); err != nil { t.Fatal(err) } } func TestProdDeploy(t *testing.T) { pkgDir := filepath.Join(t.TempDir(), "app") createApplication(t, pkgDir, false, false) prodDeploy(pkgDir, t) } func TestProdDeployWithoutTests(t *testing.T) { pkgDir := filepath.Join(t.TempDir(), "app") createApplication(t, pkgDir, false, true) prodDeploy(pkgDir, t) } func prodDeploy(pkgDir string, t *testing.T) { t.Helper() httpClient := &mock.HTTPClient{} httpClient.NextResponseString(200, `ok`) cli, stdout, _ := newTestCLI(t, "CI=true") cli.httpClient = httpClient app := vespa.ApplicationID{Tenant: "t1", Application: "a1", Instance: "i1"} assert.Nil(t, cli.Run("config", "set", "application", app.String())) assert.Nil(t, cli.Run("config", "set", "target", "cloud")) assert.Nil(t, cli.Run("auth", "api-key")) assert.Nil(t, cli.Run("auth", "cert", "--no-add")) // Zipping requires relative paths, so must let command run from pkgDir, then reset cwd for subsequent tests. if cwd, err := os.Getwd(); err != nil { t.Fatal(err) } else { defer os.Chdir(cwd) } if err := os.Chdir(pkgDir); err != nil { t.Fatal(err) } stdout.Reset() cli.Environment["VESPA_CLI_API_KEY_FILE"] = filepath.Join(cli.config.homeDir, "t1.api-key.pem") assert.Nil(t, cli.Run("prod", "deploy", "--add-cert")) assert.Contains(t, stdout.String(), "Success: Deployed") assert.Contains(t, stdout.String(), "See https://console.vespa-cloud.com/tenant/t1/application/a1/prod/deployment for deployment progress") stdout.Reset() assert.Nil(t, cli.Run("prod", "submit", "--add-cert")) // old variant also works assert.Contains(t, stdout.String(), "Success: Deployed") assert.Contains(t, stdout.String(), "See https://console.vespa-cloud.com/tenant/t1/application/a1/prod/deployment for deployment progress") } func TestProdDeployWithJava(t *testing.T) { pkgDir := filepath.Join(t.TempDir(), "app") createApplication(t, pkgDir, true, false) httpClient := &mock.HTTPClient{} httpClient.NextResponseString(200, `ok`) cli, stdout, _ := newTestCLI(t, "CI=true") cli.httpClient = httpClient assert.Nil(t, cli.Run("config", "set", "application", "t1.a1.i1")) assert.Nil(t, cli.Run("config", "set", "target", "cloud")) assert.Nil(t, cli.Run("auth", "api-key")) assert.Nil(t, cli.Run("auth", "cert", "--no-add")) // Copy an application package pre-assembled with mvn package testAppDir := filepath.Join("testdata", "applications", "withDeployment", "target") zipFile := filepath.Join(testAppDir, "application.zip") copyFile(t, filepath.Join(pkgDir, "target", "application.zip"), zipFile) testZipFile := filepath.Join(testAppDir, "application-test.zip") copyFile(t, filepath.Join(pkgDir, "target", "application-test.zip"), testZipFile) stdout.Reset() cli.Environment["VESPA_CLI_API_KEY_FILE"] = filepath.Join(cli.config.homeDir, "t1.api-key.pem") assert.Nil(t, cli.Run("prod", "deploy", pkgDir)) assert.Contains(t, stdout.String(), "Success: Deployed") assert.Contains(t, stdout.String(), "See https://console.vespa-cloud.com/tenant/t1/application/a1/prod/deployment for deployment progress") } func TestProdDeployInvalidZip(t *testing.T) { pkgDir := filepath.Join(t.TempDir(), "app") createApplication(t, pkgDir, true, false) httpClient := &mock.HTTPClient{} httpClient.NextResponseString(200, `ok`) cli, _, stderr := newTestCLI(t, "CI=true") cli.httpClient = httpClient assert.Nil(t, cli.Run("config", "set", "application", "t1.a1.i1")) assert.Nil(t, cli.Run("config", "set", "target", "cloud")) assert.Nil(t, cli.Run("auth", "api-key")) assert.Nil(t, cli.Run("auth", "cert", "--no-add")) // Copy an invalid application package containing relative file names testAppDir := filepath.Join("testdata", "applications", "withInvalidEntries", "target") zipFile := filepath.Join(testAppDir, "application.zip") copyFile(t, filepath.Join(pkgDir, "target", "application.zip"), zipFile) testZipFile := filepath.Join(testAppDir, "application-test.zip") copyFile(t, filepath.Join(pkgDir, "target", "application-test.zip"), testZipFile) assert.NotNil(t, cli.Run("prod", "deploy", pkgDir)) assert.Equal(t, "Error: found invalid path inside zip: ../../../../../../../tmp/foo\n", stderr.String()) } func copyFile(t *testing.T, dstFilename, srcFilename string) { dst, err := os.Create(dstFilename) if err != nil { t.Fatal(err) } defer dst.Close() src, err := os.Open(srcFilename) if err != nil { t.Fatal(err) } defer src.Close() if _, err := io.Copy(dst, src); err != nil { t.Fatal(err) } }