-
Notifications
You must be signed in to change notification settings - Fork 173
Add configurable presets for name prefixes, tags, etc. #1490
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
Changes from 24 commits
e5ac74d
6eaee84
601c32b
b16c18c
901097a
a815f30
13630dd
e3b0435
10a1ffc
405e202
7323d02
4dc5f41
82e1d49
6d75e84
40b004e
29a23cf
b353a2f
f636e09
347e24e
3e003c0
40f3bb4
b1427b3
f2553ff
fb902c9
6159c3c
b4564f2
70d8988
a073f84
b9e3278
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,200 @@ | ||
| package mutator | ||
|
|
||
| import ( | ||
| "context" | ||
| "path" | ||
| "slices" | ||
| "sort" | ||
| "strings" | ||
|
|
||
| "github.com/databricks/cli/bundle" | ||
| "github.com/databricks/cli/bundle/config" | ||
| "github.com/databricks/cli/libs/diag" | ||
| "github.com/databricks/cli/libs/textutil" | ||
| "github.com/databricks/databricks-sdk-go/service/catalog" | ||
| "github.com/databricks/databricks-sdk-go/service/jobs" | ||
| "github.com/databricks/databricks-sdk-go/service/ml" | ||
| ) | ||
|
|
||
| type applyPresets struct{} | ||
|
|
||
| // Apply all presets, e.g. the prefix presets that | ||
| // adds a prefix to all names of all resources. | ||
| func ApplyPresets() *applyPresets { | ||
| return &applyPresets{} | ||
| } | ||
|
|
||
| type Tag struct { | ||
| Key string | ||
| Value string | ||
| } | ||
|
|
||
| func (m *applyPresets) Name() string { | ||
| return "ApplyPresets" | ||
| } | ||
|
|
||
| func (m *applyPresets) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { | ||
| if d := validatePauseStatus(b); d != nil { | ||
| return d | ||
| } | ||
|
|
||
| r := b.Config.Resources | ||
| t := b.Config.Presets | ||
| prefix := t.NamePrefix | ||
| tags := toTagArray(t.Tags) | ||
|
|
||
| // Jobs presets: Prefix, Tags, JobsMaxConcurrentRuns, TriggerPauseStatus | ||
| for _, j := range r.Jobs { | ||
| j.Name = prefix + j.Name | ||
| if j.Tags == nil { | ||
| j.Tags = make(map[string]string) | ||
| } | ||
| for _, tag := range tags { | ||
| if j.Tags[tag.Key] == "" { | ||
| j.Tags[tag.Key] = tag.Value | ||
| } | ||
| } | ||
| if j.MaxConcurrentRuns == 0 { | ||
| j.MaxConcurrentRuns = t.JobsMaxConcurrentRuns | ||
| } | ||
| if t.TriggerPauseStatus != "" { | ||
| paused := jobs.PauseStatusPaused | ||
| if t.TriggerPauseStatus == config.Unpaused { | ||
| paused = jobs.PauseStatusUnpaused | ||
| } | ||
|
Contributor
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. It could be "foobar" and be treated as paused. To improve readability you can use a switch/case on
Contributor
Author
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. We have a validation for the |
||
|
|
||
| if j.Schedule != nil && j.Schedule.PauseStatus == "" { | ||
| j.Schedule.PauseStatus = paused | ||
| } | ||
| if j.Continuous != nil && j.Continuous.PauseStatus == "" { | ||
| j.Continuous.PauseStatus = paused | ||
| } | ||
| if j.Trigger != nil && j.Trigger.PauseStatus == "" { | ||
| j.Trigger.PauseStatus = paused | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Pipelines presets: Prefix, PipelinesDevelopment | ||
| for i := range r.Pipelines { | ||
| r.Pipelines[i].Name = prefix + r.Pipelines[i].Name | ||
| if config.IsExplicitlyEnabled(t.PipelinesDevelopment) { | ||
| r.Pipelines[i].Development = true | ||
| } | ||
| if t.TriggerPauseStatus == config.Paused { | ||
| r.Pipelines[i].Continuous = false | ||
| } | ||
|
|
||
| // As of 2024-06, pipelines don't yet support tags | ||
| } | ||
|
|
||
| // Models presets: Prefix, Tags | ||
| for _, m := range r.Models { | ||
| m.Name = prefix + m.Name | ||
| for _, t := range tags { | ||
| exists := slices.ContainsFunc(m.Tags, func(modelTag ml.ModelTag) bool { | ||
| return modelTag.Key == t.Key | ||
| }) | ||
| if !exists { | ||
| // Only add this tag if the resource didn't include any tag that overrides its value. | ||
| m.Tags = append(m.Tags, ml.ModelTag{Key: t.Key, Value: t.Value}) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Experiments presets: Prefix, Tags | ||
| for _, e := range r.Experiments { | ||
| filepath := e.Name | ||
| dir := path.Dir(filepath) | ||
| base := path.Base(filepath) | ||
| if dir == "." { | ||
| e.Name = prefix + base | ||
| } else { | ||
| e.Name = dir + "/" + prefix + base | ||
| } | ||
| for _, t := range tags { | ||
| exists := false | ||
| for _, experimentTag := range e.Tags { | ||
| if experimentTag.Key == t.Key { | ||
| exists = true | ||
| break | ||
| } | ||
| } | ||
| if !exists { | ||
| e.Tags = append(e.Tags, ml.ExperimentTag{Key: t.Key, Value: t.Value}) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Model serving endpoint presets: Prefix | ||
| for i := range r.ModelServingEndpoints { | ||
| r.ModelServingEndpoints[i].Name = normalizePrefix(prefix) + r.ModelServingEndpoints[i].Name | ||
|
|
||
| // As of 2024-06, model serving endpoints don't yet support tags | ||
| } | ||
|
|
||
| // Registered models presets: Prefix | ||
| for i := range r.RegisteredModels { | ||
| r.RegisteredModels[i].Name = normalizePrefix(prefix) + r.RegisteredModels[i].Name | ||
|
|
||
| // As of 2024-06, registered models don't yet support tags | ||
| } | ||
|
|
||
| // Quality monitors presets: Prefix | ||
| if t.TriggerPauseStatus == config.Paused { | ||
| for i := range r.QualityMonitors { | ||
| // Remove all schedules from monitors, since they don't support pausing/unpausing. | ||
| // Quality monitors might support the "pause" property in the future, so at the | ||
| // CLI level we do respect that property if it is set to "unpaused." | ||
| if r.QualityMonitors[i].Schedule != nil && r.QualityMonitors[i].Schedule.PauseStatus != catalog.MonitorCronSchedulePauseStatusUnpaused { | ||
| r.QualityMonitors[i].Schedule = nil | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func validatePauseStatus(b *bundle.Bundle) diag.Diagnostics { | ||
| p := b.Config.Presets.TriggerPauseStatus | ||
| if p == "" || p == config.Paused || p == config.Unpaused { | ||
| return nil | ||
| } | ||
| return diag.Diagnostics{{ | ||
| Summary: "Invalid value for trigger_pause_status, should be PAUSED or UNPAUSED", | ||
|
pietern marked this conversation as resolved.
Outdated
|
||
| Severity: diag.Error, | ||
| Location: b.Config.GetLocation("presets.trigger_pause_status"), | ||
| }} | ||
| } | ||
|
|
||
| // toTagArray converts a map of tags to an array of tags. | ||
| // We sort tags so ensure stable ordering. | ||
| func toTagArray(tags map[string]string) []Tag { | ||
| var tagArray []Tag | ||
| if tags == nil { | ||
| return tagArray | ||
| } | ||
| for key, value := range tags { | ||
| tagArray = append(tagArray, Tag{Key: key, Value: value}) | ||
| } | ||
| sort.Slice(tagArray, func(i, j int) bool { | ||
| return tagArray[i].Key < tagArray[j].Key | ||
| }) | ||
| return tagArray | ||
| } | ||
|
|
||
| // normalizePrefix prefixes strings like '[dev lennart] ' to 'dev_lennart_'. | ||
| // We leave unicode letters and numbers but remove all "special characters." | ||
| func normalizePrefix(prefix string) string { | ||
| prefix = strings.ReplaceAll(prefix, "[", "") | ||
| prefix = strings.Trim(prefix, " ") | ||
|
|
||
| // If the prefix ends with a ']', we add an underscore to the end. | ||
| // This makes sure that we get names like "dev_user_endpoint" instead of "dev_userendpoint" | ||
| suffix := "" | ||
| if strings.HasSuffix(prefix, "]") { | ||
| suffix = "_" | ||
| } | ||
|
|
||
| return textutil.NormalizeString(prefix) + suffix | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.