11package auth
22
33import (
4- "context"
54 "encoding/json"
6- "errors"
75 "fmt"
8- "io/fs"
9- "net/http"
10- "net/url"
6+ "maps"
7+ "slices"
118 "strings"
129
1310 "github.com/databricks/cli/cmd/root"
14- "github.com/databricks/cli/libs/databrickscfg/profile"
11+ "github.com/databricks/cli/libs/auth"
12+ "github.com/databricks/cli/libs/cmdctx"
1513 "github.com/databricks/cli/libs/flags"
16- "github.com/databricks/databricks-sdk-go/config"
1714 "github.com/spf13/cobra"
18- "gopkg.in/ini.v1"
1915)
2016
21- func canonicalHost (host string ) (string , error ) {
22- parsedHost , err := url .Parse (host )
23- if err != nil {
24- return "" , err
25- }
26- // If the host is empty, assume the scheme wasn't included.
27- if parsedHost .Host == "" {
28- return "https://" + host , nil
29- }
30- return "https://" + parsedHost .Host , nil
31- }
32-
33- var ErrNoMatchingProfiles = errors .New ("no matching profiles found" )
34-
35- const shellQuotedSpecialChars = " \t \n \r \" \\ $`!#&|;(){}[]<>?*~'"
36-
37- func resolveSection (cfg * config.Config , iniFile * config.File ) (* ini.Section , error ) {
38- var candidates []* ini.Section
39- configuredHost , err := canonicalHost (cfg .Host )
40- if err != nil {
41- return nil , err
42- }
43- for _ , section := range iniFile .Sections () {
44- hash := section .KeysHash ()
45- host , ok := hash ["host" ]
46- if ! ok {
47- // if host is not set
48- continue
49- }
50- canonical , err := canonicalHost (host )
51- if err != nil {
52- // we're fine with other corrupt profiles
53- continue
54- }
55- if canonical != configuredHost {
56- continue
57- }
58- candidates = append (candidates , section )
59- }
60- if len (candidates ) == 0 {
61- return nil , ErrNoMatchingProfiles
62- }
63- // in the real situations, we don't expect this to happen often
64- // (if not at all), hence we don't trim the list
65- if len (candidates ) > 1 {
66- var profiles []string
67- for _ , v := range candidates {
68- profiles = append (profiles , v .Name ())
69- }
70- return nil , fmt .Errorf ("%s match %s in %s" ,
71- strings .Join (profiles , " and " ), cfg .Host , cfg .ConfigFile )
72- }
73- return candidates [0 ], nil
74- }
75-
76- func loadFromDatabricksCfg (ctx context.Context , cfg * config.Config ) error {
77- iniFile , err := profile .DefaultProfiler .Get (ctx )
78- if errors .Is (err , fs .ErrNotExist ) {
79- // it's fine not to have ~/.databrickscfg
80- return nil
81- }
82- if err != nil {
83- return err
84- }
85- profile , err := resolveSection (cfg , iniFile )
86- if err == ErrNoMatchingProfiles {
87- // it's also fine for Azure CLI or Databricks CLI, which
88- // are resolved by unified auth handling in the Go SDK.
89- return nil
90- }
91- if err != nil {
92- return err
93- }
94- cfg .Profile = profile .Name ()
95- return nil
96- }
97-
9817func newEnvCommand () * cobra.Command {
9918 cmd := & cobra.Command {
10019 Use : "env" ,
101- Short : "Get env" ,
20+ Short : "Get authentication environment variables for the current CLI context" ,
21+ Long : `Output the environment variables needed to authenticate as the same identity
22+ the CLI is currently authenticated as. This is useful for configuring downstream
23+ tools that accept Databricks authentication via environment variables.` ,
10224 }
10325
104- var host string
105- var profile string
106- cmd .Flags ().StringVar (& host , "host" , host , "Hostname to get auth env for" )
107- cmd .Flags ().StringVar (& profile , "profile" , profile , "Profile to get auth env for" )
108-
10926 cmd .RunE = func (cmd * cobra.Command , args []string ) error {
110- cfg := & config.Config {
111- Host : host ,
112- Profile : profile ,
113- }
114- if profile != "" {
115- cfg .Profile = profile
116- } else if cfg .Host == "" {
117- cfg .Profile = "DEFAULT"
118- } else if err := loadFromDatabricksCfg (cmd .Context (), cfg ); err != nil {
119- return err
120- }
121- // Go SDK is lazy loaded because of Terraform semantics,
122- // so we're creating a dummy HTTP request as a placeholder
123- // for headers.
124- r := & http.Request {Header : http.Header {}}
125- err := cfg .Authenticate (r .WithContext (cmd .Context ()))
27+ _ , err := root .MustAnyClient (cmd , args )
12628 if err != nil {
12729 return err
12830 }
31+
32+ cfg := cmdctx .ConfigUsed (cmd .Context ())
33+ envVars := auth .Env (cfg )
34+
12935 // Output KEY=VALUE lines when the user explicitly passes --output text.
13036 if cmd .Flag ("output" ).Changed && root .OutputType (cmd ) == flags .OutputText {
13137 w := cmd .OutOrStdout ()
132- for _ , a := range config .ConfigAttributes {
133- if a .IsZero (cfg ) {
134- continue
135- }
136- v := a .GetString (cfg )
137- for _ , envName := range a .EnvVars {
138- fmt .Fprintf (w , "%s=%s\n " , envName , quoteEnvValue (v ))
139- }
38+ keys := slices .Sorted (maps .Keys (envVars ))
39+ for _ , k := range keys {
40+ fmt .Fprintf (w , "%s=%s\n " , k , quoteEnvValue (envVars [k ]))
14041 }
14142 return nil
14243 }
14344
144- vars := collectEnvVars (cfg )
145- raw , err := json .MarshalIndent (map [string ]any {
146- "env" : vars ,
147- }, "" , " " )
45+ raw , err := json .MarshalIndent (envVars , "" , " " )
14846 if err != nil {
14947 return err
15048 }
@@ -155,25 +53,11 @@ func newEnvCommand() *cobra.Command {
15553 return cmd
15654}
15755
158- // collectEnvVars returns the environment variables for the given config
159- // as a map from env var name to value.
160- func collectEnvVars (cfg * config.Config ) map [string ]string {
161- vars := map [string ]string {}
162- for _ , a := range config .ConfigAttributes {
163- if a .IsZero (cfg ) {
164- continue
165- }
166- v := a .GetString (cfg )
167- for _ , envName := range a .EnvVars {
168- vars [envName ] = v
169- }
170- }
171- return vars
172- }
56+ const shellQuotedSpecialChars = " \t \n \r \" \\ $`!#&|;(){}[]<>?*~'"
17357
17458// quoteEnvValue quotes a value for KEY=VALUE output if it contains spaces or
17559// shell-special characters. Single quotes prevent shell expansion, and
176- // embedded single quotes use the POSIX-compatible '\” sequence.
60+ // embedded single quotes use the POSIX-compatible '\" sequence.
17761func quoteEnvValue (v string ) string {
17862 if v == "" {
17963 return `''`
0 commit comments