diff --git a/acceptance/acceptance_test.go b/acceptance/acceptance_test.go index 5fa69b1d069..def7c16c6d4 100644 --- a/acceptance/acceptance_test.go +++ b/acceptance/acceptance_test.go @@ -155,16 +155,6 @@ func TestAccept(t *testing.T) { testAccept(t, InprocessMode, nil, false) } -// TestDmsAcc runs acceptance tests against local DMS, selected with go test -run. -// The in-process runner skips unrelated tool builds and old CLI downloads. -func TestDmsAcc(t *testing.T) { - if os.Getenv("DMS_LITEBOX_URL") == "" { - t.Skip("DMS_LITEBOX_URL is not set") - } - require.Empty(t, os.Getenv("CLOUD_ENV"), "Litebox must not use a cloud workspace") - testAccept(t, true, nil, true) -} - func TestInprocessMode(t *testing.T) { if InprocessMode && !Forcerun { t.Skip("Already tested by TestAccept") diff --git a/acceptance/internal/dms_litebox.go b/acceptance/internal/dms_litebox.go index 10014dd0c01..b59a172e06a 100644 --- a/acceptance/internal/dms_litebox.go +++ b/acceptance/internal/dms_litebox.go @@ -13,7 +13,6 @@ import ( "testing" "time" - "github.com/databricks/cli/libs/env" "github.com/databricks/cli/libs/testserver" "github.com/stretchr/testify/require" ) @@ -27,25 +26,19 @@ var liteboxPermissionTypes = []string{"directories", "files", "notebooks", "work // to its shared fakes, retaining request recording and mocks for other APIs. func configureDMSLitebox(t *testing.T, server *testserver.Server) { t.Helper() - endpoint := env.Get(t.Context(), "DMS_LITEBOX_URL") - if endpoint == "" { + settings := loadDMSLiteboxSettings(t) + if settings == nil { return } - dmsURL := liteboxURL(t, "DMS_LITEBOX_URL") - workspaceURL := liteboxURL(t, "DMS_LITEBOX_WORKSPACE_URL") + dmsURL := liteboxURL(t, settings.URL) - certFile, keyFile := env.Get(t.Context(), "DMS_LITEBOX_CERT"), env.Get(t.Context(), "DMS_LITEBOX_KEY") - caFile := env.Get(t.Context(), "DMS_LITEBOX_CA") - require.NotEmpty(t, certFile, "DMS_LITEBOX_CERT is required with DMS_LITEBOX_URL") - require.NotEmpty(t, keyFile, "DMS_LITEBOX_KEY is required with DMS_LITEBOX_URL") - require.NotEmpty(t, caFile, "DMS_LITEBOX_CA is required with DMS_LITEBOX_URL") - cert, err := tls.LoadX509KeyPair(certFile, keyFile) + cert, err := tls.LoadX509KeyPair(settings.Cert, settings.Key) require.NoError(t, err, "load Litebox client certificate and key") - pem, err := os.ReadFile(caFile) - require.NoError(t, err, "read DMS_LITEBOX_CA") + pem, err := os.ReadFile(settings.CA) + require.NoError(t, err, "read Litebox CA certificate") roots := x509.NewCertPool() require.True(t, roots.AppendCertsFromPEM(pem), "invalid Litebox certificate") - // Both upstreams must use LITE's test certificate for *.svc.cluster.local. + // The LITE gateway uses the service's test certificate. transport := &http.Transport{ TLSClientConfig: &tls.Config{ MinVersion: tls.VersionTLS12, @@ -64,10 +57,7 @@ func configureDMSLitebox(t *testing.T, server *testserver.Server) { }, } handler := func(r testserver.Request) any { - target := *workspaceURL - if isDMSPath(r.URL.Path) { - target = *dmsURL - } + target := *dmsURL target.Path, target.RawPath, target.RawQuery = r.URL.Path, r.URL.RawPath, r.URL.RawQuery req, err := http.NewRequestWithContext(r.Context, r.Method, target.String(), bytes.NewReader(r.Body)) if err != nil { @@ -95,16 +85,14 @@ func configureDMSLitebox(t *testing.T, server *testserver.Server) { routeLitebox(server, handler) } -func liteboxURL(t *testing.T, name string) *url.URL { +func liteboxURL(t *testing.T, endpoint string) *url.URL { t.Helper() - endpoint := env.Get(t.Context(), name) - require.NotEmpty(t, endpoint, "%s is required with DMS_LITEBOX_URL", name) u, err := url.Parse(endpoint) - require.NoError(t, err, "invalid %s", name) + require.NoError(t, err, "invalid DMS LITE gateway URL") ip := net.ParseIP(u.Hostname()) require.True(t, u.Scheme == "https" && (u.Hostname() == "localhost" || (ip != nil && ip.IsLoopback())) && u.User == nil && u.RawQuery == "" && u.Fragment == "" && (u.Path == "" || u.Path == "/"), - "%s must be an HTTPS loopback origin", name) + "DMS LITE gateway URL must be an HTTPS loopback origin") return u } diff --git a/acceptance/internal/dms_litebox_settings.go b/acceptance/internal/dms_litebox_settings.go new file mode 100644 index 00000000000..f36ada02976 --- /dev/null +++ b/acceptance/internal/dms_litebox_settings.go @@ -0,0 +1,65 @@ +package internal + +import ( + "encoding/json" + "errors" + "os" + "path/filepath" + "testing" + + "github.com/databricks/cli/libs/env" + "github.com/stretchr/testify/require" +) + +type dmsLiteboxSettings struct { + URL string `json:"url"` + Cert string `json:"cert"` + Key string `json:"key"` + CA string `json:"ca"` +} + +func loadDMSLiteboxSettings(t *testing.T) *dmsLiteboxSettings { + t.Helper() + acceptanceDir, err := os.Getwd() + require.NoError(t, err) + file := env.Get(t.Context(), "DMS_CLI_ENDPOINTS_FILE") + explicit := file != "" + if !explicit { + file = findDMSLiteboxSettings(t, filepath.Dir(acceptanceDir)) + if file == "" { + return nil + } + } + if !filepath.IsAbs(file) { + file = filepath.Join(acceptanceDir, file) + } + data, err := os.ReadFile(file) + if !explicit && errors.Is(err, os.ErrNotExist) { + return nil + } + require.NoError(t, err, "read DMS LITE settings %s", file) + var settings dmsLiteboxSettings + require.NoError(t, json.Unmarshal(data, &settings), "parse DMS LITE settings %s", file) + require.NotEmpty(t, settings.URL, "DMS LITE settings %s: url is required", file) + require.NotEmpty(t, settings.Cert, "DMS LITE settings %s: cert is required", file) + require.NotEmpty(t, settings.Key, "DMS LITE settings %s: key is required", file) + require.NotEmpty(t, settings.CA, "DMS LITE settings %s: ca is required", file) + return &settings +} + +func findDMSLiteboxSettings(t *testing.T, dir string) string { + t.Helper() + for { + file := filepath.Join(dir, "bundle", "metadata-service", "test", ".lite", "cli-endpoints.json") + _, err := os.Stat(file) + if err == nil { + return file + } + require.ErrorIs(t, err, os.ErrNotExist, "find DMS LITE settings %s", file) + parent := filepath.Dir(dir) + if parent == dir { + return "" + } + dir = parent + } +}