2024-12-13 14:38:58 +00:00
|
|
|
package sync_test
|
2022-09-14 15:50:29 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-10-19 14:22:55 +00:00
|
|
|
"encoding/json"
|
2022-09-14 15:50:29 +00:00
|
|
|
"fmt"
|
2022-10-19 14:22:55 +00:00
|
|
|
"io"
|
2023-06-12 11:44:00 +00:00
|
|
|
"io/fs"
|
2023-01-10 12:16:30 +00:00
|
|
|
"net/http"
|
2022-09-14 15:50:29 +00:00
|
|
|
"os"
|
|
|
|
"os/exec"
|
2023-01-10 12:16:30 +00:00
|
|
|
"path"
|
2022-09-14 15:50:29 +00:00
|
|
|
"path/filepath"
|
2022-12-12 13:31:06 +00:00
|
|
|
"strings"
|
2022-09-14 15:50:29 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2024-12-17 07:45:58 +00:00
|
|
|
"github.com/databricks/cli/integration/internal/acc"
|
2024-12-12 16:48:51 +00:00
|
|
|
"github.com/databricks/cli/internal/testcli"
|
2024-12-12 12:35:38 +00:00
|
|
|
"github.com/databricks/cli/internal/testutil"
|
2023-06-12 11:44:00 +00:00
|
|
|
"github.com/databricks/cli/libs/filer"
|
2023-05-16 16:35:39 +00:00
|
|
|
"github.com/databricks/cli/libs/sync"
|
|
|
|
"github.com/databricks/cli/libs/testfile"
|
2022-11-24 20:41:57 +00:00
|
|
|
"github.com/databricks/databricks-sdk-go"
|
2023-01-10 12:16:30 +00:00
|
|
|
"github.com/databricks/databricks-sdk-go/client"
|
2022-09-14 15:50:29 +00:00
|
|
|
"github.com/databricks/databricks-sdk-go/service/workspace"
|
|
|
|
"github.com/stretchr/testify/assert"
|
2023-01-10 12:16:30 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2022-09-14 15:50:29 +00:00
|
|
|
)
|
|
|
|
|
2023-02-20 10:33:30 +00:00
|
|
|
var (
|
|
|
|
repoUrl = "https://github.com/databricks/databricks-empty-ide-project.git"
|
2023-06-02 14:02:18 +00:00
|
|
|
repoFiles = []string{}
|
2023-02-20 10:33:30 +00:00
|
|
|
)
|
|
|
|
|
2022-10-05 11:28:53 +00:00
|
|
|
// This test needs auth env vars to run.
|
|
|
|
// Please run using the deco env test or deco env shell
|
2023-01-10 12:16:30 +00:00
|
|
|
func setupRepo(t *testing.T, wsc *databricks.WorkspaceClient, ctx context.Context) (localRoot, remoteRoot string) {
|
2022-09-14 15:50:29 +00:00
|
|
|
me, err := wsc.CurrentUser.Me(ctx)
|
2023-01-24 14:06:59 +00:00
|
|
|
require.NoError(t, err)
|
2024-12-12 12:35:38 +00:00
|
|
|
repoPath := fmt.Sprintf("/Repos/%s/%s", me.UserName, testutil.RandomName("empty-repo-sync-integration-"))
|
2022-09-14 15:50:29 +00:00
|
|
|
|
2024-10-07 13:21:05 +00:00
|
|
|
repoInfo, err := wsc.Repos.Create(ctx, workspace.CreateRepoRequest{
|
2022-09-14 15:50:29 +00:00
|
|
|
Path: repoPath,
|
|
|
|
Url: repoUrl,
|
|
|
|
Provider: "gitHub",
|
|
|
|
})
|
2023-01-24 14:06:59 +00:00
|
|
|
require.NoError(t, err)
|
2022-09-14 15:50:29 +00:00
|
|
|
|
|
|
|
t.Cleanup(func() {
|
2022-09-27 16:58:55 +00:00
|
|
|
err := wsc.Repos.DeleteByRepoId(ctx, repoInfo.Id)
|
2022-09-14 15:50:29 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
})
|
|
|
|
|
|
|
|
tempDir := t.TempDir()
|
2023-01-10 12:16:30 +00:00
|
|
|
localRoot = filepath.Join(tempDir, "empty-repo")
|
|
|
|
remoteRoot = repoPath
|
2023-02-20 10:33:30 +00:00
|
|
|
|
|
|
|
// clone public empty remote repo
|
|
|
|
cmd := exec.Command("git", "clone", repoUrl, localRoot)
|
|
|
|
err = cmd.Run()
|
|
|
|
require.NoError(t, err)
|
2023-01-10 12:16:30 +00:00
|
|
|
return localRoot, remoteRoot
|
|
|
|
}
|
2022-09-14 15:50:29 +00:00
|
|
|
|
2023-06-02 14:02:18 +00:00
|
|
|
type syncTest struct {
|
2023-01-10 12:16:30 +00:00
|
|
|
t *testing.T
|
2024-12-12 16:48:51 +00:00
|
|
|
c *testcli.Runner
|
2023-01-10 12:16:30 +00:00
|
|
|
w *databricks.WorkspaceClient
|
2023-06-12 11:44:00 +00:00
|
|
|
f filer.Filer
|
2023-01-10 12:16:30 +00:00
|
|
|
localRoot string
|
|
|
|
remoteRoot string
|
|
|
|
}
|
2022-09-14 15:50:29 +00:00
|
|
|
|
2024-12-16 11:34:37 +00:00
|
|
|
func setupSyncTest(t *testing.T, args ...string) (context.Context, *syncTest) {
|
|
|
|
ctx, wt := acc.WorkspaceTest(t)
|
2024-12-12 21:28:04 +00:00
|
|
|
w := wt.W
|
2023-06-02 14:02:18 +00:00
|
|
|
|
|
|
|
localRoot := t.TempDir()
|
2024-12-12 21:28:04 +00:00
|
|
|
remoteRoot := acc.TemporaryWorkspaceDir(wt, "sync-")
|
2023-06-12 11:44:00 +00:00
|
|
|
f, err := filer.NewWorkspaceFilesClient(w, remoteRoot)
|
|
|
|
require.NoError(t, err)
|
2023-06-02 14:02:18 +00:00
|
|
|
|
|
|
|
// Prepend common arguments.
|
|
|
|
args = append([]string{
|
|
|
|
"sync",
|
|
|
|
localRoot,
|
|
|
|
remoteRoot,
|
|
|
|
"--output",
|
|
|
|
"json",
|
|
|
|
}, args...)
|
|
|
|
|
2024-12-16 11:34:37 +00:00
|
|
|
c := testcli.NewRunner(t, ctx, args...)
|
2023-06-02 14:02:18 +00:00
|
|
|
c.RunBackground()
|
|
|
|
|
2024-12-16 11:34:37 +00:00
|
|
|
return ctx, &syncTest{
|
2023-06-02 14:02:18 +00:00
|
|
|
t: t,
|
|
|
|
c: c,
|
|
|
|
w: w,
|
2023-06-12 11:44:00 +00:00
|
|
|
f: f,
|
2023-06-02 14:02:18 +00:00
|
|
|
localRoot: localRoot,
|
|
|
|
remoteRoot: remoteRoot,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *syncTest) waitForCompletionMarker() {
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
s.t.Fatal("timed out waiting for sync to complete")
|
2024-12-12 16:48:51 +00:00
|
|
|
case line := <-s.c.StdoutLines:
|
2023-06-02 14:02:18 +00:00
|
|
|
var event sync.EventBase
|
|
|
|
err := json.Unmarshal([]byte(line), &event)
|
|
|
|
require.NoError(s.t, err)
|
|
|
|
if event.Type == sync.EventTypeComplete {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *syncTest) remoteDirContent(ctx context.Context, relativeDir string, expectedFiles []string) {
|
2023-01-10 12:16:30 +00:00
|
|
|
remoteDir := path.Join(a.remoteRoot, relativeDir)
|
|
|
|
a.c.Eventually(func() bool {
|
2023-04-21 08:30:20 +00:00
|
|
|
objects, err := a.w.Workspace.ListAll(ctx, workspace.ListWorkspaceRequest{
|
2023-01-10 12:16:30 +00:00
|
|
|
Path: remoteDir,
|
2022-09-14 15:50:29 +00:00
|
|
|
})
|
2023-01-10 12:16:30 +00:00
|
|
|
require.NoError(a.t, err)
|
|
|
|
return len(objects) == len(expectedFiles)
|
2022-10-05 11:28:53 +00:00
|
|
|
}, 30*time.Second, 5*time.Second)
|
2023-04-21 08:30:20 +00:00
|
|
|
objects, err := a.w.Workspace.ListAll(ctx, workspace.ListWorkspaceRequest{
|
2023-01-10 12:16:30 +00:00
|
|
|
Path: remoteDir,
|
2022-09-14 15:50:29 +00:00
|
|
|
})
|
2023-01-10 12:16:30 +00:00
|
|
|
require.NoError(a.t, err)
|
|
|
|
|
|
|
|
var actualFiles []string
|
2022-11-24 20:41:57 +00:00
|
|
|
for _, v := range objects {
|
2023-01-10 12:16:30 +00:00
|
|
|
actualFiles = append(actualFiles, v.Path)
|
2022-09-14 15:50:29 +00:00
|
|
|
}
|
2023-01-10 12:16:30 +00:00
|
|
|
|
|
|
|
assert.Len(a.t, actualFiles, len(expectedFiles))
|
|
|
|
for _, v := range expectedFiles {
|
|
|
|
assert.Contains(a.t, actualFiles, path.Join(a.remoteRoot, relativeDir, v))
|
2022-09-14 15:50:29 +00:00
|
|
|
}
|
2023-01-10 12:16:30 +00:00
|
|
|
}
|
|
|
|
|
2023-06-02 14:02:18 +00:00
|
|
|
func (a *syncTest) remoteFileContent(ctx context.Context, relativePath, expectedContent string) {
|
2023-01-10 12:16:30 +00:00
|
|
|
filePath := path.Join(a.remoteRoot, relativePath)
|
|
|
|
|
|
|
|
// Remove leading "/" so we can use it in the URL.
|
2025-01-07 10:49:23 +00:00
|
|
|
urlPath := "/api/2.0/workspace-files/" + strings.TrimLeft(filePath, "/")
|
2023-01-10 12:16:30 +00:00
|
|
|
|
|
|
|
apiClient, err := client.New(a.w.Config)
|
|
|
|
require.NoError(a.t, err)
|
|
|
|
|
|
|
|
var res []byte
|
|
|
|
a.c.Eventually(func() bool {
|
Bump github.com/databricks/databricks-sdk-go from 0.55.0 to 0.56.1 (#2238)
Bumps
[github.com/databricks/databricks-sdk-go](https://github.com/databricks/databricks-sdk-go)
from 0.55.0 to 0.56.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/databricks/databricks-sdk-go/releases">github.com/databricks/databricks-sdk-go's
releases</a>.</em></p>
<blockquote>
<h2>v0.56.1</h2>
<h3>Bug Fixes</h3>
<ul>
<li>Do not send query parameters when set to zero value (<a
href="https://redirect.github.com/databricks/databricks-sdk-go/pull/1136">#1136</a>).</li>
</ul>
<h2>v0.56.0</h2>
<h3>Bug Fixes</h3>
<ul>
<li>Support Query parameters for all HTTP operations (<a
href="https://redirect.github.com/databricks/databricks-sdk-go/pull/1124">#1124</a>).</li>
</ul>
<h3>Internal Changes</h3>
<ul>
<li>Add download target to MakeFile (<a
href="https://redirect.github.com/databricks/databricks-sdk-go/pull/1125">#1125</a>).</li>
<li>Delete examples/mocking module (<a
href="https://redirect.github.com/databricks/databricks-sdk-go/pull/1126">#1126</a>).</li>
<li>Scope the traversing directory in the Recursive list workspace test
(<a
href="https://redirect.github.com/databricks/databricks-sdk-go/pull/1120">#1120</a>).</li>
</ul>
<h3>API Changes:</h3>
<ul>
<li>Added <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/iam#AccessControlAPI">w.AccessControl</a>
workspace-level service.</li>
<li>Added <code>HttpRequest</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServingEndpointsAPI">w.ServingEndpoints</a>
workspace-level service.</li>
<li>Added <code>ReviewState</code>, <code>Reviews</code> and
<code>RunnerCollaborators</code> fields for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/cleanrooms#CleanRoomAssetNotebook">cleanrooms.CleanRoomAssetNotebook</a>.</li>
<li>Added <code>CleanRoomsNotebookOutput</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/jobs#RunOutput">jobs.RunOutput</a>.</li>
<li>Added <code>RunAsRepl</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/jobs#SparkJarTask">jobs.SparkJarTask</a>.</li>
<li>Added <code>Scopes</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/oauth2#UpdateCustomAppIntegration">oauth2.UpdateCustomAppIntegration</a>.</li>
<li>Added <code>Contents</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#GetOpenApiResponse">serving.GetOpenApiResponse</a>.</li>
<li>Added <code>Activated</code>, <code>ActivationUrl</code>,
<code>AuthenticationType</code>, <code>Cloud</code>,
<code>Comment</code>, <code>CreatedAt</code>, <code>CreatedBy</code>,
<code>DataRecipientGlobalMetastoreId</code>, <code>IpAccessList</code>,
<code>MetastoreId</code>, <code>Name</code>, <code>Owner</code>,
<code>PropertiesKvpairs</code>, <code>Region</code>,
<code>SharingCode</code>, <code>Tokens</code>, <code>UpdatedAt</code>
and <code>UpdatedBy</code> fields for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/sharing#RecipientInfo">sharing.RecipientInfo</a>.</li>
<li>Added <code>ExpirationTime</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/sharing#RecipientInfo">sharing.RecipientInfo</a>.</li>
<li>Added <code>Pending</code> enum value for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/cleanrooms#CleanRoomAssetStatusEnum">cleanrooms.CleanRoomAssetStatusEnum</a>.</li>
<li>Added <code>AddNodesFailed</code>,
<code>AutomaticClusterUpdate</code>, <code>AutoscalingBackoff</code> and
<code>AutoscalingFailed</code> enum values for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/compute#EventType">compute.EventType</a>.</li>
<li>Added <code>PendingWarehouse</code> enum value for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/dashboards#MessageStatus">dashboards.MessageStatus</a>.</li>
<li>Added <code>Cpu</code>, <code>GpuLarge</code>,
<code>GpuMedium</code>, <code>GpuSmall</code> and
<code>MultigpuMedium</code> enum values for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServingModelWorkloadType">serving.ServingModelWorkloadType</a>.</li>
<li>Changed <code>Update</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/sharing#RecipientsAPI">w.Recipients</a>
workspace-level service to return <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/sharing#RecipientInfo">sharing.RecipientInfo</a>.</li>
<li>Changed <code>Update</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/sharing#RecipientsAPI">w.Recipients</a>
workspace-level service return type to become non-empty.</li>
<li>Changed <code>Update</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/sharing#RecipientsAPI">w.Recipients</a>
workspace-level service to type <code>Update</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/sharing#RecipientsAPI">w.Recipients</a>
workspace-level service.</li>
<li>Changed <code>Create</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServingEndpointsAPI">w.ServingEndpoints</a>
workspace-level service with new required argument order.</li>
<li>Changed <code>GetOpenApi</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServingEndpointsAPI">w.ServingEndpoints</a>
workspace-level service return type to become non-empty.</li>
<li>Changed <code>Patch</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServingEndpointsAPI">w.ServingEndpoints</a>
workspace-level service to type <code>Patch</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServingEndpointsAPI">w.ServingEndpoints</a>
workspace-level service.</li>
<li>Changed <code>Patch</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServingEndpointsAPI">w.ServingEndpoints</a>
workspace-level service to return <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#EndpointTags">serving.EndpointTags</a>.</li>
<li>Changed <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#EndpointTagList">serving.EndpointTagList</a>
to.</li>
<li>Changed <code>CollaboratorAlias</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/cleanrooms#CleanRoomCollaborator">cleanrooms.CleanRoomCollaborator</a>
to be required.</li>
<li>Changed <code>CollaboratorAlias</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/cleanrooms#CleanRoomCollaborator">cleanrooms.CleanRoomCollaborator</a>
to be required.</li>
<li>Changed <code>Behavior</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#AiGatewayGuardrailPiiBehavior">serving.AiGatewayGuardrailPiiBehavior</a>
to no longer be required.</li>
<li>Changed <code>Behavior</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#AiGatewayGuardrailPiiBehavior">serving.AiGatewayGuardrailPiiBehavior</a>
to no longer be required.</li>
<li>Changed <code>Config</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#CreateServingEndpoint">serving.CreateServingEndpoint</a>
to no longer be required.</li>
<li>Changed <code>ProjectId</code> and <code>Region</code> fields for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#GoogleCloudVertexAiConfig">serving.GoogleCloudVertexAiConfig</a>
to be required.</li>
<li>Changed <code>ProjectId</code> and <code>Region</code> fields for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#GoogleCloudVertexAiConfig">serving.GoogleCloudVertexAiConfig</a>
to be required.</li>
<li>Changed <code>WorkloadType</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServedEntityInput">serving.ServedEntityInput</a>
to type <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServingModelWorkloadType">serving.ServingModelWorkloadType</a>.</li>
<li>Changed <code>WorkloadType</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServedEntityOutput">serving.ServedEntityOutput</a>
to type <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServingModelWorkloadType">serving.ServingModelWorkloadType</a>.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/databricks/databricks-sdk-go/blob/main/CHANGELOG.md">github.com/databricks/databricks-sdk-go's
changelog</a>.</em></p>
<blockquote>
<h2>[Release] Release v0.56.1</h2>
<h3>Bug Fixes</h3>
<ul>
<li>Do not send query parameters when set to zero value (<a
href="https://redirect.github.com/databricks/databricks-sdk-go/pull/1136">#1136</a>).</li>
</ul>
<h2>[Release] Release v0.56.0</h2>
<h3>Bug Fixes</h3>
<ul>
<li>Support Query parameters for all HTTP operations (<a
href="https://redirect.github.com/databricks/databricks-sdk-go/pull/1124">#1124</a>).</li>
</ul>
<h3>Internal Changes</h3>
<ul>
<li>Add download target to MakeFile (<a
href="https://redirect.github.com/databricks/databricks-sdk-go/pull/1125">#1125</a>).</li>
<li>Delete examples/mocking module (<a
href="https://redirect.github.com/databricks/databricks-sdk-go/pull/1126">#1126</a>).</li>
<li>Scope the traversing directory in the Recursive list workspace test
(<a
href="https://redirect.github.com/databricks/databricks-sdk-go/pull/1120">#1120</a>).</li>
</ul>
<h3>API Changes:</h3>
<ul>
<li>Added <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/iam#AccessControlAPI">w.AccessControl</a>
workspace-level service.</li>
<li>Added <code>HttpRequest</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServingEndpointsAPI">w.ServingEndpoints</a>
workspace-level service.</li>
<li>Added <code>ReviewState</code>, <code>Reviews</code> and
<code>RunnerCollaborators</code> fields for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/cleanrooms#CleanRoomAssetNotebook">cleanrooms.CleanRoomAssetNotebook</a>.</li>
<li>Added <code>CleanRoomsNotebookOutput</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/jobs#RunOutput">jobs.RunOutput</a>.</li>
<li>Added <code>RunAsRepl</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/jobs#SparkJarTask">jobs.SparkJarTask</a>.</li>
<li>Added <code>Scopes</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/oauth2#UpdateCustomAppIntegration">oauth2.UpdateCustomAppIntegration</a>.</li>
<li>Added <code>Contents</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#GetOpenApiResponse">serving.GetOpenApiResponse</a>.</li>
<li>Added <code>Activated</code>, <code>ActivationUrl</code>,
<code>AuthenticationType</code>, <code>Cloud</code>,
<code>Comment</code>, <code>CreatedAt</code>, <code>CreatedBy</code>,
<code>DataRecipientGlobalMetastoreId</code>, <code>IpAccessList</code>,
<code>MetastoreId</code>, <code>Name</code>, <code>Owner</code>,
<code>PropertiesKvpairs</code>, <code>Region</code>,
<code>SharingCode</code>, <code>Tokens</code>, <code>UpdatedAt</code>
and <code>UpdatedBy</code> fields for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/sharing#RecipientInfo">sharing.RecipientInfo</a>.</li>
<li>Added <code>ExpirationTime</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/sharing#RecipientInfo">sharing.RecipientInfo</a>.</li>
<li>Added <code>Pending</code> enum value for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/cleanrooms#CleanRoomAssetStatusEnum">cleanrooms.CleanRoomAssetStatusEnum</a>.</li>
<li>Added <code>AddNodesFailed</code>,
<code>AutomaticClusterUpdate</code>, <code>AutoscalingBackoff</code> and
<code>AutoscalingFailed</code> enum values for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/compute#EventType">compute.EventType</a>.</li>
<li>Added <code>PendingWarehouse</code> enum value for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/dashboards#MessageStatus">dashboards.MessageStatus</a>.</li>
<li>Added <code>Cpu</code>, <code>GpuLarge</code>,
<code>GpuMedium</code>, <code>GpuSmall</code> and
<code>MultigpuMedium</code> enum values for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServingModelWorkloadType">serving.ServingModelWorkloadType</a>.</li>
<li>Changed <code>Update</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/sharing#RecipientsAPI">w.Recipients</a>
workspace-level service to return <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/sharing#RecipientInfo">sharing.RecipientInfo</a>.</li>
<li>Changed <code>Update</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/sharing#RecipientsAPI">w.Recipients</a>
workspace-level service return type to become non-empty.</li>
<li>Changed <code>Update</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/sharing#RecipientsAPI">w.Recipients</a>
workspace-level service to type <code>Update</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/sharing#RecipientsAPI">w.Recipients</a>
workspace-level service.</li>
<li>Changed <code>Create</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServingEndpointsAPI">w.ServingEndpoints</a>
workspace-level service with new required argument order.</li>
<li>Changed <code>GetOpenApi</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServingEndpointsAPI">w.ServingEndpoints</a>
workspace-level service return type to become non-empty.</li>
<li>Changed <code>Patch</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServingEndpointsAPI">w.ServingEndpoints</a>
workspace-level service to type <code>Patch</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServingEndpointsAPI">w.ServingEndpoints</a>
workspace-level service.</li>
<li>Changed <code>Patch</code> method for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#ServingEndpointsAPI">w.ServingEndpoints</a>
workspace-level service to return <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#EndpointTags">serving.EndpointTags</a>.</li>
<li>Changed <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#EndpointTagList">serving.EndpointTagList</a>
to.</li>
<li>Changed <code>CollaboratorAlias</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/cleanrooms#CleanRoomCollaborator">cleanrooms.CleanRoomCollaborator</a>
to be required.</li>
<li>Changed <code>CollaboratorAlias</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/cleanrooms#CleanRoomCollaborator">cleanrooms.CleanRoomCollaborator</a>
to be required.</li>
<li>Changed <code>Behavior</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#AiGatewayGuardrailPiiBehavior">serving.AiGatewayGuardrailPiiBehavior</a>
to no longer be required.</li>
<li>Changed <code>Behavior</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#AiGatewayGuardrailPiiBehavior">serving.AiGatewayGuardrailPiiBehavior</a>
to no longer be required.</li>
<li>Changed <code>Config</code> field for <a
href="https://pkg.go.dev/github.com/databricks/databricks-sdk-go/service/serving#CreateServingEndpoint">serving.CreateServingEndpoint</a>
to no longer be required.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/databricks/databricks-sdk-go/commit/bf617bb7a6f46370b94886dd674e4721b17224fd"><code>bf617bb</code></a>
[Release] Release v0.56.1 (<a
href="https://redirect.github.com/databricks/databricks-sdk-go/issues/1137">#1137</a>)</li>
<li><a
href="https://github.com/databricks/databricks-sdk-go/commit/18cebf1d5ca8889ae82f660c96fecc8bc5b73be5"><code>18cebf1</code></a>
[Fix] Do not send query parameters when set to zero value (<a
href="https://redirect.github.com/databricks/databricks-sdk-go/issues/1136">#1136</a>)</li>
<li><a
href="https://github.com/databricks/databricks-sdk-go/commit/28ff749ee2271172ceda01aaaa6e997e8c2aebd7"><code>28ff749</code></a>
[Release] Release v0.56.0 (<a
href="https://redirect.github.com/databricks/databricks-sdk-go/issues/1134">#1134</a>)</li>
<li><a
href="https://github.com/databricks/databricks-sdk-go/commit/113454080f34e4da04782895ea5d61101bf2b425"><code>1134540</code></a>
[Internal] Add download target to MakeFile (<a
href="https://redirect.github.com/databricks/databricks-sdk-go/issues/1125">#1125</a>)</li>
<li><a
href="https://github.com/databricks/databricks-sdk-go/commit/e079db96f33d53d6659b222a905da366dbab576b"><code>e079db9</code></a>
[Fix] Support Query parameters for all HTTP operations (<a
href="https://redirect.github.com/databricks/databricks-sdk-go/issues/1124">#1124</a>)</li>
<li><a
href="https://github.com/databricks/databricks-sdk-go/commit/1045fb9697db505f5fd1ca0ebe4be8b6479df981"><code>1045fb9</code></a>
[Internal] Delete examples/mocking module (<a
href="https://redirect.github.com/databricks/databricks-sdk-go/issues/1126">#1126</a>)</li>
<li><a
href="https://github.com/databricks/databricks-sdk-go/commit/914ab6b7e8e48ca6da6803c10c2d720ba496cd87"><code>914ab6b</code></a>
[Internal] Scope the traversing directory in the Recursive list
workspace tes...</li>
<li>See full diff in <a
href="https://github.com/databricks/databricks-sdk-go/compare/v0.55.0...v0.56.1">compare
view</a></li>
</ul>
</details>
<br />
<details>
<summary>Most Recent Ignore Conditions Applied to This Pull
Request</summary>
| Dependency Name | Ignore Conditions |
| --- | --- |
| github.com/databricks/databricks-sdk-go | [>= 0.28.a, < 0.29] |
</details>
[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/databricks/databricks-sdk-go&package-manager=go_modules&previous-version=0.55.0&new-version=0.56.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Andrew Nester <andrew.nester@databricks.com>
2025-01-27 13:11:07 +00:00
|
|
|
err = apiClient.Do(ctx, http.MethodGet, urlPath, nil, nil, nil, &res)
|
2023-01-10 12:16:30 +00:00
|
|
|
require.NoError(a.t, err)
|
|
|
|
actualContent := string(res)
|
|
|
|
return actualContent == expectedContent
|
2022-10-05 11:28:53 +00:00
|
|
|
}, 30*time.Second, 5*time.Second)
|
2022-09-14 15:50:29 +00:00
|
|
|
}
|
2022-10-19 14:22:55 +00:00
|
|
|
|
2023-06-12 11:44:00 +00:00
|
|
|
func (a *syncTest) remoteNotExist(ctx context.Context, relativePath string) {
|
|
|
|
_, err := a.f.Stat(ctx, relativePath)
|
|
|
|
require.ErrorIs(a.t, err, fs.ErrNotExist)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *syncTest) remoteExists(ctx context.Context, relativePath string) {
|
|
|
|
_, err := a.f.Stat(ctx, relativePath)
|
|
|
|
require.NoError(a.t, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *syncTest) touchFile(ctx context.Context, path string) {
|
|
|
|
err := a.f.Write(ctx, path, strings.NewReader("contents"), filer.CreateParentDirectories)
|
|
|
|
require.NoError(a.t, err)
|
|
|
|
}
|
|
|
|
|
2023-06-02 14:02:18 +00:00
|
|
|
func (a *syncTest) objectType(ctx context.Context, relativePath, expected string) {
|
2023-01-10 12:16:30 +00:00
|
|
|
path := path.Join(a.remoteRoot, relativePath)
|
|
|
|
|
|
|
|
a.c.Eventually(func() bool {
|
|
|
|
metadata, err := a.w.Workspace.GetStatusByPath(ctx, path)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return metadata.ObjectType.String() == expected
|
|
|
|
}, 30*time.Second, 5*time.Second)
|
|
|
|
}
|
|
|
|
|
2023-06-02 14:02:18 +00:00
|
|
|
func (a *syncTest) language(ctx context.Context, relativePath, expected string) {
|
2023-01-10 12:16:30 +00:00
|
|
|
path := path.Join(a.remoteRoot, relativePath)
|
|
|
|
|
|
|
|
a.c.Eventually(func() bool {
|
|
|
|
metadata, err := a.w.Workspace.GetStatusByPath(ctx, path)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return metadata.Language.String() == expected
|
|
|
|
}, 30*time.Second, 5*time.Second)
|
|
|
|
}
|
|
|
|
|
2023-06-02 14:02:18 +00:00
|
|
|
func (a *syncTest) snapshotContains(files []string) {
|
2023-01-10 12:16:30 +00:00
|
|
|
snapshotPath := filepath.Join(a.localRoot, ".databricks/sync-snapshots", sync.GetFileName(a.w.Config.Host, a.remoteRoot))
|
|
|
|
assert.FileExists(a.t, snapshotPath)
|
2022-10-19 14:22:55 +00:00
|
|
|
|
|
|
|
var s *sync.Snapshot
|
|
|
|
f, err := os.Open(snapshotPath)
|
2023-01-10 12:16:30 +00:00
|
|
|
assert.NoError(a.t, err)
|
2022-10-19 14:22:55 +00:00
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
bytes, err := io.ReadAll(f)
|
2023-01-10 12:16:30 +00:00
|
|
|
assert.NoError(a.t, err)
|
2022-10-19 14:22:55 +00:00
|
|
|
err = json.Unmarshal(bytes, &s)
|
2023-01-10 12:16:30 +00:00
|
|
|
assert.NoError(a.t, err)
|
2022-10-19 14:22:55 +00:00
|
|
|
|
2023-01-10 12:16:30 +00:00
|
|
|
assert.Equal(a.t, s.Host, a.w.Config.Host)
|
|
|
|
assert.Equal(a.t, s.RemotePath, a.remoteRoot)
|
|
|
|
for _, filePath := range files {
|
2023-10-03 13:47:46 +00:00
|
|
|
_, ok := s.LastModifiedTimes[filePath]
|
2025-01-02 11:03:41 +00:00
|
|
|
assert.True(a.t, ok, "%s not in snapshot file: %v", filePath, s.LastModifiedTimes)
|
2022-10-19 14:22:55 +00:00
|
|
|
}
|
2023-10-03 13:47:46 +00:00
|
|
|
assert.Equal(a.t, len(files), len(s.LastModifiedTimes))
|
2022-10-19 14:22:55 +00:00
|
|
|
}
|
|
|
|
|
2024-12-13 14:47:50 +00:00
|
|
|
func TestSyncFullFileSync(t *testing.T) {
|
2024-12-16 11:34:37 +00:00
|
|
|
ctx, assertSync := setupSyncTest(t, "--full", "--watch")
|
2022-10-19 14:22:55 +00:00
|
|
|
|
2023-02-20 10:33:30 +00:00
|
|
|
// .gitignore is created by the sync process to enforce .databricks is not synced
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore"))
|
2022-10-19 14:22:55 +00:00
|
|
|
|
2023-01-10 12:16:30 +00:00
|
|
|
// New file
|
2023-06-02 14:02:18 +00:00
|
|
|
localFilePath := filepath.Join(assertSync.localRoot, "foo.txt")
|
2023-01-10 12:16:30 +00:00
|
|
|
f := testfile.CreateFile(t, localFilePath)
|
|
|
|
defer f.Close(t)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, "foo.txt", ".gitignore"))
|
2023-01-10 12:16:30 +00:00
|
|
|
assertSync.remoteFileContent(ctx, "foo.txt", "")
|
|
|
|
|
|
|
|
// Write to file
|
|
|
|
f.Overwrite(t, `{"statement": "Mi Gente"}`)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-01-10 12:16:30 +00:00
|
|
|
assertSync.remoteFileContent(ctx, "foo.txt", `{"statement": "Mi Gente"}`)
|
|
|
|
|
|
|
|
// Write again
|
|
|
|
f.Overwrite(t, `{"statement": "Young Dumb & Broke"}`)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-01-10 12:16:30 +00:00
|
|
|
assertSync.remoteFileContent(ctx, "foo.txt", `{"statement": "Young Dumb & Broke"}`)
|
|
|
|
|
|
|
|
// delete
|
|
|
|
f.Remove(t)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore"))
|
2023-01-10 12:16:30 +00:00
|
|
|
}
|
|
|
|
|
2024-12-13 14:47:50 +00:00
|
|
|
func TestSyncIncrementalFileSync(t *testing.T) {
|
2024-12-16 11:34:37 +00:00
|
|
|
ctx, assertSync := setupSyncTest(t, "--watch")
|
2022-10-19 14:22:55 +00:00
|
|
|
|
2023-02-20 10:33:30 +00:00
|
|
|
// .gitignore is created by the sync process to enforce .databricks is not synced
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore"))
|
2023-01-10 12:16:30 +00:00
|
|
|
|
|
|
|
// New file
|
2023-06-02 14:02:18 +00:00
|
|
|
localFilePath := filepath.Join(assertSync.localRoot, "foo.txt")
|
2023-01-10 12:16:30 +00:00
|
|
|
f := testfile.CreateFile(t, localFilePath)
|
|
|
|
defer f.Close(t)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, "foo.txt", ".gitignore"))
|
2023-01-10 12:16:30 +00:00
|
|
|
assertSync.remoteFileContent(ctx, "foo.txt", "")
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.snapshotContains(append(repoFiles, "foo.txt", ".gitignore"))
|
2023-01-10 12:16:30 +00:00
|
|
|
|
|
|
|
// Write to file
|
|
|
|
f.Overwrite(t, `{"statement": "Mi Gente"}`)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-01-10 12:16:30 +00:00
|
|
|
assertSync.remoteFileContent(ctx, "foo.txt", `{"statement": "Mi Gente"}`)
|
|
|
|
|
|
|
|
// Write again
|
|
|
|
f.Overwrite(t, `{"statement": "Young Dumb & Broke"}`)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-01-10 12:16:30 +00:00
|
|
|
assertSync.remoteFileContent(ctx, "foo.txt", `{"statement": "Young Dumb & Broke"}`)
|
|
|
|
|
|
|
|
// delete
|
|
|
|
f.Remove(t)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore"))
|
|
|
|
assertSync.snapshotContains(append(repoFiles, ".gitignore"))
|
2023-01-10 12:16:30 +00:00
|
|
|
}
|
2022-10-19 14:22:55 +00:00
|
|
|
|
2024-12-13 14:47:50 +00:00
|
|
|
func TestSyncNestedFolderSync(t *testing.T) {
|
2024-12-16 11:34:37 +00:00
|
|
|
ctx, assertSync := setupSyncTest(t, "--watch")
|
2023-01-10 12:16:30 +00:00
|
|
|
|
2023-02-20 10:33:30 +00:00
|
|
|
// .gitignore is created by the sync process to enforce .databricks is not synced
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore"))
|
2023-01-10 12:16:30 +00:00
|
|
|
|
|
|
|
// New file
|
2023-06-02 14:02:18 +00:00
|
|
|
localFilePath := filepath.Join(assertSync.localRoot, "dir1/dir2/dir3/foo.txt")
|
2023-01-10 12:16:30 +00:00
|
|
|
err := os.MkdirAll(filepath.Dir(localFilePath), 0o755)
|
2022-10-19 14:22:55 +00:00
|
|
|
assert.NoError(t, err)
|
2023-01-10 12:16:30 +00:00
|
|
|
f := testfile.CreateFile(t, localFilePath)
|
|
|
|
defer f.Close(t)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore", "dir1"))
|
2023-01-10 12:16:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "dir1", []string{"dir2"})
|
|
|
|
assertSync.remoteDirContent(ctx, "dir1/dir2", []string{"dir3"})
|
|
|
|
assertSync.remoteDirContent(ctx, "dir1/dir2/dir3", []string{"foo.txt"})
|
2024-05-30 07:41:50 +00:00
|
|
|
assertSync.snapshotContains(append(repoFiles, ".gitignore", "dir1/dir2/dir3/foo.txt"))
|
2023-01-10 12:16:30 +00:00
|
|
|
|
|
|
|
// delete
|
|
|
|
f.Remove(t)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-06-12 11:44:00 +00:00
|
|
|
assertSync.remoteNotExist(ctx, "dir1")
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.snapshotContains(append(repoFiles, ".gitignore"))
|
2023-01-10 12:16:30 +00:00
|
|
|
}
|
|
|
|
|
2024-12-13 14:47:50 +00:00
|
|
|
func TestSyncNestedFolderDoesntFailOnNonEmptyDirectory(t *testing.T) {
|
2024-12-16 11:34:37 +00:00
|
|
|
ctx, assertSync := setupSyncTest(t, "--watch")
|
2023-06-12 11:44:00 +00:00
|
|
|
|
|
|
|
// .gitignore is created by the sync process to enforce .databricks is not synced
|
|
|
|
assertSync.waitForCompletionMarker()
|
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore"))
|
|
|
|
|
|
|
|
// New file
|
|
|
|
localFilePath := filepath.Join(assertSync.localRoot, "dir1/dir2/dir3/foo.txt")
|
|
|
|
err := os.MkdirAll(filepath.Dir(localFilePath), 0o755)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
f := testfile.CreateFile(t, localFilePath)
|
|
|
|
defer f.Close(t)
|
|
|
|
assertSync.waitForCompletionMarker()
|
|
|
|
assertSync.remoteDirContent(ctx, "dir1/dir2/dir3", []string{"foo.txt"})
|
|
|
|
|
|
|
|
// Add file to dir1 to simulate a user writing to the workspace directly.
|
|
|
|
assertSync.touchFile(ctx, "dir1/foo.txt")
|
|
|
|
|
|
|
|
// Remove original file.
|
|
|
|
f.Remove(t)
|
|
|
|
assertSync.waitForCompletionMarker()
|
|
|
|
|
|
|
|
// Sync should have removed these directories.
|
|
|
|
assertSync.remoteNotExist(ctx, "dir1/dir2/dir3")
|
|
|
|
assertSync.remoteNotExist(ctx, "dir1/dir2")
|
|
|
|
|
|
|
|
// Sync should have ignored not being able to delete dir1.
|
|
|
|
assertSync.remoteExists(ctx, "dir1/foo.txt")
|
|
|
|
assertSync.remoteExists(ctx, "dir1")
|
|
|
|
}
|
|
|
|
|
2024-12-13 14:47:50 +00:00
|
|
|
func TestSyncNestedSpacePlusAndHashAreEscapedSync(t *testing.T) {
|
2024-12-16 11:34:37 +00:00
|
|
|
ctx, assertSync := setupSyncTest(t, "--watch")
|
2023-03-15 16:25:57 +00:00
|
|
|
|
|
|
|
// .gitignore is created by the sync process to enforce .databricks is not synced
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-03-15 16:25:57 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore"))
|
|
|
|
|
|
|
|
// New file
|
2023-06-02 14:02:18 +00:00
|
|
|
localFilePath := filepath.Join(assertSync.localRoot, "dir1/a b+c/c+d e/e+f g#i.txt")
|
2023-03-15 16:25:57 +00:00
|
|
|
err := os.MkdirAll(filepath.Dir(localFilePath), 0o755)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
f := testfile.CreateFile(t, localFilePath)
|
|
|
|
defer f.Close(t)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-03-15 16:25:57 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore", "dir1"))
|
|
|
|
assertSync.remoteDirContent(ctx, "dir1", []string{"a b+c"})
|
|
|
|
assertSync.remoteDirContent(ctx, "dir1/a b+c", []string{"c+d e"})
|
|
|
|
assertSync.remoteDirContent(ctx, "dir1/a b+c/c+d e", []string{"e+f g#i.txt"})
|
2024-05-30 07:41:50 +00:00
|
|
|
assertSync.snapshotContains(append(repoFiles, ".gitignore", "dir1/a b+c/c+d e/e+f g#i.txt"))
|
2023-03-15 16:25:57 +00:00
|
|
|
|
|
|
|
// delete
|
|
|
|
f.Remove(t)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-06-12 11:44:00 +00:00
|
|
|
assertSync.remoteNotExist(ctx, "dir1/a b+c/c+d e")
|
2023-03-15 16:25:57 +00:00
|
|
|
assertSync.snapshotContains(append(repoFiles, ".gitignore"))
|
|
|
|
}
|
|
|
|
|
2023-01-10 12:16:30 +00:00
|
|
|
// This is a check for the edge case when a user does the following:
|
|
|
|
//
|
|
|
|
// 1. Add file foo/bar.txt
|
|
|
|
// 2. Delete foo/bar.txt (including the directory)
|
|
|
|
// 3. Add file foo
|
|
|
|
//
|
|
|
|
// In the above scenario sync should delete the empty folder and add foo to the remote
|
|
|
|
// file system
|
2024-12-13 14:47:50 +00:00
|
|
|
func TestSyncIncrementalFileOverwritesFolder(t *testing.T) {
|
2024-12-16 11:34:37 +00:00
|
|
|
ctx, assertSync := setupSyncTest(t, "--watch")
|
2023-01-10 12:16:30 +00:00
|
|
|
|
|
|
|
// create foo/bar.txt
|
2023-06-02 14:02:18 +00:00
|
|
|
localFilePath := filepath.Join(assertSync.localRoot, "foo/bar.txt")
|
2023-01-10 12:16:30 +00:00
|
|
|
err := os.MkdirAll(filepath.Dir(localFilePath), 0o755)
|
2022-12-12 13:31:06 +00:00
|
|
|
assert.NoError(t, err)
|
2023-01-10 12:16:30 +00:00
|
|
|
f := testfile.CreateFile(t, localFilePath)
|
|
|
|
defer f.Close(t)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-02-20 14:41:37 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore", "foo"))
|
2023-01-10 12:16:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "foo", []string{"bar.txt"})
|
2024-05-30 07:41:50 +00:00
|
|
|
assertSync.snapshotContains(append(repoFiles, ".gitignore", "foo/bar.txt"))
|
2023-01-10 12:16:30 +00:00
|
|
|
|
|
|
|
// delete foo/bar.txt
|
|
|
|
f.Remove(t)
|
2023-06-02 14:02:18 +00:00
|
|
|
os.Remove(filepath.Join(assertSync.localRoot, "foo"))
|
|
|
|
assertSync.waitForCompletionMarker()
|
2023-06-12 11:44:00 +00:00
|
|
|
assertSync.remoteNotExist(ctx, "foo")
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.snapshotContains(append(repoFiles, ".gitignore"))
|
2023-01-10 12:16:30 +00:00
|
|
|
|
2023-06-02 14:02:18 +00:00
|
|
|
f2 := testfile.CreateFile(t, filepath.Join(assertSync.localRoot, "foo"))
|
2023-01-10 12:16:30 +00:00
|
|
|
defer f2.Close(t)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore", "foo"))
|
2023-01-10 12:16:30 +00:00
|
|
|
assertSync.objectType(ctx, "foo", "FILE")
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.snapshotContains(append(repoFiles, ".gitignore", "foo"))
|
2023-01-10 12:16:30 +00:00
|
|
|
}
|
|
|
|
|
2024-12-13 14:47:50 +00:00
|
|
|
func TestSyncIncrementalSyncPythonNotebookToFile(t *testing.T) {
|
2024-12-16 11:34:37 +00:00
|
|
|
ctx, assertSync := setupSyncTest(t, "--watch")
|
2023-01-10 12:16:30 +00:00
|
|
|
|
|
|
|
// create python notebook
|
2023-06-02 14:02:18 +00:00
|
|
|
localFilePath := filepath.Join(assertSync.localRoot, "foo.py")
|
2023-01-10 12:16:30 +00:00
|
|
|
f := testfile.CreateFile(t, localFilePath)
|
|
|
|
defer f.Close(t)
|
|
|
|
f.Overwrite(t, "# Databricks notebook source")
|
|
|
|
|
|
|
|
// notebook was uploaded properly
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore", "foo"))
|
2023-01-10 12:16:30 +00:00
|
|
|
assertSync.objectType(ctx, "foo", "NOTEBOOK")
|
|
|
|
assertSync.language(ctx, "foo", "PYTHON")
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.snapshotContains(append(repoFiles, ".gitignore", "foo.py"))
|
2023-01-10 12:16:30 +00:00
|
|
|
|
|
|
|
// convert to vanilla python file
|
|
|
|
f.Overwrite(t, "# No longer a python notebook")
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-01-10 12:16:30 +00:00
|
|
|
assertSync.objectType(ctx, "foo.py", "FILE")
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore", "foo.py"))
|
|
|
|
assertSync.snapshotContains(append(repoFiles, ".gitignore", "foo.py"))
|
2023-01-10 12:16:30 +00:00
|
|
|
|
|
|
|
// delete the vanilla python file
|
|
|
|
f.Remove(t)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore"))
|
|
|
|
assertSync.snapshotContains(append(repoFiles, ".gitignore"))
|
2023-01-10 12:16:30 +00:00
|
|
|
}
|
|
|
|
|
2024-12-13 14:47:50 +00:00
|
|
|
func TestSyncIncrementalSyncFileToPythonNotebook(t *testing.T) {
|
2024-12-16 11:34:37 +00:00
|
|
|
ctx, assertSync := setupSyncTest(t, "--watch")
|
2023-01-10 12:16:30 +00:00
|
|
|
|
|
|
|
// create vanilla python file
|
2023-06-02 14:02:18 +00:00
|
|
|
localFilePath := filepath.Join(assertSync.localRoot, "foo.py")
|
2023-01-10 12:16:30 +00:00
|
|
|
f := testfile.CreateFile(t, localFilePath)
|
|
|
|
defer f.Close(t)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-01-10 12:16:30 +00:00
|
|
|
|
|
|
|
// assert file upload
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore", "foo.py"))
|
2023-01-10 12:16:30 +00:00
|
|
|
assertSync.objectType(ctx, "foo.py", "FILE")
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.snapshotContains(append(repoFiles, ".gitignore", "foo.py"))
|
2023-01-10 12:16:30 +00:00
|
|
|
|
|
|
|
// convert to notebook
|
|
|
|
f.Overwrite(t, "# Databricks notebook source")
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-01-10 12:16:30 +00:00
|
|
|
assertSync.objectType(ctx, "foo", "NOTEBOOK")
|
|
|
|
assertSync.language(ctx, "foo", "PYTHON")
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore", "foo"))
|
|
|
|
assertSync.snapshotContains(append(repoFiles, ".gitignore", "foo.py"))
|
2023-01-10 12:16:30 +00:00
|
|
|
}
|
|
|
|
|
2024-12-13 14:47:50 +00:00
|
|
|
func TestSyncIncrementalSyncPythonNotebookDelete(t *testing.T) {
|
2024-12-16 11:34:37 +00:00
|
|
|
ctx, assertSync := setupSyncTest(t, "--watch")
|
2023-01-10 12:16:30 +00:00
|
|
|
|
|
|
|
// create python notebook
|
2023-06-02 14:02:18 +00:00
|
|
|
localFilePath := filepath.Join(assertSync.localRoot, "foo.py")
|
2023-01-10 12:16:30 +00:00
|
|
|
f := testfile.CreateFile(t, localFilePath)
|
|
|
|
defer f.Close(t)
|
|
|
|
f.Overwrite(t, "# Databricks notebook source")
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-01-10 12:16:30 +00:00
|
|
|
|
|
|
|
// notebook was uploaded properly
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore", "foo"))
|
2023-01-10 12:16:30 +00:00
|
|
|
assertSync.objectType(ctx, "foo", "NOTEBOOK")
|
|
|
|
assertSync.language(ctx, "foo", "PYTHON")
|
|
|
|
|
|
|
|
// Delete notebook
|
|
|
|
f.Remove(t)
|
2023-06-02 14:02:18 +00:00
|
|
|
assertSync.waitForCompletionMarker()
|
2023-02-20 10:33:30 +00:00
|
|
|
assertSync.remoteDirContent(ctx, "", append(repoFiles, ".gitignore"))
|
2022-10-19 14:22:55 +00:00
|
|
|
}
|
2023-02-20 13:34:48 +00:00
|
|
|
|
2024-12-13 14:47:50 +00:00
|
|
|
func TestSyncEnsureRemotePathIsUsableIfRepoDoesntExist(t *testing.T) {
|
2024-12-13 16:09:51 +00:00
|
|
|
ctx, wt := acc.WorkspaceTest(t)
|
|
|
|
wsc := wt.W
|
2023-02-20 13:34:48 +00:00
|
|
|
|
|
|
|
me, err := wsc.CurrentUser.Me(ctx)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Hypothetical repo path doesn't exist.
|
2024-12-12 12:35:38 +00:00
|
|
|
nonExistingRepoPath := fmt.Sprintf("/Repos/%s/%s", me.UserName, testutil.RandomName("doesnt-exist-"))
|
2023-07-30 07:19:49 +00:00
|
|
|
err = sync.EnsureRemotePathIsUsable(ctx, wsc, nonExistingRepoPath, nil)
|
2023-02-20 13:34:48 +00:00
|
|
|
assert.ErrorContains(t, err, " does not exist; please create it first")
|
|
|
|
|
|
|
|
// Paths nested under a hypothetical repo path should yield the same error.
|
|
|
|
nestedPath := path.Join(nonExistingRepoPath, "nested/directory")
|
2023-07-30 07:19:49 +00:00
|
|
|
err = sync.EnsureRemotePathIsUsable(ctx, wsc, nestedPath, nil)
|
2023-02-20 13:34:48 +00:00
|
|
|
assert.ErrorContains(t, err, " does not exist; please create it first")
|
|
|
|
}
|
|
|
|
|
2024-12-13 14:47:50 +00:00
|
|
|
func TestSyncEnsureRemotePathIsUsableIfRepoExists(t *testing.T) {
|
2024-12-13 16:09:51 +00:00
|
|
|
ctx, wt := acc.WorkspaceTest(t)
|
|
|
|
wsc := wt.W
|
2023-02-20 13:34:48 +00:00
|
|
|
|
|
|
|
_, remoteRepoPath := setupRepo(t, wsc, ctx)
|
|
|
|
|
|
|
|
// Repo itself is usable.
|
2023-07-30 07:19:49 +00:00
|
|
|
err := sync.EnsureRemotePathIsUsable(ctx, wsc, remoteRepoPath, nil)
|
2023-02-20 13:34:48 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
// Path nested under repo path is usable.
|
|
|
|
nestedPath := path.Join(remoteRepoPath, "nested/directory")
|
2023-07-30 07:19:49 +00:00
|
|
|
err = sync.EnsureRemotePathIsUsable(ctx, wsc, nestedPath, nil)
|
2023-02-20 13:34:48 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
// Verify that the directory has been created.
|
|
|
|
info, err := wsc.Workspace.GetStatusByPath(ctx, nestedPath)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, workspace.ObjectTypeDirectory, info.ObjectType)
|
|
|
|
}
|
|
|
|
|
2024-12-13 14:47:50 +00:00
|
|
|
func TestSyncEnsureRemotePathIsUsableInWorkspace(t *testing.T) {
|
2024-12-13 16:09:51 +00:00
|
|
|
ctx, wt := acc.WorkspaceTest(t)
|
|
|
|
wsc := wt.W
|
2023-02-20 13:34:48 +00:00
|
|
|
|
|
|
|
me, err := wsc.CurrentUser.Me(ctx)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2024-12-12 12:35:38 +00:00
|
|
|
remotePath := fmt.Sprintf("/Users/%s/%s", me.UserName, testutil.RandomName("ensure-path-exists-test-"))
|
2023-07-30 07:19:49 +00:00
|
|
|
err = sync.EnsureRemotePathIsUsable(ctx, wsc, remotePath, me)
|
2023-02-20 13:34:48 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
// Clean up directory after test.
|
|
|
|
defer func() {
|
|
|
|
err := wsc.Workspace.Delete(ctx, workspace.Delete{
|
|
|
|
Path: remotePath,
|
|
|
|
})
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Verify that the directory has been created.
|
|
|
|
info, err := wsc.Workspace.GetStatusByPath(ctx, remotePath)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, workspace.ObjectTypeDirectory, info.ObjectType)
|
|
|
|
}
|