package databrickscfg import ( "context" "errors" "fmt" "os" "strings" "github.com/databricks/cli/libs/log" "github.com/databricks/databricks-sdk-go/config" "gopkg.in/ini.v1" ) var ResolveProfileFromHost = profileFromHostLoader{} var errNoMatchingProfiles = errors.New("no matching config profiles found") type errMultipleProfiles []string func (e errMultipleProfiles) Error() string { return fmt.Sprintf("multiple profiles matched: %s", strings.Join(e, ", ")) } func findMatchingProfile(configFile *config.File, matcher func(*ini.Section) bool) (*ini.Section, error) { // Look for sections in the configuration file that match the configured host. var matching []*ini.Section for _, section := range configFile.Sections() { if !matcher(section) { continue } matching = append(matching, section) } // If there are no matching sections, we don't do anything. if len(matching) == 0 { return nil, errNoMatchingProfiles } // If there are multiple matching sections, let the user know it is impossible // to unambiguously select a profile to use. if len(matching) > 1 { var names errMultipleProfiles for _, section := range matching { names = append(names, section.Name()) } return nil, names } return matching[0], nil } type profileFromHostLoader struct{} func (l profileFromHostLoader) Name() string { return "resolve-profile-from-host" } func (l profileFromHostLoader) Configure(cfg *config.Config) error { // Skip an attempt to resolve a profile from the host if any authentication // is already configured (either directly, through environment variables, or // if a profile was specified). if cfg.Host == "" || l.isAnyAuthConfigured(cfg) { return nil } ctx := context.Background() configFile, err := config.LoadFile(cfg.ConfigFile) if err != nil { if os.IsNotExist(err) { return nil } return fmt.Errorf("cannot parse config file: %w", err) } // Normalized version of the configured host. host := normalizeHost(cfg.Host) match, err := findMatchingProfile(configFile, func(s *ini.Section) bool { if cfg.Profile != "" { return cfg.Profile == s.Name() } key, err := s.GetKey("host") if err != nil { log.Tracef(ctx, "section %s: %s", s.Name(), err) return false } // Check if this section matches the normalized host return normalizeHost(key.Value()) == host }) if err == errNoMatchingProfiles { return nil } if err, ok := err.(errMultipleProfiles); ok { return fmt.Errorf( "%s: %w: please set DATABRICKS_CONFIG_PROFILE to specify one", host, err) } if err != nil { return err } log.Debugf(ctx, "Loading profile %s because of host match", match.Name()) err = config.ConfigAttributes.ResolveFromStringMap(cfg, match.KeysHash()) if err != nil { return fmt.Errorf("%s %s profile: %w", configFile.Path(), match.Name(), err) } return nil } func (l profileFromHostLoader) isAnyAuthConfigured(cfg *config.Config) bool { for _, a := range config.ConfigAttributes { if a.Auth == "" { continue } if !a.IsZero(cfg) { return true } } return false }