2023-07-15 14:08:44 +00:00
|
|
|
<<<<<<< HEAD
|
2023-06-18 14:47:01 +00:00
|
|
|
package mutator
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2023-07-07 16:04:28 +00:00
|
|
|
"path"
|
2023-07-10 07:12:50 +00:00
|
|
|
"strings"
|
2023-06-18 14:47:01 +00:00
|
|
|
|
|
|
|
"github.com/databricks/cli/bundle"
|
|
|
|
"github.com/databricks/cli/bundle/config"
|
2023-07-10 07:12:50 +00:00
|
|
|
"github.com/databricks/databricks-sdk-go/apierr"
|
2023-07-07 09:12:14 +00:00
|
|
|
"github.com/databricks/databricks-sdk-go/service/jobs"
|
2023-06-18 14:47:01 +00:00
|
|
|
"github.com/databricks/databricks-sdk-go/service/ml"
|
|
|
|
)
|
|
|
|
|
2023-07-15 14:10:40 +00:00
|
|
|
type processEnvironmentMode struct{}
|
2023-06-18 14:47:01 +00:00
|
|
|
|
2023-07-07 09:09:09 +00:00
|
|
|
const developmentConcurrentRuns = 4
|
2023-06-18 15:40:30 +00:00
|
|
|
|
2023-07-10 07:12:50 +00:00
|
|
|
func ProcessEnvironmentMode() *processEnvironmentMode {
|
2023-06-18 14:47:01 +00:00
|
|
|
return &processEnvironmentMode{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *processEnvironmentMode) Name() string {
|
|
|
|
return "ProcessEnvironmentMode"
|
|
|
|
}
|
|
|
|
|
2023-07-03 14:30:42 +00:00
|
|
|
// Mark all resources as being for 'development' purposes, i.e.
|
2023-06-18 14:47:01 +00:00
|
|
|
// changing their their name, adding tags, and (in the future)
|
|
|
|
// marking them as 'hidden' in the UI.
|
2023-07-10 07:12:50 +00:00
|
|
|
func transformDevelopmentMode(b *bundle.Bundle) error {
|
2023-06-18 14:47:01 +00:00
|
|
|
r := b.Config.Resources
|
|
|
|
|
2023-07-10 07:12:50 +00:00
|
|
|
prefix := "[dev " + b.Config.Workspace.CurrentUser.ShortName + "] "
|
|
|
|
|
2023-06-18 14:47:01 +00:00
|
|
|
for i := range r.Jobs {
|
2023-07-10 07:12:50 +00:00
|
|
|
r.Jobs[i].Name = prefix + r.Jobs[i].Name
|
2023-06-18 14:47:01 +00:00
|
|
|
if r.Jobs[i].Tags == nil {
|
|
|
|
r.Jobs[i].Tags = make(map[string]string)
|
|
|
|
}
|
2023-07-10 07:12:50 +00:00
|
|
|
r.Jobs[i].Tags["dev"] = b.Config.Workspace.CurrentUser.DisplayName
|
2023-06-18 15:40:30 +00:00
|
|
|
if r.Jobs[i].MaxConcurrentRuns == 0 {
|
2023-07-07 09:09:09 +00:00
|
|
|
r.Jobs[i].MaxConcurrentRuns = developmentConcurrentRuns
|
2023-06-18 15:40:30 +00:00
|
|
|
}
|
2023-06-20 09:21:33 +00:00
|
|
|
if r.Jobs[i].Schedule != nil {
|
2023-07-07 09:12:14 +00:00
|
|
|
r.Jobs[i].Schedule.PauseStatus = jobs.PauseStatusPaused
|
2023-06-20 09:21:33 +00:00
|
|
|
}
|
|
|
|
if r.Jobs[i].Continuous != nil {
|
2023-07-07 09:12:14 +00:00
|
|
|
r.Jobs[i].Continuous.PauseStatus = jobs.PauseStatusPaused
|
2023-06-20 09:21:33 +00:00
|
|
|
}
|
|
|
|
if r.Jobs[i].Trigger != nil {
|
2023-07-07 09:12:14 +00:00
|
|
|
r.Jobs[i].Trigger.PauseStatus = jobs.PauseStatusPaused
|
2023-06-20 09:21:33 +00:00
|
|
|
}
|
2023-06-18 14:47:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for i := range r.Pipelines {
|
2023-07-10 07:12:50 +00:00
|
|
|
r.Pipelines[i].Name = prefix + r.Pipelines[i].Name
|
2023-06-18 14:47:01 +00:00
|
|
|
r.Pipelines[i].Development = true
|
2023-06-18 15:40:30 +00:00
|
|
|
// (pipelines don't yet support tags)
|
2023-06-18 14:47:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for i := range r.Models {
|
2023-07-10 07:12:50 +00:00
|
|
|
r.Models[i].Name = prefix + r.Models[i].Name
|
2023-07-03 14:30:42 +00:00
|
|
|
r.Models[i].Tags = append(r.Models[i].Tags, ml.ModelTag{Key: "dev", Value: ""})
|
2023-06-18 14:47:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for i := range r.Experiments {
|
2023-07-07 16:04:28 +00:00
|
|
|
filepath := r.Experiments[i].Name
|
|
|
|
dir := path.Dir(filepath)
|
|
|
|
base := path.Base(filepath)
|
2023-07-07 09:49:02 +00:00
|
|
|
if dir == "." {
|
2023-07-10 07:12:50 +00:00
|
|
|
r.Experiments[i].Name = prefix + base
|
2023-07-07 09:49:02 +00:00
|
|
|
} else {
|
2023-07-10 07:12:50 +00:00
|
|
|
r.Experiments[i].Name = dir + "/" + prefix + base
|
2023-07-07 09:49:02 +00:00
|
|
|
}
|
2023-07-10 07:12:50 +00:00
|
|
|
r.Experiments[i].Tags = append(r.Experiments[i].Tags, ml.ExperimentTag{Key: "dev", Value: b.Config.Workspace.CurrentUser.DisplayName})
|
2023-06-18 14:47:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-07-10 07:12:50 +00:00
|
|
|
func validateDevelopmentMode(b *bundle.Bundle) error {
|
2023-07-15 14:10:40 +00:00
|
|
|
if path := findIncorrectPath(b, config.Development); path != "" {
|
|
|
|
return fmt.Errorf("%s must start with '~/' or contain the current username when using 'mode: development'", path)
|
2023-07-10 07:12:50 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-07-15 14:10:40 +00:00
|
|
|
func findIncorrectPath(b *bundle.Bundle, mode config.Mode) string {
|
2023-07-10 07:12:50 +00:00
|
|
|
username := b.Config.Workspace.CurrentUser.UserName
|
2023-07-15 14:10:40 +00:00
|
|
|
containsExpected := true
|
|
|
|
if mode == config.Production {
|
|
|
|
containsExpected = false
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.Contains(b.Config.Workspace.RootPath, username) != containsExpected && b.Config.Workspace.RootPath != "" {
|
|
|
|
return "root_path"
|
|
|
|
}
|
|
|
|
if strings.Contains(b.Config.Workspace.StatePath, username) != containsExpected {
|
|
|
|
return "state_path"
|
|
|
|
}
|
|
|
|
if strings.Contains(b.Config.Workspace.FilesPath, username) != containsExpected {
|
|
|
|
return "files_path"
|
|
|
|
}
|
|
|
|
if strings.Contains(b.Config.Workspace.ArtifactsPath, username) != containsExpected {
|
|
|
|
return "artifacts_path"
|
|
|
|
}
|
|
|
|
return ""
|
2023-07-10 07:12:50 +00:00
|
|
|
}
|
|
|
|
|
2023-07-10 07:21:14 +00:00
|
|
|
func validateProductionMode(ctx context.Context, b *bundle.Bundle, isPrincipalUsed bool) error {
|
2023-07-10 07:12:50 +00:00
|
|
|
if b.Config.Bundle.Git.Inferred {
|
2023-07-15 14:10:40 +00:00
|
|
|
return fmt.Errorf("environment with 'mode: production' must specify an explicit 'environments.git' configuration")
|
2023-07-10 07:12:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
r := b.Config.Resources
|
|
|
|
for i := range r.Pipelines {
|
|
|
|
if r.Pipelines[i].Development {
|
|
|
|
return fmt.Errorf("environment with 'mode: production' cannot specify a pipeline with 'development: true'")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-10 07:21:14 +00:00
|
|
|
if !isPrincipalUsed {
|
2023-07-15 14:10:40 +00:00
|
|
|
if path := findIncorrectPath(b, config.Production); path != "" {
|
|
|
|
message := "%s must not contain the current username when using 'mode: production' without a service principal"
|
|
|
|
if path == "root_path" {
|
|
|
|
return fmt.Errorf(message+"\n tip: set workspace.root_path to a shared path such as /Shared/.bundle/${bundle.name}/${bundle.environment}", path)
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf(message, path)
|
|
|
|
}
|
2023-07-10 07:12:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if !arePermissionsSetExplicitly(r) {
|
2023-07-15 14:10:40 +00:00
|
|
|
return fmt.Errorf("permissions and run_as must be set when using 'mode_production' without a service principals")
|
2023-07-10 07:12:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determines whether a service principal identity is used to run the CLI.
|
2023-07-10 07:21:14 +00:00
|
|
|
func isServicePrincipalUsed(ctx context.Context, b *bundle.Bundle) (bool, error) {
|
2023-07-10 07:12:50 +00:00
|
|
|
ws := b.WorkspaceClient()
|
|
|
|
|
2023-07-10 07:21:14 +00:00
|
|
|
_, err := ws.ServicePrincipals.GetById(ctx, b.Config.Workspace.CurrentUser.Id)
|
2023-07-10 07:12:50 +00:00
|
|
|
if err != nil {
|
|
|
|
apiError, ok := err.(*apierr.APIError)
|
|
|
|
if ok && apiError.StatusCode == 404 {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determines whether permissions and run_as are explicitly set for all resources.
|
|
|
|
// We do this in a best-effort fashion; we may not actually test all resources,
|
|
|
|
// as we expect customers to use the top-level 'permissions' and 'run_as' fields.
|
|
|
|
// We'd rather not check for those specific fields though, as customers might
|
|
|
|
// set specific permissions instead!
|
|
|
|
func arePermissionsSetExplicitly(r config.Resources) bool {
|
|
|
|
for i := range r.Pipelines {
|
|
|
|
if r.Pipelines[i].Permissions == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range r.Jobs {
|
|
|
|
if r.Jobs[i].Permissions == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if r.Jobs[i].RunAs == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2023-07-15 14:10:40 +00:00
|
|
|
return true
|
2023-07-10 07:12:50 +00:00
|
|
|
}
|
|
|
|
|
2023-06-18 14:47:01 +00:00
|
|
|
func (m *processEnvironmentMode) Apply(ctx context.Context, b *bundle.Bundle) error {
|
|
|
|
switch b.Config.Bundle.Mode {
|
2023-07-03 14:30:42 +00:00
|
|
|
case config.Development:
|
2023-07-10 07:12:50 +00:00
|
|
|
err := validateDevelopmentMode(b)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return transformDevelopmentMode(b)
|
|
|
|
case config.Production:
|
2023-07-15 14:10:40 +00:00
|
|
|
isPrincipal, err := isServicePrincipalUsed(ctx, b)
|
2023-07-10 07:21:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return validateProductionMode(ctx, b, isPrincipal)
|
2023-07-03 14:30:42 +00:00
|
|
|
case "":
|
2023-06-18 14:47:01 +00:00
|
|
|
// No action
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("unsupported value specified for 'mode': %s", b.Config.Bundle.Mode)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2023-07-15 14:08:44 +00:00
|
|
|
||||||| 3354750
|
|
|
|
=======
|
2023-07-12 06:51:54 +00:00
|
|
|
package mutator
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"path"
|
|
|
|
|
|
|
|
"github.com/databricks/cli/bundle"
|
|
|
|
"github.com/databricks/cli/bundle/config"
|
|
|
|
"github.com/databricks/databricks-sdk-go/service/jobs"
|
|
|
|
"github.com/databricks/databricks-sdk-go/service/ml"
|
|
|
|
)
|
|
|
|
|
|
|
|
type processEnvironmentMode struct{}
|
|
|
|
|
|
|
|
const developmentConcurrentRuns = 4
|
|
|
|
|
|
|
|
func ProcessEnvironmentMode() bundle.Mutator {
|
|
|
|
return &processEnvironmentMode{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *processEnvironmentMode) Name() string {
|
|
|
|
return "ProcessEnvironmentMode"
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mark all resources as being for 'development' purposes, i.e.
|
|
|
|
// changing their their name, adding tags, and (in the future)
|
|
|
|
// marking them as 'hidden' in the UI.
|
|
|
|
func processDevelopmentMode(b *bundle.Bundle) error {
|
|
|
|
r := b.Config.Resources
|
|
|
|
|
|
|
|
for i := range r.Jobs {
|
|
|
|
r.Jobs[i].Name = "[dev] " + r.Jobs[i].Name
|
|
|
|
if r.Jobs[i].Tags == nil {
|
|
|
|
r.Jobs[i].Tags = make(map[string]string)
|
|
|
|
}
|
|
|
|
r.Jobs[i].Tags["dev"] = ""
|
|
|
|
if r.Jobs[i].MaxConcurrentRuns == 0 {
|
|
|
|
r.Jobs[i].MaxConcurrentRuns = developmentConcurrentRuns
|
|
|
|
}
|
|
|
|
if r.Jobs[i].Schedule != nil {
|
|
|
|
r.Jobs[i].Schedule.PauseStatus = jobs.PauseStatusPaused
|
|
|
|
}
|
|
|
|
if r.Jobs[i].Continuous != nil {
|
|
|
|
r.Jobs[i].Continuous.PauseStatus = jobs.PauseStatusPaused
|
|
|
|
}
|
|
|
|
if r.Jobs[i].Trigger != nil {
|
|
|
|
r.Jobs[i].Trigger.PauseStatus = jobs.PauseStatusPaused
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range r.Pipelines {
|
|
|
|
r.Pipelines[i].Name = "[dev] " + r.Pipelines[i].Name
|
|
|
|
r.Pipelines[i].Development = true
|
|
|
|
// (pipelines don't yet support tags)
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range r.Models {
|
|
|
|
r.Models[i].Name = "[dev] " + r.Models[i].Name
|
|
|
|
r.Models[i].Tags = append(r.Models[i].Tags, ml.ModelTag{Key: "dev", Value: ""})
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range r.Experiments {
|
|
|
|
filepath := r.Experiments[i].Name
|
|
|
|
dir := path.Dir(filepath)
|
|
|
|
base := path.Base(filepath)
|
|
|
|
if dir == "." {
|
|
|
|
r.Experiments[i].Name = "[dev] " + base
|
|
|
|
} else {
|
|
|
|
r.Experiments[i].Name = dir + "/[dev] " + base
|
|
|
|
}
|
|
|
|
r.Experiments[i].Tags = append(r.Experiments[i].Tags, ml.ExperimentTag{Key: "dev", Value: ""})
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *processEnvironmentMode) Apply(ctx context.Context, b *bundle.Bundle) error {
|
|
|
|
switch b.Config.Bundle.Mode {
|
|
|
|
case config.Development:
|
|
|
|
return processDevelopmentMode(b)
|
|
|
|
case "":
|
|
|
|
// No action
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("unsupported value specified for 'mode': %s", b.Config.Bundle.Mode)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2023-07-15 14:08:44 +00:00
|
|
|
>>>>>>> databricks/main
|