mirror of https://github.com/databricks/cli.git
Compare commits
5 Commits
49598cce63
...
3b33d8ce65
Author | SHA1 | Date |
---|---|---|
Lennart Kats | 3b33d8ce65 | |
Lennart Kats | e533dd0912 | |
Lennart Kats | 4c06a34e0d | |
Lennart Kats | b11917e559 | |
Lennart Kats | 8abae34f7c |
|
@ -29,7 +29,7 @@ func (m *upload) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
|
||||||
b.Files, err = sync.RunOnce(ctx)
|
b.Files, err = sync.RunOnce(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, fs.ErrPermission) {
|
if errors.Is(err, fs.ErrPermission) {
|
||||||
return permissions.ReportPossiblePermissionDenied(ctx, b, b.Config.Workspace.StatePath)
|
return permissions.ReportPossiblePermissionDenied(ctx, b, b.Config.Workspace.FilePath)
|
||||||
}
|
}
|
||||||
return diag.FromErr(err)
|
return diag.FromErr(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,9 @@ import (
|
||||||
"github.com/databricks/cli/bundle"
|
"github.com/databricks/cli/bundle"
|
||||||
"github.com/databricks/cli/libs/diag"
|
"github.com/databricks/cli/libs/diag"
|
||||||
"github.com/databricks/cli/libs/dyn"
|
"github.com/databricks/cli/libs/dyn"
|
||||||
"github.com/databricks/cli/libs/log"
|
|
||||||
"github.com/databricks/cli/libs/set"
|
"github.com/databricks/cli/libs/set"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CheckPermissionsFilename = "permissions.check"
|
|
||||||
|
|
||||||
type permissionDiagnostics struct{}
|
type permissionDiagnostics struct{}
|
||||||
|
|
||||||
func PermissionDiagnostics() bundle.Mutator {
|
func PermissionDiagnostics() bundle.Mutator {
|
||||||
|
@ -108,43 +105,3 @@ func isGroupOfCurrentUser(b *bundle.Bundle, groupName string) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReportPossiblePermissionDenied generates a diagnostic message when a permission denied error is encountered.
|
|
||||||
//
|
|
||||||
// Note that since the workspace API doesn't always distinguish between permission denied and path errors,
|
|
||||||
// we must treat this as a "possible permission error". See acquire.go for more about this.
|
|
||||||
func ReportPossiblePermissionDenied(ctx context.Context, b *bundle.Bundle, path string) diag.Diagnostics {
|
|
||||||
log.Errorf(ctx, "Failed to update, encountered possible permission error: %v", path)
|
|
||||||
|
|
||||||
user := b.Config.Workspace.CurrentUser.DisplayName
|
|
||||||
if user == "" {
|
|
||||||
user = b.Config.Workspace.CurrentUser.UserName
|
|
||||||
}
|
|
||||||
canManageBundle, assistance := analyzeBundlePermissions(b)
|
|
||||||
|
|
||||||
if !canManageBundle {
|
|
||||||
return diag.Diagnostics{{
|
|
||||||
Summary: fmt.Sprintf("unable to deploy to %s as %s.\n"+
|
|
||||||
"Please make sure the current user or one of their groups is listed under the permissions of this bundle.\n"+
|
|
||||||
"%s\n"+
|
|
||||||
"They may need to redeploy the bundle to apply the new permissions.\n"+
|
|
||||||
"Please refer to https://docs.databricks.com/dev-tools/bundles/permissions.html for more on managing permissions.",
|
|
||||||
path, user, assistance),
|
|
||||||
Severity: diag.Error,
|
|
||||||
ID: diag.PathPermissionDenied,
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// According databricks.yml, the current user has the right permissions.
|
|
||||||
// But we're still seeing permission errors. So someone else will need
|
|
||||||
// to redeploy the bundle with the right set of permissions.
|
|
||||||
return diag.Diagnostics{{
|
|
||||||
Summary: fmt.Sprintf("access denied while updating deployment permissions as %s.\n"+
|
|
||||||
"%s\n"+
|
|
||||||
"They can redeploy the project to apply the latest set of permissions.\n"+
|
|
||||||
"Please refer to https://docs.databricks.com/dev-tools/bundles/permissions.html for more on managing permissions.",
|
|
||||||
user, assistance),
|
|
||||||
Severity: diag.Error,
|
|
||||||
ID: diag.CannotChangePathPermissions,
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
|
@ -41,72 +41,6 @@ func TestPermissionDiagnosticsApplySuccessWithOwner(t *testing.T) {
|
||||||
require.Empty(t, diags)
|
require.Empty(t, diags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPermissionDiagnosticsPermissionDeniedWithGroup(t *testing.T) {
|
|
||||||
b := mockBundle([]resources.Permission{
|
|
||||||
{Level: "CAN_MANAGE", GroupName: "testgroup"},
|
|
||||||
})
|
|
||||||
|
|
||||||
diags := permissions.ReportPossiblePermissionDenied(context.Background(), b, "testpath")
|
|
||||||
expected := "EPERM3: access denied while updating deployment permissions as testuser@databricks.com.\n" +
|
|
||||||
"For assistance, contact the owners of this project.\n" +
|
|
||||||
"They can redeploy the project to apply the latest set of permissions.\n" +
|
|
||||||
"Please refer to https://docs.databricks.com/dev-tools/bundles/permissions.html for more on managing permissions."
|
|
||||||
require.ErrorContains(t, diags.Error(), expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPermissionDiagnosticsPermissionDeniedWithOtherGroup(t *testing.T) {
|
|
||||||
b := mockBundle([]resources.Permission{
|
|
||||||
{Level: "CAN_MANAGE", GroupName: "othergroup"},
|
|
||||||
})
|
|
||||||
|
|
||||||
diags := permissions.ReportPossiblePermissionDenied(context.Background(), b, "testpath")
|
|
||||||
expected := "EPERM1: unable to deploy to testpath as testuser@databricks.com.\n" +
|
|
||||||
"Please make sure the current user or one of their groups is listed under the permissions of this bundle.\n" +
|
|
||||||
"For assistance, users or groups with appropriate permissions may include: othergroup.\n" +
|
|
||||||
"They may need to redeploy the bundle to apply the new permissions.\n" +
|
|
||||||
"Please refer to https://docs.databricks.com/dev-tools/bundles/permissions.html for more on managing permissions."
|
|
||||||
require.ErrorContains(t, diags.Error(), expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPermissionDiagnosticsPermissionDeniedWithoutPermission(t *testing.T) {
|
|
||||||
b := mockBundle([]resources.Permission{
|
|
||||||
{Level: "CAN_VIEW", UserName: "testuser@databricks.com"},
|
|
||||||
})
|
|
||||||
|
|
||||||
diags := permissions.ReportPossiblePermissionDenied(context.Background(), b, "testpath")
|
|
||||||
expected := "EPERM1: unable to deploy to testpath as testuser@databricks.com.\n" +
|
|
||||||
"Please make sure the current user or one of their groups is listed under the permissions of this bundle.\n" +
|
|
||||||
"For assistance, contact the owners of this project.\n" +
|
|
||||||
"They may need to redeploy the bundle to apply the new permissions.\n" +
|
|
||||||
"Please refer to https://docs.databricks.com/dev-tools/bundles/permissions.html for more on managing permissions."
|
|
||||||
require.ErrorContains(t, diags.Error(), expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPermissionDiagnosticsPermissionDeniedNilPermission(t *testing.T) {
|
|
||||||
b := mockBundle(nil)
|
|
||||||
|
|
||||||
diags := permissions.ReportPossiblePermissionDenied(context.Background(), b, "testpath")
|
|
||||||
expected := "EPERM1: unable to deploy to testpath as testuser@databricks.com.\n" +
|
|
||||||
"Please make sure the current user or one of their groups is listed under the permissions of this bundle.\n" +
|
|
||||||
"For assistance, contact the owners of this project.\n" +
|
|
||||||
"They may need to redeploy the bundle to apply the new permissions.\n" +
|
|
||||||
"Please refer to https://docs.databricks.com/dev-tools/bundles/permissions.html for more on managing permissions"
|
|
||||||
require.ErrorContains(t, diags.Error(), expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPermissionDiagnosticsFindOtherOwners(t *testing.T) {
|
|
||||||
b := mockBundle([]resources.Permission{
|
|
||||||
{Level: "CAN_MANAGE", GroupName: "testgroup"},
|
|
||||||
{Level: "CAN_MANAGE", UserName: "alice@databricks.com"},
|
|
||||||
})
|
|
||||||
|
|
||||||
diags := permissions.ReportPossiblePermissionDenied(context.Background(), b, "testpath")
|
|
||||||
require.ErrorContains(t, diags.Error(), "EPERM3: access denied while updating deployment permissions as testuser@databricks.com.\n"+
|
|
||||||
"For assistance, users or groups with appropriate permissions may include: alice@databricks.com.\n"+
|
|
||||||
"They can redeploy the project to apply the latest set of permissions.\n"+
|
|
||||||
"Please refer to https://docs.databricks.com/dev-tools/bundles/permissions.html for more on managing permissions.")
|
|
||||||
}
|
|
||||||
|
|
||||||
func mockBundle(permissions []resources.Permission) *bundle.Bundle {
|
func mockBundle(permissions []resources.Permission) *bundle.Bundle {
|
||||||
return &bundle.Bundle{
|
return &bundle.Bundle{
|
||||||
Config: config.Root{
|
Config: config.Root{
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/databricks/cli/bundle"
|
||||||
|
"github.com/databricks/cli/libs/diag"
|
||||||
|
"github.com/databricks/cli/libs/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReportPossiblePermissionDenied generates a diagnostic message when a permission denied error is encountered.
|
||||||
|
//
|
||||||
|
// Note that since the workspace API doesn't always distinguish between permission denied and path errors,
|
||||||
|
// we must treat this as a "possible permission error". See acquire.go for more about this.
|
||||||
|
func ReportPossiblePermissionDenied(ctx context.Context, b *bundle.Bundle, path string) diag.Diagnostics {
|
||||||
|
log.Errorf(ctx, "Failed to update, encountered possible permission error: %v", path)
|
||||||
|
|
||||||
|
user := b.Config.Workspace.CurrentUser.DisplayName
|
||||||
|
if user == "" {
|
||||||
|
user = b.Config.Workspace.CurrentUser.UserName
|
||||||
|
}
|
||||||
|
canManageBundle, assistance := analyzeBundlePermissions(b)
|
||||||
|
|
||||||
|
if !canManageBundle {
|
||||||
|
return diag.Diagnostics{{
|
||||||
|
Summary: fmt.Sprintf("unable to deploy to %s as %s.\n"+
|
||||||
|
"Please make sure the current user or one of their groups is listed under the permissions of this bundle.\n"+
|
||||||
|
"%s\n"+
|
||||||
|
"They may need to redeploy the bundle to apply the new permissions.\n"+
|
||||||
|
"Please refer to https://docs.databricks.com/dev-tools/bundles/permissions.html for more on managing permissions.",
|
||||||
|
path, user, assistance),
|
||||||
|
Severity: diag.Error,
|
||||||
|
ID: diag.PathPermissionDenied,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// According databricks.yml, the current user has the right permissions.
|
||||||
|
// But we're still seeing permission errors. So someone else will need
|
||||||
|
// to redeploy the bundle with the right set of permissions.
|
||||||
|
return diag.Diagnostics{{
|
||||||
|
Summary: fmt.Sprintf("unable to deploy to %s as %s. Cannot apply local deployment permissions.\n"+
|
||||||
|
"%s\n"+
|
||||||
|
"They can redeploy the project to apply the latest set of permissions.\n"+
|
||||||
|
"Please refer to https://docs.databricks.com/dev-tools/bundles/permissions.html for more on managing permissions.",
|
||||||
|
path, user, assistance),
|
||||||
|
Severity: diag.Error,
|
||||||
|
ID: diag.CannotChangePathPermissions,
|
||||||
|
}}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package permissions_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/databricks/cli/bundle/config/resources"
|
||||||
|
"github.com/databricks/cli/bundle/permissions"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPermissionsReportPermissionDeniedWithGroup(t *testing.T) {
|
||||||
|
b := mockBundle([]resources.Permission{
|
||||||
|
{Level: "CAN_MANAGE", GroupName: "testgroup"},
|
||||||
|
})
|
||||||
|
|
||||||
|
diags := permissions.ReportPossiblePermissionDenied(context.Background(), b, "testpath")
|
||||||
|
expected := "EPERM3: unable to deploy to testpath as testuser@databricks.com. Cannot apply local deployment permissions.\n" +
|
||||||
|
"For assistance, contact the owners of this project.\n" +
|
||||||
|
"They can redeploy the project to apply the latest set of permissions.\n" +
|
||||||
|
"Please refer to https://docs.databricks.com/dev-tools/bundles/permissions.html for more on managing permissions."
|
||||||
|
require.ErrorContains(t, diags.Error(), expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPermissionsReportPermissionDeniedWithOtherGroup(t *testing.T) {
|
||||||
|
b := mockBundle([]resources.Permission{
|
||||||
|
{Level: "CAN_MANAGE", GroupName: "othergroup"},
|
||||||
|
})
|
||||||
|
|
||||||
|
diags := permissions.ReportPossiblePermissionDenied(context.Background(), b, "testpath")
|
||||||
|
expected := "EPERM1: unable to deploy to testpath as testuser@databricks.com.\n" +
|
||||||
|
"Please make sure the current user or one of their groups is listed under the permissions of this bundle.\n" +
|
||||||
|
"For assistance, users or groups with appropriate permissions may include: othergroup.\n" +
|
||||||
|
"They may need to redeploy the bundle to apply the new permissions.\n" +
|
||||||
|
"Please refer to https://docs.databricks.com/dev-tools/bundles/permissions.html for more on managing permissions."
|
||||||
|
require.ErrorContains(t, diags.Error(), expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPermissionsReportPermissionDeniedWithoutPermission(t *testing.T) {
|
||||||
|
b := mockBundle([]resources.Permission{
|
||||||
|
{Level: "CAN_VIEW", UserName: "testuser@databricks.com"},
|
||||||
|
})
|
||||||
|
|
||||||
|
diags := permissions.ReportPossiblePermissionDenied(context.Background(), b, "testpath")
|
||||||
|
expected := "EPERM1: unable to deploy to testpath as testuser@databricks.com.\n" +
|
||||||
|
"Please make sure the current user or one of their groups is listed under the permissions of this bundle.\n" +
|
||||||
|
"For assistance, contact the owners of this project.\n" +
|
||||||
|
"They may need to redeploy the bundle to apply the new permissions.\n" +
|
||||||
|
"Please refer to https://docs.databricks.com/dev-tools/bundles/permissions.html for more on managing permissions."
|
||||||
|
require.ErrorContains(t, diags.Error(), expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPermissionsReportPermissionDeniedNilPermission(t *testing.T) {
|
||||||
|
b := mockBundle(nil)
|
||||||
|
|
||||||
|
diags := permissions.ReportPossiblePermissionDenied(context.Background(), b, "testpath")
|
||||||
|
expected := "EPERM1: unable to deploy to testpath as testuser@databricks.com.\n" +
|
||||||
|
"Please make sure the current user or one of their groups is listed under the permissions of this bundle.\n" +
|
||||||
|
"For assistance, contact the owners of this project.\n" +
|
||||||
|
"They may need to redeploy the bundle to apply the new permissions.\n" +
|
||||||
|
"Please refer to https://docs.databricks.com/dev-tools/bundles/permissions.html for more on managing permissions"
|
||||||
|
require.ErrorContains(t, diags.Error(), expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPermissionsReportFindOtherOwners(t *testing.T) {
|
||||||
|
b := mockBundle([]resources.Permission{
|
||||||
|
{Level: "CAN_MANAGE", GroupName: "testgroup"},
|
||||||
|
{Level: "CAN_MANAGE", UserName: "alice@databricks.com"},
|
||||||
|
})
|
||||||
|
|
||||||
|
diags := permissions.ReportPossiblePermissionDenied(context.Background(), b, "testpath")
|
||||||
|
require.ErrorContains(t, diags.Error(), "EPERM3: unable to deploy to testpath as testuser@databricks.com. Cannot apply local deployment permissions.\n"+
|
||||||
|
"For assistance, users or groups with appropriate permissions may include: alice@databricks.com.\n"+
|
||||||
|
"They can redeploy the project to apply the latest set of permissions.\n"+
|
||||||
|
"Please refer to https://docs.databricks.com/dev-tools/bundles/permissions.html for more on managing permissions.")
|
||||||
|
}
|
|
@ -55,6 +55,7 @@ func Initialize() bundle.Mutator {
|
||||||
"workspace",
|
"workspace",
|
||||||
"variables",
|
"variables",
|
||||||
),
|
),
|
||||||
|
// Provide permission config errors & warnings after initializing all variables
|
||||||
permissions.PermissionDiagnostics(),
|
permissions.PermissionDiagnostics(),
|
||||||
mutator.SetRunAs(),
|
mutator.SetRunAs(),
|
||||||
mutator.OverrideCompute(),
|
mutator.OverrideCompute(),
|
||||||
|
|
|
@ -304,7 +304,7 @@ func (w *workspaceFilesClient) ReadDir(ctx context.Context, name string) ([]fs.D
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: This API returns a 404 if the specified path does not exist,
|
// NOTE: This API returns a 404 if the specified path does not exist,
|
||||||
// but can also do so if we don't have access to write to the path.
|
// but can also do so if we don't have read access.
|
||||||
if aerr.StatusCode == http.StatusNotFound {
|
if aerr.StatusCode == http.StatusNotFound {
|
||||||
return nil, NoSuchDirectoryError{path.Dir(absPath)}
|
return nil, NoSuchDirectoryError{path.Dir(absPath)}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue