-
Notifications
You must be signed in to change notification settings - Fork 184
Add command to open a resource in the browser #1846
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
Merged
Merged
Changes from 1 commit
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
84a8698
Add command to open the web UI for a resource
pietern 15dc49f
Update how resource lookup is done to account for duplicates
pietern 57cf0d6
Short help
pietern e6a8d8a
Add lookup by fully qualified name
pietern 9a0f169
Rename test
pietern 94d3b5e
Simplify test
pietern 1c55402
Make Lookup return Reference object
pietern 03e9b5f
Simplify test
pietern File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package resources | ||
|
|
||
| import ( | ||
| "github.com/databricks/cli/bundle" | ||
| "golang.org/x/exp/maps" | ||
| ) | ||
|
|
||
| func CompletionMap(b *bundle.Bundle) map[string]string { | ||
| out := make(map[string]string) | ||
| keyOnly, keyWithType := Keys(b) | ||
|
|
||
| // Keep track of resources we have seen by their fully qualified key. | ||
| seen := make(map[string]bool) | ||
|
|
||
| // First add resources that can be identified by key alone. | ||
| for k, v := range keyOnly { | ||
| // Invariant: len(v) >= 1. See [ResourceKeys]. | ||
| if len(v) == 1 { | ||
| seen[v[0].key] = true | ||
| out[k] = v[0].resource.GetName() | ||
| } | ||
| } | ||
|
|
||
| // Then add resources that can only be identified by their type and key. | ||
| for k, v := range keyWithType { | ||
| // Invariant: len(v) == 1. See [ResourceKeys]. | ||
| _, ok := seen[v[0].key] | ||
| if ok { | ||
| continue | ||
| } | ||
| out[k] = v[0].resource.GetName() | ||
| } | ||
|
|
||
| return out | ||
| } | ||
|
|
||
| func Completions(b *bundle.Bundle) []string { | ||
| return maps.Keys(CompletionMap(b)) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| package resources |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package resources | ||
|
|
||
| import ( | ||
| "fmt" | ||
|
|
||
| "github.com/databricks/cli/bundle" | ||
| "github.com/databricks/cli/bundle/config" | ||
| ) | ||
|
|
||
| type pair struct { | ||
| key string | ||
| resource config.ConfigResource | ||
| } | ||
|
|
||
| // lookup maps identifiers to a list of resources that match that identifier. | ||
| // The list can have more than 1 entry if resources of different types use the | ||
| // same key. When this happens, the user should disambiguate between them. | ||
| type lookup map[string][]pair | ||
|
|
||
| // Keys computes maps that index resources by their key (e.g. `my_job`) and by their key | ||
| // prefixed by their type (e.g. `jobs.my_job`). The resource key alone may be ambiguous (it is | ||
| // possible for resources of different types to have the same key), but the key prefixed by | ||
| // the type is guaranteed to be unique. | ||
| func Keys(b *bundle.Bundle) (keyOnly lookup, keyWithType lookup) { | ||
| keyOnly = make(lookup) | ||
| keyWithType = make(lookup) | ||
|
|
||
| // Collect all resources by their key and prefixed key. | ||
| for _, group := range b.Config.Resources.AllResources() { | ||
| typ := group.Description.PluralName | ||
| for k, v := range group.Resources { | ||
| kt := fmt.Sprintf("%s.%s", typ, k) | ||
| p := pair{key: kt, resource: v} | ||
| keyOnly[k] = append(keyOnly[k], p) | ||
| keyWithType[kt] = append(keyWithType[kt], p) | ||
| } | ||
| } | ||
|
|
||
| return | ||
| } | ||
|
|
||
| // Lookup returns the resource with the given key. | ||
| // It first attempts to find a resource with the key alone. | ||
| // If this fails, it tries the key prefixed by the resource type. | ||
| // If this also fails, it returns an error. | ||
| func Lookup(b *bundle.Bundle, key string) (config.ConfigResource, error) { | ||
| keyOnly, keyWithType := Keys(b) | ||
|
|
||
| // First try to find the resource by key alone. | ||
| if res, ok := keyOnly[key]; ok { | ||
| if len(res) == 1 { | ||
| return res[0].resource, nil | ||
| } | ||
| } | ||
|
|
||
| // Then try to find the resource by key and type. | ||
| if res, ok := keyWithType[key]; ok { | ||
| if len(res) == 1 { | ||
| return res[0].resource, nil | ||
| } | ||
| } | ||
|
|
||
| return nil, fmt.Errorf("resource with key %q not found", key) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| package resources |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| package bundle | ||
|
|
||
| import ( | ||
| "errors" | ||
| "fmt" | ||
| "os" | ||
| "path/filepath" | ||
|
|
||
| "github.com/databricks/cli/bundle" | ||
| "github.com/databricks/cli/bundle/config/mutator" | ||
| "github.com/databricks/cli/bundle/deploy/terraform" | ||
| "github.com/databricks/cli/bundle/phases" | ||
| "github.com/databricks/cli/bundle/resources" | ||
| "github.com/databricks/cli/cmd/bundle/utils" | ||
| "github.com/databricks/cli/cmd/root" | ||
| "github.com/databricks/cli/libs/cmdio" | ||
| "github.com/spf13/cobra" | ||
|
|
||
| "github.com/pkg/browser" | ||
| ) | ||
|
|
||
| func newOpenCommand() *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "open", | ||
| Short: "Open the web UI for a resource", | ||
| Args: root.MaximumNArgs(1), | ||
| } | ||
|
|
||
| var forcePull bool | ||
| cmd.Flags().BoolVar(&forcePull, "force-pull", false, "Skip local cache and load the state from the remote workspace") | ||
|
|
||
| cmd.RunE = func(cmd *cobra.Command, args []string) error { | ||
| ctx := cmd.Context() | ||
| b, diags := utils.ConfigureBundleWithVariables(cmd) | ||
| if err := diags.Error(); err != nil { | ||
| return diags.Error() | ||
| } | ||
|
|
||
| diags = bundle.Apply(ctx, b, phases.Initialize()) | ||
| if err := diags.Error(); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // If no arguments are specified, prompt the user to select something to run. | ||
| if len(args) == 0 && cmdio.IsPromptSupported(ctx) { | ||
| // Invert completions from KEY -> NAME, to NAME -> KEY. | ||
| inv := make(map[string]string) | ||
| for k, v := range resources.CompletionMap(b) { | ||
| inv[v] = k | ||
| } | ||
| id, err := cmdio.Select(ctx, inv, "Resource to open") | ||
| if err != nil { | ||
| return err | ||
| } | ||
| args = append(args, id) | ||
| } | ||
|
|
||
| if len(args) < 1 { | ||
| return fmt.Errorf("expected a KEY of the resource to open") | ||
| } | ||
|
|
||
| cacheDir, err := terraform.Dir(ctx, b) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| _, stateFileErr := os.Stat(filepath.Join(cacheDir, terraform.TerraformStateFileName)) | ||
| _, configFileErr := os.Stat(filepath.Join(cacheDir, terraform.TerraformConfigFileName)) | ||
| noCache := errors.Is(stateFileErr, os.ErrNotExist) || errors.Is(configFileErr, os.ErrNotExist) | ||
|
|
||
| if forcePull || noCache { | ||
| diags = bundle.Apply(ctx, b, bundle.Seq( | ||
| terraform.StatePull(), | ||
| terraform.Interpolate(), | ||
| terraform.Write(), | ||
| )) | ||
| if err := diags.Error(); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| diags = bundle.Apply(ctx, b, bundle.Seq( | ||
| terraform.Load(), | ||
| mutator.InitializeURLs(), | ||
| )) | ||
| if err := diags.Error(); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // Locate resource to open. | ||
| resource, err := resources.Lookup(b, args[0]) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // Confirm that the resource has a URL. | ||
| url := resource.GetURL() | ||
| if url == "" { | ||
| return errors.New("resource does not have a URL associated with it (has it been deployed?)") | ||
| } | ||
|
|
||
| return browser.OpenURL(url) | ||
| } | ||
|
|
||
| cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { | ||
| b, diags := root.MustConfigureBundle(cmd) | ||
| if err := diags.Error(); err != nil { | ||
| cobra.CompErrorln(err.Error()) | ||
| return nil, cobra.ShellCompDirectiveError | ||
| } | ||
|
|
||
| // No completion in the context of a bundle. | ||
| // Source and destination paths are taken from bundle configuration. | ||
| if b == nil { | ||
| return nil, cobra.ShellCompDirectiveNoFileComp | ||
| } | ||
|
|
||
| if len(args) == 0 { | ||
| return resources.Completions(b), cobra.ShellCompDirectiveNoFileComp | ||
| } else { | ||
| return nil, cobra.ShellCompDirectiveNoFileComp | ||
| } | ||
| } | ||
|
|
||
| return cmd | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Shall we add the case if len > 1 then we return the error that the resource is ambiguous?
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.
Done.
I looked through the commit history and found that we added validation of uniqueness early last year. This makes the probability of seeing ambiguous keys very low (I think it's still possible if a resource with a duplicate key is defined in the target overrides). Relevant PRs: #1614 and #332.