diff --git a/CHANGELOG.md b/CHANGELOG.md index f7887d420..42d164946 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ ENHANCEMENTS: * Updated `github.com/rancher/types` and `github.com/rancher/norman` go modules and vendor files to support rancher v2.2.6 * Updated rancher to v2.2.6 and k3s to v0.7.0 on acceptance tests * Added cluster and project scope support on `rancher2_catalog` resource and datasource +* Updated `provider` config validation to enable bootstrap and resource creation at same run BUG FIXES: diff --git a/rancher2/config.go b/rancher2/config.go index bf406723c..f33ccba35 100644 --- a/rancher2/config.go +++ b/rancher2/config.go @@ -2,6 +2,7 @@ package rancher2 import ( "fmt" + "sync" "github.com/rancher/norman/clientbase" "github.com/rancher/norman/types" @@ -29,6 +30,7 @@ type Config struct { Bootstrap bool `json:"bootstrap"` ClusterID string `json:"clusterId"` ProjectID string `json:"projectId"` + Sync sync.Mutex Client Client } @@ -69,6 +71,9 @@ func (c *Config) UpdateToken(token string) error { // ManagementClient creates a Rancher client scoped to the management API func (c *Config) ManagementClient() (*managementClient.Client, error) { + c.Sync.Lock() + defer c.Sync.Unlock() + if c.Client.Management != nil { return c.Client.Management, nil } @@ -87,6 +92,9 @@ func (c *Config) ManagementClient() (*managementClient.Client, error) { // ClusterClient creates a Rancher client scoped to a Cluster API func (c *Config) ClusterClient(id string) (*clusterClient.Client, error) { + c.Sync.Lock() + defer c.Sync.Unlock() + if id == "" { return nil, fmt.Errorf("[ERROR] Rancher Cluster Client: cluster ID is nil") } @@ -111,6 +119,9 @@ func (c *Config) ClusterClient(id string) (*clusterClient.Client, error) { // ProjectClient creates a Rancher client scoped to a Project API func (c *Config) ProjectClient(id string) (*projectClient.Client, error) { + c.Sync.Lock() + defer c.Sync.Unlock() + if id == "" { return nil, fmt.Errorf("[ERROR] Rancher Project Client: project ID is nil") } diff --git a/rancher2/provider.go b/rancher2/provider.go index 92b640ef1..edbf9f071 100644 --- a/rancher2/provider.go +++ b/rancher2/provider.go @@ -7,6 +7,10 @@ import ( "github.com/hashicorp/terraform/terraform" ) +const ( + providerDefaulEmptyString = "nil" +) + // CLIConfig used to store data from file. type CLIConfig struct { AdminPass string `json:"adminpass"` @@ -27,14 +31,14 @@ func Provider() terraform.ResourceProvider { "api_url": &schema.Schema{ Type: schema.TypeString, Required: true, - DefaultFunc: schema.EnvDefaultFunc("RANCHER_URL", ""), + DefaultFunc: schema.EnvDefaultFunc("RANCHER_URL", providerDefaulEmptyString), Description: descriptions["api_url"], }, "access_key": &schema.Schema{ Type: schema.TypeString, Optional: true, Sensitive: true, - DefaultFunc: schema.EnvDefaultFunc("RANCHER_ACCESS_KEY", ""), + DefaultFunc: schema.EnvDefaultFunc("RANCHER_ACCESS_KEY", providerDefaulEmptyString), Description: descriptions["access_key"], }, "bootstrap": &schema.Schema{ @@ -47,14 +51,14 @@ func Provider() terraform.ResourceProvider { Type: schema.TypeString, Optional: true, Sensitive: true, - DefaultFunc: schema.EnvDefaultFunc("RANCHER_SECRET_KEY", ""), + DefaultFunc: schema.EnvDefaultFunc("RANCHER_SECRET_KEY", providerDefaulEmptyString), Description: descriptions["secret_key"], }, "token_key": &schema.Schema{ Type: schema.TypeString, Optional: true, Sensitive: true, - DefaultFunc: schema.EnvDefaultFunc("RANCHER_TOKEN_KEY", ""), + DefaultFunc: schema.EnvDefaultFunc("RANCHER_TOKEN_KEY", providerDefaulEmptyString), Description: descriptions["token_key"], }, "ca_certs": &schema.Schema{ @@ -153,12 +157,8 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { insecure := d.Get("insecure").(bool) bootstrap := d.Get("bootstrap").(bool) - if apiURL == "" { - return &Config{}, fmt.Errorf("[ERROR] No api_url provided") - } - config := &Config{ - URL: NormalizeURL(apiURL), + URL: apiURL, AccessKey: accessKey, SecretKey: secretKey, TokenKey: tokenKey, @@ -167,21 +167,26 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { Bootstrap: bootstrap, } - // If bootstrap tokenkey accesskey nor secretkey can be provided - if bootstrap { - if config.TokenKey != "" || config.AccessKey != "" || config.SecretKey != "" { + return providerValidateConfig(config) +} + +func providerValidateConfig(config *Config) (*Config, error) { + if config.URL == providerDefaulEmptyString { + return &Config{}, fmt.Errorf("[ERROR] No api_url provided") + } + + config.URL = NormalizeURL(config.URL) + + if config.Bootstrap { + // If bootstrap tokenkey accesskey nor secretkey can be provided + if config.TokenKey != providerDefaulEmptyString || config.AccessKey != providerDefaulEmptyString || config.SecretKey != providerDefaulEmptyString { return &Config{}, fmt.Errorf("[ERROR] Bootsrap mode activated. Token_key or access_key and secret_key can not be provided") } } else { // Else token or access key and secret key should be provided - if config.TokenKey == "" && (config.AccessKey == "" || config.SecretKey == "") { + if config.TokenKey == providerDefaulEmptyString && (config.AccessKey == providerDefaulEmptyString || config.SecretKey == providerDefaulEmptyString) { return &Config{}, fmt.Errorf("[ERROR] No token_key nor access_key and secret_key are provided") } - - _, err := config.ManagementClient() - if err != nil { - return &Config{}, err - } } return config, nil diff --git a/rancher2/resource_rancher2_bootstrap_test.go b/rancher2/resource_rancher2_bootstrap_test.go index 183dc9dea..72c9c57f0 100644 --- a/rancher2/resource_rancher2_bootstrap_test.go +++ b/rancher2/resource_rancher2_bootstrap_test.go @@ -27,7 +27,7 @@ func init() { testAccRancher2ProviderConfig = ` provider "rancher2" { bootstrap = true - token_key = "" + token_key = "` + providerDefaulEmptyString + `" } ` diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index f4b824c3f..e5a9c6798 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -9,14 +9,16 @@ description: |- # Rancher2 Provider The Rancher2 provider is used to interact with the -resources supported by Rancher v2. The provider needs to be configured -with the URL of the Rancher server at minimum and API credentials if -access control is enabled on the server. +resources supported by Rancher v2. + +The provider can be configured in 2 modes: +- Admin: this is the default mode, indeed to manage rancher2 resources. It should be configured with the `api_url` of the Rancher server and API credentials, `token_key` or `access_key` and `secret_key`. +- Bootstrap: this mode is indeed to bootstrap a rancher2 system. It is enabled if `bootstrap = true`. In this mode, `token_key` or `access_key` and `secret_key` can not be provided. More info at [rancher2_bootstrap resource](r/bootstrap.html) ## Example Usage ```hcl -# Configure the Rancher2 provider +# Configure the Rancher2 provider to admin provider "rancher2" { api_url = "https://rancher.my-domain.com" access_key = "${var.rancher2_access_key}" @@ -24,6 +26,50 @@ provider "rancher2" { } ``` +```hcl +# Configure the Rancher2 provider to bootstrap +provider "rancher2" { + api_url = "https://rancher.my-domain.com" + bootstrap = true +} +``` + +```hcl +# Configure the Rancher2 provider to bootstrap and admin +# Provider config for bootstrap +provider "rancher2" { + alias = "bootstrap" + + api_url = "https://rancher.my-domain.com" + bootstrap = true +} + +# Create a new rancher2_bootstrap using bootstrap provider config +resource "rancher2_bootstrap" "admin" { + provider = "rancher2.bootstrap" + + password = "blahblah" + telemetry = true +} + +# Provider config for admin +provider "rancher2" { + alias = "admin" + + api_url = "${rancher2_bootstrap.admin.url}" + token_key = "${rancher2_bootstrap.admin.token}" + insecure = true +} + +# Create a new rancher2 resource using admin provider config +resource "rancher2_catalog" "foo" { + provider = "rancher2.admin" + + name = "test" + url = "http://foo.com:8080" +} +``` + ## Argument Reference The following arguments are supported: diff --git a/website/docs/r/bootstrap.html.markdown b/website/docs/r/bootstrap.html.markdown index 956c3146e..b7c2fddc7 100644 --- a/website/docs/r/bootstrap.html.markdown +++ b/website/docs/r/bootstrap.html.markdown @@ -8,7 +8,7 @@ description: |- # rancher2\_bootstrap -Provides a Rancher v2 bootstrap resource. This can be used to bootstrap rancher v2 environments and output information. +Provides a Rancher v2 bootstrap resource. This can be used to bootstrap rancher v2 environments and output information. It just works if `bootstrap` provider config is added to the tf file. More info at [rancher2 provider](../index.html) This resource is indeed to bootstrap a rancher system doing following tasks: - Update default admin password, provided by `password` or generating a random one. @@ -16,8 +16,6 @@ This resource is indeed to bootstrap a rancher system doing following tasks: - Set `telemetry-opt` setting. - Create a token for admin user with concrete TTL. -It just works if `bootstrap = true` is added to the provider configuration or exporting env variable `RANCHER_BOOTSTRAP=true`. In this mode, `token_key` or `access_key` and `secret_key` can not be provided. - Rancher2 admin password could be updated setting `password` field and applying this resource again. Rancher2 admin `token` could also be updated if `token_update` is set to true. Refresh resource function will check if token is expired. If it's expired, `token_update` will be set to true to force token regeneration on next `terraform apply`. @@ -27,19 +25,37 @@ Login to rancher2 is done using `token` first and if fails, using admin `current ## Example Usage ```hcl -# Provider config +# Provider bootstrap config provider "rancher2" { api_url = "https://rancher.my-domain.com" bootstrap = true } -# Create a new rancher2 Bootstrap +# Create a new rancher2_bootstrap resource "rancher2_bootstrap" "admin" { password = "blahblah" telemetry = true } ``` +```hcl +# Provider bootstrap config with alias +provider "rancher2" { + alias = "bootstrap" + + api_url = "https://rancher.my-domain.com" + bootstrap = true +} + +# Create a new rancher2_bootstrap using bootstrap provider config +resource "rancher2_bootstrap" "admin" { + provider = "rancher2.bootstrap" + + password = "blahblah" + telemetry = true +} +``` + ## Argument Reference The following arguments are supported: