From f8aef8b911221ff76ff07520b51c5058553185dd Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Mon, 15 Jan 2018 11:20:59 -0500 Subject: [PATCH] Ability to specify OS_* variables for OpenStack configuration When we convert the OpenStack cloud provider to run in an external process, we should be able to use kubernetes Secrets capability to inject the OS_* variables. This way we can specify the cloud configuration as a configmap, specify secrets for the userid/password information. The configmap can be mounted as a file. the secrets can be made available as environment variables. the external controller itself can run as a pod/daemonset. For backward compat, we preload all the OS_* variables, if anything is in the config file, then that overrides the environment variables. --- .../providers/openstack/openstack.go | 41 ++++++++++- .../providers/openstack/openstack_test.go | 72 ++++++------------- 2 files changed, 61 insertions(+), 52 deletions(-) diff --git a/pkg/cloudprovider/providers/openstack/openstack.go b/pkg/cloudprovider/providers/openstack/openstack.go index 759412a9f01..ab82bb39084 100644 --- a/pkg/cloudprovider/providers/openstack/openstack.go +++ b/pkg/cloudprovider/providers/openstack/openstack.go @@ -23,6 +23,7 @@ import ( "io" "io/ioutil" "net/http" + "os" "regexp" "strings" "time" @@ -180,12 +181,50 @@ func (cfg Config) toAuth3Options() tokens3.AuthOptions { } } +// configFromEnv allows setting up credentials etc using the +// standard OS_* OpenStack client environment variables. +func configFromEnv() (cfg Config, ok bool) { + cfg.Global.AuthUrl = os.Getenv("OS_AUTH_URL") + cfg.Global.Username = os.Getenv("OS_USERNAME") + cfg.Global.Password = os.Getenv("OS_PASSWORD") + cfg.Global.Region = os.Getenv("OS_REGION_NAME") + + cfg.Global.TenantId = os.Getenv("OS_TENANT_ID") + if cfg.Global.TenantId == "" { + cfg.Global.TenantId = os.Getenv("OS_PROJECT_ID") + } + cfg.Global.TenantName = os.Getenv("OS_TENANT_NAME") + if cfg.Global.TenantName == "" { + cfg.Global.TenantName = os.Getenv("OS_PROJECT_NAME") + } + + cfg.Global.DomainId = os.Getenv("OS_DOMAIN_ID") + if cfg.Global.DomainId == "" { + cfg.Global.DomainId = os.Getenv("OS_USER_DOMAIN_ID") + } + cfg.Global.DomainName = os.Getenv("OS_DOMAIN_NAME") + if cfg.Global.DomainName == "" { + cfg.Global.DomainName = os.Getenv("OS_USER_DOMAIN_NAME") + } + + ok = cfg.Global.AuthUrl != "" && + cfg.Global.Username != "" && + cfg.Global.Password != "" && + (cfg.Global.TenantId != "" || cfg.Global.TenantName != "" || + cfg.Global.DomainId != "" || cfg.Global.DomainName != "") + + cfg.Metadata.SearchOrder = fmt.Sprintf("%s,%s", configDriveID, metadataID) + cfg.BlockStorage.BSVersion = "auto" + + return +} + func readConfig(config io.Reader) (Config, error) { if config == nil { return Config{}, fmt.Errorf("no OpenStack cloud provider config file given") } - var cfg Config + cfg, _ := configFromEnv() // Set default values for config params cfg.BlockStorage.BSVersion = "auto" diff --git a/pkg/cloudprovider/providers/openstack/openstack_test.go b/pkg/cloudprovider/providers/openstack/openstack_test.go index 168f6d301b7..5d82069dcac 100644 --- a/pkg/cloudprovider/providers/openstack/openstack_test.go +++ b/pkg/cloudprovider/providers/openstack/openstack_test.go @@ -90,10 +90,17 @@ func TestReadConfig(t *testing.T) { t.Errorf("Should fail when no config is provided: %s", err) } + os.Setenv("OS_PASSWORD", "mypass") + defer os.Unsetenv("OS_PASSWORD") + + os.Setenv("OS_TENANT_NAME", "admin") + defer os.Unsetenv("OS_TENANT_NAME") + cfg, err := readConfig(strings.NewReader(` [Global] auth-url = http://auth.url - username = user + user-id = user + tenant-name = demo [LoadBalancer] create-monitor = yes monitor-delay = 1m @@ -113,6 +120,19 @@ func TestReadConfig(t *testing.T) { t.Errorf("incorrect authurl: %s", cfg.Global.AuthUrl) } + if cfg.Global.UserId != "user" { + t.Errorf("incorrect userid: %s", cfg.Global.UserId) + } + + if cfg.Global.Password != "mypass" { + t.Errorf("incorrect password: %s", cfg.Global.Password) + } + + // config file wins over environment variable + if cfg.Global.TenantName != "demo" { + t.Errorf("incorrect tenant name: %s", cfg.Global.TenantName) + } + if !cfg.LoadBalancer.CreateMonitor { t.Errorf("incorrect lb.createmonitor: %t", cfg.LoadBalancer.CreateMonitor) } @@ -377,56 +397,6 @@ func TestNodeAddresses(t *testing.T) { } } -// This allows acceptance testing against an existing OpenStack -// install, using the standard OS_* OpenStack client environment -// variables. -// FIXME: it would be better to hermetically test against canned JSON -// requests/responses. -func configFromEnv() (cfg Config, ok bool) { - cfg.Global.AuthUrl = os.Getenv("OS_AUTH_URL") - - cfg.Global.TenantId = os.Getenv("OS_TENANT_ID") - // Rax/nova _insists_ that we don't specify both tenant ID and name - if cfg.Global.TenantId == "" { - cfg.Global.TenantName = os.Getenv("OS_TENANT_NAME") - } - - cfg.Global.Username = os.Getenv("OS_USERNAME") - cfg.Global.Password = os.Getenv("OS_PASSWORD") - cfg.Global.Region = os.Getenv("OS_REGION_NAME") - - cfg.Global.TenantName = os.Getenv("OS_TENANT_NAME") - if cfg.Global.TenantName == "" { - cfg.Global.TenantName = os.Getenv("OS_PROJECT_NAME") - } - - cfg.Global.TenantId = os.Getenv("OS_TENANT_ID") - if cfg.Global.TenantId == "" { - cfg.Global.TenantId = os.Getenv("OS_PROJECT_ID") - } - - cfg.Global.DomainId = os.Getenv("OS_DOMAIN_ID") - if cfg.Global.DomainId == "" { - cfg.Global.DomainId = os.Getenv("OS_USER_DOMAIN_ID") - } - - cfg.Global.DomainName = os.Getenv("OS_DOMAIN_NAME") - if cfg.Global.DomainName == "" { - cfg.Global.DomainName = os.Getenv("OS_USER_DOMAIN_NAME") - } - - ok = (cfg.Global.AuthUrl != "" && - cfg.Global.Username != "" && - cfg.Global.Password != "" && - (cfg.Global.TenantId != "" || cfg.Global.TenantName != "" || - cfg.Global.DomainId != "" || cfg.Global.DomainName != "")) - - cfg.Metadata.SearchOrder = fmt.Sprintf("%s,%s", configDriveID, metadataID) - cfg.BlockStorage.BSVersion = "auto" - - return -} - func TestNewOpenStack(t *testing.T) { cfg, ok := configFromEnv() if !ok {