2023-03-29 18:44:19 +00:00
|
|
|
package databrickscfg
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-06-02 11:49:39 +00:00
|
|
|
"errors"
|
2023-03-29 18:44:19 +00:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
|
2023-05-16 16:35:39 +00:00
|
|
|
"github.com/databricks/cli/libs/log"
|
2023-03-29 18:44:19 +00:00
|
|
|
"github.com/databricks/databricks-sdk-go/config"
|
|
|
|
"gopkg.in/ini.v1"
|
|
|
|
)
|
|
|
|
|
|
|
|
var ResolveProfileFromHost = profileFromHostLoader{}
|
|
|
|
|
2023-06-02 11:49:39 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-03-29 18:44:19 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-06-02 11:49:39 +00:00
|
|
|
ctx := context.Background()
|
2023-04-03 19:33:21 +00:00
|
|
|
configFile, err := config.LoadFile(cfg.ConfigFile)
|
2023-03-29 18:44:19 +00:00
|
|
|
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)
|
2023-06-02 11:49:39 +00:00
|
|
|
match, err := findMatchingProfile(configFile, func(s *ini.Section) bool {
|
|
|
|
key, err := s.GetKey("host")
|
2023-03-29 18:44:19 +00:00
|
|
|
if err != nil {
|
2023-06-02 11:49:39 +00:00
|
|
|
log.Tracef(ctx, "section %s: %s", s.Name(), err)
|
|
|
|
return false
|
2023-03-29 18:44:19 +00:00
|
|
|
}
|
|
|
|
|
2023-06-02 11:49:39 +00:00
|
|
|
// Check if this section matches the normalized host
|
|
|
|
return normalizeHost(key.Value()) == host
|
|
|
|
})
|
|
|
|
if err == errNoMatchingProfiles {
|
2023-03-29 18:44:19 +00:00
|
|
|
return nil
|
|
|
|
}
|
2023-06-02 11:49:39 +00:00
|
|
|
if err, ok := err.(errMultipleProfiles); ok {
|
2023-03-29 18:44:19 +00:00
|
|
|
return fmt.Errorf(
|
2023-06-02 11:49:39 +00:00
|
|
|
"%s: %w: please set DATABRICKS_CONFIG_PROFILE to specify one",
|
|
|
|
host, err)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2023-03-29 18:44:19 +00:00
|
|
|
}
|
|
|
|
|
2023-06-02 11:49:39 +00:00
|
|
|
log.Debugf(ctx, "Loading profile %s because of host match", match.Name())
|
2023-03-29 18:44:19 +00:00
|
|
|
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
|
|
|
|
}
|