Skip to content

Commit

Permalink
Merge pull request rancher#67 from rawmind0/bootstrap
Browse files Browse the repository at this point in the history
Updated provider config validation to enable bootstrap and resource creation at same run
  • Loading branch information
rawmind0 authored Aug 6, 2019
2 parents f94da44 + 46d56a9 commit 8881694
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 28 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
11 changes: 11 additions & 0 deletions rancher2/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package rancher2

import (
"fmt"
"sync"

"github.com/rancher/norman/clientbase"
"github.com/rancher/norman/types"
Expand Down Expand Up @@ -29,6 +30,7 @@ type Config struct {
Bootstrap bool `json:"bootstrap"`
ClusterID string `json:"clusterId"`
ProjectID string `json:"projectId"`
Sync sync.Mutex
Client Client
}

Expand Down Expand Up @@ -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
}
Expand All @@ -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")
}
Expand All @@ -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")
}
Expand Down
41 changes: 23 additions & 18 deletions rancher2/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand All @@ -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{
Expand All @@ -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{
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion rancher2/resource_rancher2_bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func init() {
testAccRancher2ProviderConfig = `
provider "rancher2" {
bootstrap = true
token_key = ""
token_key = "` + providerDefaulEmptyString + `"
}
`

Expand Down
54 changes: 50 additions & 4 deletions website/docs/index.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,67 @@ 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}"
secret_key = "${var.rancher2_secret_key}"
}
```

```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:
Expand Down
26 changes: 21 additions & 5 deletions website/docs/r/bootstrap.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@ 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.
- Set `server-url` setting, based on `api_url`.
- 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`.
Expand All @@ -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:
Expand Down

0 comments on commit 8881694

Please sign in to comment.