Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 214
Add support for "debug runs"#493
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
48d6df327b2b77823c8686414701bdd21247c654ecb7a80afFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| package mutator | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/databricks/cli/bundle" | ||
| "github.com/databricks/cli/bundle/config" | ||
| "github.com/databricks/cli/bundle/config/resources" | ||
| ) | ||
| type overrideCompute struct { | ||
| compute string | ||
| } | ||
| func OverrideCompute(compute string) bundle.Mutator { | ||
| return &overrideCompute{compute: compute} | ||
| } | ||
| func (m *overrideCompute) Name() string { | ||
| return "OverrideCompute" | ||
| } | ||
| func (m *overrideCompute) overrideJobCompute(j *resources.Job) { | ||
| for i := range j.Tasks { | ||
| task := &j.Tasks[i] | ||
| if task.NewCluster != nil { | ||
| task.NewCluster = nil | ||
| task.ExistingClusterId = m.compute | ||
| } else if task.ExistingClusterId != "" { | ||
| task.ExistingClusterId = m.compute | ||
| } | ||
| } | ||
| } | ||
| func (m *overrideCompute) Apply(ctx context.Context, b *bundle.Bundle) error { | ||
| if m.compute == "" { | ||
| return nil | ||
| } | ||
| if b.Config.Bundle.Mode != config.Debug { | ||
| return fmt.Errorf("cannot override compute for an environment that does not use 'mode: debug'") | ||
| } | ||
| r := b.Config.Resources | ||
| for i := range r.Jobs { | ||
| m.overrideJobCompute(r.Jobs[i]) | ||
| } | ||
| return nil | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| package mutator_test | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/databricks/cli/bundle" | ||
| "github.com/databricks/cli/bundle/config" | ||
| "github.com/databricks/cli/bundle/config/mutator" | ||
| "github.com/databricks/cli/bundle/config/resources" | ||
| "github.com/databricks/databricks-sdk-go/service/compute" | ||
| "github.com/databricks/databricks-sdk-go/service/jobs" | ||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
| func TestOverrideCompute(t *testing.T) { | ||
| bundle := &bundle.Bundle{ | ||
| Config: config.Root{ | ||
| Bundle: config.Bundle{ | ||
| Mode: config.Debug, | ||
| }, | ||
| Resources: config.Resources{ | ||
| Jobs: map[string]*resources.Job{ | ||
| "job1": {JobSettings: &jobs.JobSettings{ | ||
| Name: "job1", | ||
| Tasks: []jobs.JobTaskSettings{ | ||
| { | ||
| NewCluster: &compute.BaseClusterInfo{}, | ||
| }, | ||
| { | ||
| ExistingClusterId: "cluster2", | ||
| }, | ||
| }, | ||
| }}, | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| m := mutator.OverrideCompute("newClusterID") | ||
| err := m.Apply(context.Background(), bundle) | ||
| require.NoError(t, err) | ||
| assert.Nil(t, bundle.Config.Resources.Jobs["job1"].Tasks[0].NewCluster) | ||
| assert.Equal(t, "newClusterID", bundle.Config.Resources.Jobs["job1"].Tasks[0].ExistingClusterId) | ||
| assert.Equal(t, "newClusterID", bundle.Config.Resources.Jobs["job1"].Tasks[1].ExistingClusterId) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| package mutator | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/databricks/cli/bundle" | ||
| "github.com/databricks/cli/bundle/config" | ||
| "github.com/databricks/databricks-sdk-go/service/ml" | ||
| ) | ||
| type processEnvironmentMode struct{} | ||
| const debugConcurrentRuns = 4 | ||
| func ProcessEnvironmentMode() bundle.Mutator { | ||
| return &processEnvironmentMode{} | ||
| } | ||
| func (m *processEnvironmentMode) Name() string { | ||
| return "ProcessEnvironmentMode" | ||
| } | ||
| // Mark all resources as being for 'debug' purposes, i.e. | ||
| // changing their their name, adding tags, and (in the future) | ||
| // marking them as 'hidden' in the UI. | ||
| func processDebugMode(b *bundle.Bundle) error { | ||
| r := b.Config.Resources | ||
| for i := range r.Jobs { | ||
| r.Jobs[i].Name = "[debug] " + r.Jobs[i].Name | ||
ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Saad: this should probably say [username debug] ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. | ||
| if r.Jobs[i].Tags == nil { | ||
| r.Jobs[i].Tags = make(map[string]string) | ||
| } | ||
| r.Jobs[i].Tags["debug"] = "" | ||
| if r.Jobs[i].MaxConcurrentRuns == 0 { | ||
| r.Jobs[i].MaxConcurrentRuns = debugConcurrentRuns | ||
| } | ||
lennartkats-db marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| if r.Jobs[i].Schedule != nil { | ||
| r.Jobs[i].Schedule.PauseStatus = "PAUSED" | ||
| } | ||
| if r.Jobs[i].Continuous != nil { | ||
| r.Jobs[i].Continuous.PauseStatus = "PAUSED" | ||
| } | ||
| if r.Jobs[i].Trigger != nil { | ||
| r.Jobs[i].Trigger.PauseStatus = "PAUSED" | ||
| } | ||
| } | ||
| for i := range r.Pipelines { | ||
| r.Pipelines[i].Name = "[debug] " + r.Pipelines[i].Name | ||
| r.Pipelines[i].Development = true | ||
| // (pipelines don't yet support tags) | ||
| } | ||
| for i := range r.Models { | ||
| r.Models[i].Name = "[debug] " + r.Models[i].Name | ||
| r.Models[i].Tags = append(r.Models[i].Tags, ml.ModelTag{Key: "debug", Value: ""}) | ||
| } | ||
| for i := range r.Experiments { | ||
| r.Experiments[i].Name = "[debug] " + r.Experiments[i].Name | ||
| r.Experiments[i].Tags = append(r.Experiments[i].Tags, ml.ExperimentTag{Key: "debug", Value: ""}) | ||
| } | ||
| return nil | ||
| } | ||
| func (m *processEnvironmentMode) Apply(ctx context.Context, b *bundle.Bundle) error { | ||
| switch b.Config.Bundle.Mode { | ||
| case config.Debug: | ||
| return processDebugMode(b) | ||
| case config.Default, "": | ||
| // No action | ||
| case config.PullRequest: | ||
| return fmt.Errorf("not implemented") | ||
| default: | ||
| return fmt.Errorf("unsupported value specified for 'mode': %s", b.Config.Bundle.Mode) | ||
| } | ||
| return nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| package mutator_test | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/databricks/cli/bundle" | ||
| "github.com/databricks/cli/bundle/config" | ||
| "github.com/databricks/cli/bundle/config/mutator" | ||
| "github.com/databricks/cli/bundle/config/resources" | ||
| "github.com/databricks/databricks-sdk-go/service/jobs" | ||
| "github.com/databricks/databricks-sdk-go/service/ml" | ||
| "github.com/databricks/databricks-sdk-go/service/pipelines" | ||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
| func TestProcessEnvironmentModeApplyDebug(t *testing.T) { | ||
| bundle := &bundle.Bundle{ | ||
| Config: config.Root{ | ||
| Bundle: config.Bundle{ | ||
| Mode: config.Debug, | ||
| }, | ||
| Resources: config.Resources{ | ||
| Jobs: map[string]*resources.Job{ | ||
| "job1": {JobSettings: &jobs.JobSettings{Name: "job1"}}, | ||
| }, | ||
| Pipelines: map[string]*resources.Pipeline{ | ||
| "pipeline1": {PipelineSpec: &pipelines.PipelineSpec{Name: "pipeline1"}}, | ||
| }, | ||
| Experiments: map[string]*resources.MlflowExperiment{ | ||
| "experiment1": {Experiment: &ml.Experiment{Name: "experiment1"}}, | ||
| }, | ||
| Models: map[string]*resources.MlflowModel{ | ||
| "model1": {Model: &ml.Model{Name: "model1"}}, | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| m := mutator.ProcessEnvironmentMode() | ||
| err := m.Apply(context.Background(), bundle) | ||
| require.NoError(t, err) | ||
| assert.Equal(t, "[debug] job1", bundle.Config.Resources.Jobs["job1"].Name) | ||
| assert.Equal(t, "[debug] pipeline1", bundle.Config.Resources.Pipelines["pipeline1"].Name) | ||
| assert.Equal(t, "[debug] experiment1", bundle.Config.Resources.Experiments["experiment1"].Name) | ||
| assert.Equal(t, "[debug] model1", bundle.Config.Resources.Models["model1"].Name) | ||
| assert.Equal(t, "debug", bundle.Config.Resources.Experiments["experiment1"].Experiment.Tags[0].Key) | ||
| assert.True(t, bundle.Config.Resources.Pipelines["pipeline1"].PipelineSpec.Development) | ||
| } | ||
| func TestProcessEnvironmentModeApplyDefault(t *testing.T) { | ||
| bundle := &bundle.Bundle{ | ||
| Config: config.Root{ | ||
| Bundle: config.Bundle{ | ||
| Mode: config.Default, | ||
| }, | ||
| Resources: config.Resources{ | ||
| Jobs: map[string]*resources.Job{ | ||
| "job1": {JobSettings: &jobs.JobSettings{Name: "job1"}}, | ||
| }, | ||
| Pipelines: map[string]*resources.Pipeline{ | ||
| "pipeline1": {PipelineSpec: &pipelines.PipelineSpec{Name: "pipeline1"}}, | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| m := mutator.ProcessEnvironmentMode() | ||
| err := m.Apply(context.Background(), bundle) | ||
| require.NoError(t, err) | ||
| assert.Equal(t, "job1", bundle.Config.Resources.Jobs["job1"].Name) | ||
| assert.Equal(t, "pipeline1", bundle.Config.Resources.Pipelines["pipeline1"].Name) | ||
| assert.False(t, bundle.Config.Resources.Pipelines["pipeline1"].PipelineSpec.Development) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -243,6 +243,15 @@ func (r *jobRunner) Run(ctx context.Context, opts *Options) (output.RunOutput, e | ||
| if err != nil { | ||
| return nil, fmt.Errorf("cannot start job") | ||
| } | ||
| if opts.NoWait { | ||
| details, err := w.Jobs.GetRun(ctx, jobs.GetRunRequest{ | ||
| RunId: waiter.RunId, | ||
| }) | ||
| progressLogger.Log(progress.NewJobRunUrlEvent(details.RunPageUrl)) | ||
lennartkats-db marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| return nil, err | ||
| } | ||
| run, err := waiter.OnProgress(func(r *jobs.Run) { | ||
| pullRunId(r) | ||
| logDebug(r) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -8,6 +8,7 @@ resources: | ||
| environments: | ||
| development: | ||
| mode: debug | ||
| resources: | ||
| pipelines: | ||
| nyc_taxi_pipeline: | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it should rather be a property of a bundle flipped by a command-line flag and not the environment variable.
https://github.com/databricks/cli/blob/ccbcccd92903bc0a6dbdbc94072035b30aca8527/cmd/bundle/destroy.go#L27C1-L28
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thing is, it's not really "ephemeral" at this time. We need to create real, persistent jobs and pipelines. The main reason is that one job may need to reference another job or another pipeline in the same DAB.
When we say "ephemeral" in this case we really just mean that we need to have "some" garbage collection mechanism that removes these persistent jobs after perhaps 24 hours of non-use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can add that cleanup capability for previous normal runs from bundle runner 🤷♂️