[Rancher] - allow for importing resources using environment ID to target (#11688)

This commit is contained in:
John Engelman 2017-02-05 04:35:48 -06:00 committed by Paul Stack
parent 64e2381cb2
commit 1efd5ff814
18 changed files with 256 additions and 61 deletions

View File

@ -7,31 +7,30 @@ import (
"github.com/raphink/go-rancher/catalog" "github.com/raphink/go-rancher/catalog"
) )
// Config is the configuration parameters for a Rancher API
type Config struct { type Config struct {
*rancherClient.RancherClient
APIURL string APIURL string
AccessKey string AccessKey string
SecretKey string SecretKey string
} }
// Create creates a generic Rancher client // GlobalClient creates a Rancher client scoped to the global API
func (c *Config) CreateClient() error { func (c *Config) GlobalClient() (*rancherClient.RancherClient, error) {
client, err := rancherClient.NewRancherClient(&rancherClient.ClientOpts{ client, err := rancherClient.NewRancherClient(&rancherClient.ClientOpts{
Url: c.APIURL, Url: c.APIURL,
AccessKey: c.AccessKey, AccessKey: c.AccessKey,
SecretKey: c.SecretKey, SecretKey: c.SecretKey,
}) })
if err != nil { if err != nil {
return err return nil, err
} }
log.Printf("[INFO] Rancher Client configured for url: %s", c.APIURL) log.Printf("[INFO] Rancher Client configured for url: %s", c.APIURL)
c.RancherClient = client return client, nil
return nil
} }
// EnvironmentClient creates a Rancher client scoped to an Environment's API
func (c *Config) EnvironmentClient(env string) (*rancherClient.RancherClient, error) { func (c *Config) EnvironmentClient(env string) (*rancherClient.RancherClient, error) {
url := c.APIURL + "/projects/" + env + "/schemas" url := c.APIURL + "/projects/" + env + "/schemas"
@ -49,8 +48,13 @@ func (c *Config) EnvironmentClient(env string) (*rancherClient.RancherClient, er
return client, nil return client, nil
} }
// RegistryClient creates a Rancher client scoped to a Registry's API
func (c *Config) RegistryClient(id string) (*rancherClient.RancherClient, error) { func (c *Config) RegistryClient(id string) (*rancherClient.RancherClient, error) {
reg, err := c.Registry.ById(id) client, err := c.GlobalClient()
if err != nil {
return nil, err
}
reg, err := client.Registry.ById(id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -58,6 +62,7 @@ func (c *Config) RegistryClient(id string) (*rancherClient.RancherClient, error)
return c.EnvironmentClient(reg.AccountId) return c.EnvironmentClient(reg.AccountId)
} }
// CatalogClient creates a Rancher client scoped to a Catalog's API
func (c *Config) CatalogClient() (*catalog.RancherClient, error) { func (c *Config) CatalogClient() (*catalog.RancherClient, error) {
url := c.APIURL + "-catalog/schemas" url := c.APIURL + "-catalog/schemas"

View File

@ -60,7 +60,7 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
SecretKey: d.Get("secret_key").(string), SecretKey: d.Get("secret_key").(string),
} }
err := config.CreateClient() _, err := config.GlobalClient()
return config, err return config, err
} }

View File

@ -47,7 +47,10 @@ func resourceRancherEnvironment() *schema.Resource {
func resourceRancherEnvironmentCreate(d *schema.ResourceData, meta interface{}) error { func resourceRancherEnvironmentCreate(d *schema.ResourceData, meta interface{}) error {
log.Printf("[INFO] Creating Environment: %s", d.Id()) log.Printf("[INFO] Creating Environment: %s", d.Id())
client := meta.(*Config) client, err := meta.(*Config).GlobalClient()
if err != nil {
return err
}
name := d.Get("name").(string) name := d.Get("name").(string)
description := d.Get("description").(string) description := d.Get("description").(string)
@ -87,7 +90,10 @@ func resourceRancherEnvironmentCreate(d *schema.ResourceData, meta interface{})
func resourceRancherEnvironmentRead(d *schema.ResourceData, meta interface{}) error { func resourceRancherEnvironmentRead(d *schema.ResourceData, meta interface{}) error {
log.Printf("[INFO] Refreshing Environment: %s", d.Id()) log.Printf("[INFO] Refreshing Environment: %s", d.Id())
client := meta.(*Config) client, err := meta.(*Config).GlobalClient()
if err != nil {
return err
}
env, err := client.Project.ById(d.Id()) env, err := client.Project.ById(d.Id())
if err != nil { if err != nil {
@ -110,13 +116,16 @@ func resourceRancherEnvironmentRead(d *schema.ResourceData, meta interface{}) er
d.Set("description", env.Description) d.Set("description", env.Description)
d.Set("name", env.Name) d.Set("name", env.Name)
d.Set("orchestration", GetActiveOrchestration(env)) d.Set("orchestration", getActiveOrchestration(env))
return nil return nil
} }
func resourceRancherEnvironmentUpdate(d *schema.ResourceData, meta interface{}) error { func resourceRancherEnvironmentUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Config) client, err := meta.(*Config).GlobalClient()
if err != nil {
return err
}
name := d.Get("name").(string) name := d.Get("name").(string)
description := d.Get("description").(string) description := d.Get("description").(string)
@ -145,7 +154,10 @@ func resourceRancherEnvironmentUpdate(d *schema.ResourceData, meta interface{})
func resourceRancherEnvironmentDelete(d *schema.ResourceData, meta interface{}) error { func resourceRancherEnvironmentDelete(d *schema.ResourceData, meta interface{}) error {
log.Printf("[INFO] Deleting Environment: %s", d.Id()) log.Printf("[INFO] Deleting Environment: %s", d.Id())
id := d.Id() id := d.Id()
client := meta.(*Config) client, err := meta.(*Config).GlobalClient()
if err != nil {
return err
}
env, err := client.Project.ById(id) env, err := client.Project.ById(id)
if err != nil { if err != nil {
@ -193,7 +205,7 @@ func setOrchestrationFields(orchestration string, data map[string]interface{}) {
// EnvironmentStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch // EnvironmentStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
// a Rancher Environment. // a Rancher Environment.
func EnvironmentStateRefreshFunc(client *Config, environmentID string) resource.StateRefreshFunc { func EnvironmentStateRefreshFunc(client *rancherClient.RancherClient, environmentID string) resource.StateRefreshFunc {
return func() (interface{}, string, error) { return func() (interface{}, string, error) {
env, err := client.Project.ById(environmentID) env, err := client.Project.ById(environmentID)

View File

@ -62,7 +62,10 @@ func TestAccRancherEnvironment_disappears(t *testing.T) {
func testAccRancherEnvironmentDisappears(env *rancherClient.Project) resource.TestCheckFunc { func testAccRancherEnvironmentDisappears(env *rancherClient.Project) resource.TestCheckFunc {
return func(s *terraform.State) error { return func(s *terraform.State) error {
client := testAccProvider.Meta().(*Config) client, err := testAccProvider.Meta().(*Config).GlobalClient()
if err != nil {
return err
}
if err := client.Project.Delete(env); err != nil { if err := client.Project.Delete(env); err != nil {
return fmt.Errorf("Error deleting Environment: %s", err) return fmt.Errorf("Error deleting Environment: %s", err)
} }
@ -96,7 +99,10 @@ func testAccCheckRancherEnvironmentExists(n string, env *rancherClient.Project)
return fmt.Errorf("No App Name is set") return fmt.Errorf("No App Name is set")
} }
client := testAccProvider.Meta().(*Config) client, err := testAccProvider.Meta().(*Config).GlobalClient()
if err != nil {
return err
}
foundEnv, err := client.Project.ById(rs.Primary.ID) foundEnv, err := client.Project.ById(rs.Primary.ID)
if err != nil { if err != nil {
@ -114,7 +120,10 @@ func testAccCheckRancherEnvironmentExists(n string, env *rancherClient.Project)
} }
func testAccCheckRancherEnvironmentDestroy(s *terraform.State) error { func testAccCheckRancherEnvironmentDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*Config) client, err := testAccProvider.Meta().(*Config).GlobalClient()
if err != nil {
return err
}
for _, rs := range s.RootModule().Resources { for _, rs := range s.RootModule().Resources {
if rs.Type != "rancher_environment" { if rs.Type != "rancher_environment" {

View File

@ -205,12 +205,21 @@ func resourceRancherRegistrationTokenDelete(d *schema.ResourceData, meta interfa
} }
func resourceRancherRegistrationTokenImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { func resourceRancherRegistrationTokenImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
client := meta.(*Config) envID, resourceID := splitID(d.Id())
regT, err := client.RegistrationToken.ById(d.Id()) d.SetId(resourceID)
if err != nil { if envID != "" {
return []*schema.ResourceData{}, err d.Set("environment_id", envID)
} else {
client, err := meta.(*Config).GlobalClient()
if err != nil {
return []*schema.ResourceData{}, err
}
token, err := client.RegistrationToken.ById(d.Id())
if err != nil {
return []*schema.ResourceData{}, err
}
d.Set("environment_id", token.AccountId)
} }
d.Set("environment_id", regT.AccountId)
return []*schema.ResourceData{d}, nil return []*schema.ResourceData{d}, nil
} }

View File

@ -136,7 +136,10 @@ func testAccCheckRancherRegistrationTokenExists(n string, regT *rancherClient.Re
return fmt.Errorf("No App Name is set") return fmt.Errorf("No App Name is set")
} }
client, _ := testAccProvider.Meta().(*Config).EnvironmentClient(rs.Primary.Attributes["environment_id"]) client, err := testAccProvider.Meta().(*Config).EnvironmentClient(rs.Primary.Attributes["environment_id"])
if err != nil {
return err
}
foundRegT, err := client.RegistrationToken.ById(rs.Primary.ID) foundRegT, err := client.RegistrationToken.ById(rs.Primary.ID)
if err != nil { if err != nil {
@ -154,12 +157,16 @@ func testAccCheckRancherRegistrationTokenExists(n string, regT *rancherClient.Re
} }
func testAccCheckRancherRegistrationTokenDestroy(s *terraform.State) error { func testAccCheckRancherRegistrationTokenDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*Config)
for _, rs := range s.RootModule().Resources { for _, rs := range s.RootModule().Resources {
if rs.Type != "rancher_registration_token" { if rs.Type != "rancher_registration_token" {
continue continue
} }
client, err := testAccProvider.Meta().(*Config).GlobalClient()
if err != nil {
return err
}
regT, err := client.RegistrationToken.ById(rs.Primary.ID) regT, err := client.RegistrationToken.ById(rs.Primary.ID)
if err == nil { if err == nil {

View File

@ -211,12 +211,21 @@ func resourceRancherRegistryDelete(d *schema.ResourceData, meta interface{}) err
} }
func resourceRancherRegistryImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { func resourceRancherRegistryImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
client := meta.(*Config) envID, resourceID := splitID(d.Id())
reg, err := client.Registry.ById(d.Id()) d.SetId(resourceID)
if err != nil { if envID != "" {
return []*schema.ResourceData{}, err d.Set("environment_id", envID)
} else {
client, err := meta.(*Config).GlobalClient()
if err != nil {
return []*schema.ResourceData{}, err
}
registry, err := client.Registry.ById(d.Id())
if err != nil {
return []*schema.ResourceData{}, err
}
d.Set("environment_id", registry.AccountId)
} }
d.Set("environment_id", reg.AccountId)
return []*schema.ResourceData{d}, nil return []*schema.ResourceData{d}, nil
} }

View File

@ -233,12 +233,21 @@ func resourceRancherRegistryCredentialDelete(d *schema.ResourceData, meta interf
} }
func resourceRancherRegistryCredentialImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { func resourceRancherRegistryCredentialImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
client := meta.(*Config) regID, resourceID := splitID(d.Id())
regC, err := client.RegistryCredential.ById(d.Id()) d.SetId(resourceID)
if err != nil { if regID != "" {
return []*schema.ResourceData{}, err d.Set("registry_id", regID)
} else {
client, err := meta.(*Config).GlobalClient()
if err != nil {
return []*schema.ResourceData{}, err
}
cred, err := client.RegistryCredential.ById(d.Id())
if err != nil {
return []*schema.ResourceData{}, err
}
d.Set("registry_id", cred.RegistryId)
} }
d.Set("environment_id", regC.AccountId)
return []*schema.ResourceData{d}, nil return []*schema.ResourceData{d}, nil
} }

View File

@ -129,7 +129,10 @@ func testAccCheckRancherRegistryCredentialExists(n string, reg *rancherClient.Re
return fmt.Errorf("No App Name is set") return fmt.Errorf("No App Name is set")
} }
client, _ := testAccProvider.Meta().(*Config).RegistryClient(rs.Primary.Attributes["registry_id"]) client, err := testAccProvider.Meta().(*Config).RegistryClient(rs.Primary.Attributes["registry_id"])
if err != nil {
return err
}
foundReg, err := client.RegistryCredential.ById(rs.Primary.ID) foundReg, err := client.RegistryCredential.ById(rs.Primary.ID)
if err != nil { if err != nil {
@ -147,12 +150,15 @@ func testAccCheckRancherRegistryCredentialExists(n string, reg *rancherClient.Re
} }
func testAccCheckRancherRegistryCredentialDestroy(s *terraform.State) error { func testAccCheckRancherRegistryCredentialDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*Config)
for _, rs := range s.RootModule().Resources { for _, rs := range s.RootModule().Resources {
if rs.Type != "rancher_registry_credential" { if rs.Type != "rancher_registry_credential" {
continue continue
} }
client, err := testAccProvider.Meta().(*Config).GlobalClient()
if err != nil {
return err
}
reg, err := client.RegistryCredential.ById(rs.Primary.ID) reg, err := client.RegistryCredential.ById(rs.Primary.ID)
if err == nil { if err == nil {

View File

@ -138,7 +138,10 @@ func testAccCheckRancherRegistryExists(n string, reg *rancherClient.Registry) re
return fmt.Errorf("No App Name is set") return fmt.Errorf("No App Name is set")
} }
client, _ := testAccProvider.Meta().(*Config).EnvironmentClient(rs.Primary.Attributes["environment_id"]) client, err := testAccProvider.Meta().(*Config).EnvironmentClient(rs.Primary.Attributes["environment_id"])
if err != nil {
return err
}
foundReg, err := client.Registry.ById(rs.Primary.ID) foundReg, err := client.Registry.ById(rs.Primary.ID)
if err != nil { if err != nil {
@ -156,12 +159,15 @@ func testAccCheckRancherRegistryExists(n string, reg *rancherClient.Registry) re
} }
func testAccCheckRancherRegistryDestroy(s *terraform.State) error { func testAccCheckRancherRegistryDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*Config)
for _, rs := range s.RootModule().Resources { for _, rs := range s.RootModule().Resources {
if rs.Type != "rancher_registry" { if rs.Type != "rancher_registry" {
continue continue
} }
client, err := testAccProvider.Meta().(*Config).GlobalClient()
if err != nil {
return err
}
reg, err := client.Registry.ById(rs.Primary.ID) reg, err := client.Registry.ById(rs.Primary.ID)
if err == nil { if err == nil {

View File

@ -156,10 +156,13 @@ func resourceRancherStackRead(d *schema.ResourceData, meta interface{}) error {
dockerCompose := strings.Replace(config.DockerComposeConfig, "\r", "", -1) dockerCompose := strings.Replace(config.DockerComposeConfig, "\r", "", -1)
rancherCompose := strings.Replace(config.RancherComposeConfig, "\r", "", -1) rancherCompose := strings.Replace(config.RancherComposeConfig, "\r", "", -1)
catalogId := d.Get("catalog_id") catalogID := d.Get("catalog_id")
if catalogId == "" { if catalogID == "" {
d.Set("docker_compose", dockerCompose) d.Set("docker_compose", dockerCompose)
d.Set("rancher_compose", rancherCompose) d.Set("rancher_compose", rancherCompose)
} else {
d.Set("docker_compose", "")
d.Set("rancher_compose", "")
} }
d.Set("rendered_docker_compose", dockerCompose) d.Set("rendered_docker_compose", dockerCompose)
d.Set("rendered_rancher_compose", rancherCompose) d.Set("rendered_rancher_compose", rancherCompose)
@ -203,7 +206,7 @@ func resourceRancherStackUpdate(d *schema.ResourceData, meta interface{}) error
} }
var newStack rancherClient.Environment var newStack rancherClient.Environment
if err := client.Update("environment", &stack.Resource, data, &newStack); err != nil { if err = client.Update("environment", &stack.Resource, data, &newStack); err != nil {
return err return err
} }
@ -334,12 +337,21 @@ func resourceRancherStackDelete(d *schema.ResourceData, meta interface{}) error
} }
func resourceRancherStackImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { func resourceRancherStackImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
client := meta.(*Config) envID, resourceID := splitID(d.Id())
stack, err := client.Environment.ById(d.Id()) d.SetId(resourceID)
if err != nil { if envID != "" {
return []*schema.ResourceData{}, err d.Set("environment_id", envID)
} else {
client, err := meta.(*Config).GlobalClient()
if err != nil {
return []*schema.ResourceData{}, err
}
stack, err := client.Environment.ById(d.Id())
if err != nil {
return []*schema.ResourceData{}, err
}
d.Set("environment_id", stack.AccountId)
} }
d.Set("environment_id", stack.AccountId)
return []*schema.ResourceData{d}, nil return []*schema.ResourceData{d}, nil
} }

View File

@ -177,7 +177,10 @@ func testAccCheckRancherStackExists(n string, stack *rancherClient.Environment)
return fmt.Errorf("No App Name is set") return fmt.Errorf("No App Name is set")
} }
client, _ := testAccProvider.Meta().(*Config).EnvironmentClient(rs.Primary.Attributes["environment_id"]) client, err := testAccProvider.Meta().(*Config).EnvironmentClient(rs.Primary.Attributes["environment_id"])
if err != nil {
return err
}
foundStack, err := client.Environment.ById(rs.Primary.ID) foundStack, err := client.Environment.ById(rs.Primary.ID)
if err != nil { if err != nil {
@ -216,12 +219,15 @@ func testAccCheckRancherStackAttributes(stack *rancherClient.Environment, enviro
} }
func testAccCheckRancherStackDestroy(s *terraform.State) error { func testAccCheckRancherStackDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*Config)
for _, rs := range s.RootModule().Resources { for _, rs := range s.RootModule().Resources {
if rs.Type != "rancher_stack" { if rs.Type != "rancher_stack" {
continue continue
} }
client, err := testAccProvider.Meta().(*Config).GlobalClient()
if err != nil {
return err
}
stack, err := client.Environment.ById(rs.Primary.ID) stack, err := client.Environment.ById(rs.Primary.ID)
if err == nil { if err == nil {

View File

@ -1,14 +1,18 @@
package rancher package rancher
import "github.com/rancher/go-rancher/client" import (
"strings"
"github.com/rancher/go-rancher/client"
)
const ( const (
REMOVED = "removed" stateRemoved = "removed"
PURGED = "purged" statePurged = "purged"
) )
// GetActiveOrchestration get the name of the active orchestration for a environment // GetActiveOrchestration get the name of the active orchestration for a environment
func GetActiveOrchestration(project *client.Project) string { func getActiveOrchestration(project *client.Project) string {
orch := "cattle" orch := "cattle"
switch { switch {
@ -24,5 +28,12 @@ func GetActiveOrchestration(project *client.Project) string {
} }
func removed(state string) bool { func removed(state string) bool {
return state == REMOVED || state == PURGED return state == stateRemoved || state == statePurged
}
func splitID(id string) (envID, resourceID string) {
if strings.Contains(id, "/") {
return id[0:strings.Index(id, "/")], id[strings.Index(id, "/")+1:]
}
return "", id
} }

View File

@ -0,0 +1,62 @@
package rancher
import (
"testing"
"github.com/rancher/go-rancher/client"
)
var idTests = []struct {
id string
envID string
resourceID string
}{
{"1a05", "", "1a05"},
{"1a05/1s234", "1a05", "1s234"},
}
func TestSplitId(t *testing.T) {
for _, tt := range idTests {
envID, resourceID := splitID(tt.id)
if envID != tt.envID || resourceID != tt.resourceID {
t.Errorf("splitId(%s) => [%s, %s]) want [%s, %s]", tt.id, envID, resourceID, tt.envID, tt.resourceID)
}
}
}
var stateTests = []struct {
state string
removed bool
}{
{"removed", true},
{"purged", true},
{"active", false},
}
func TestRemovedState(t *testing.T) {
for _, tt := range stateTests {
removed := removed(tt.state)
if removed != tt.removed {
t.Errorf("removed(%s) => %t, wants %t", tt.state, removed, tt.removed)
}
}
}
var orchestrationTests = []struct {
project *client.Project
orchestration string
}{
{&client.Project{}, "cattle"},
{&client.Project{Swarm: true}, "swarm"},
{&client.Project{Mesos: true}, "mesos"},
{&client.Project{Kubernetes: true}, "kubernetes"},
}
func TestActiveOrchestration(t *testing.T) {
for _, tt := range orchestrationTests {
orchestration := getActiveOrchestration(tt.project)
if orchestration != tt.orchestration {
t.Errorf("getActiveOrchestration(%+v) => %s, wants %s", tt.project, orchestration, tt.orchestration)
}
}
}

View File

@ -39,7 +39,15 @@ The following attributes are exported:
## Import ## Import
Registration tokens can be imported using their Rancher API ID, e.g. Registration tokens can be imported using the Environment and Registration token
IDs in the form `<environment_id>/<registration_token_id>`.
```
$ terraform import rancher_registration_token.dev_token 1a5/1c11
```
If the credentials for the Rancher provider have access to the global API, then
then `environment_id` can be omitted e.g.
``` ```
$ terraform import rancher_registration_token.dev_token 1c11 $ terraform import rancher_registration_token.dev_token 1c11

View File

@ -37,7 +37,15 @@ No further attributes are exported.
## Import ## Import
Registries can be imported using their Rancher API ID, e.g. Registries can be imported using the Environment and Registry IDs in the form
`<environment_id>/<registry_id>`
```
$ terraform import rancher_registry.private_registry 1a5/1sp31
```
If the credentials for the Rancher provider have access to the global API, then
then `environment_id` can be omitted e.g.
``` ```
$ terraform import rancher_registry.private_registry 1sp31 $ terraform import rancher_registry.private_registry 1sp31

View File

@ -41,7 +41,15 @@ No further attributes are exported.
## Import ## Import
Registry credentials can be imported using their Rancher API ID, e.g. Registry credentials can be imported using the Registry and credentials
IDs in the format `<registry_id>/<credential_id>`
```
$ terraform import rancher_registry_credential.private_registry 1sp31/1c605
```
If the credentials for the Rancher provider have access to the global API, then
then `registry_id` can be omitted e.g.
``` ```
$ terraform import rancher_registry_credential.private_registry 1c605 $ terraform import rancher_registry_credential.private_registry 1c605

View File

@ -57,7 +57,15 @@ The following attributes are exported:
## Import ## Import
Stacks can be imported using their Rancher API ID, e.g. Stacks can be imported using the Environment and Stack ID in the form
`<environment_id>/<stack_id>`
```
$ terraform import rancher_stack.foo 1a5/1e149
```
If the credentials for the Rancher provider have access to the global API, then
then `environment_id` can be omitted e.g.
``` ```
$ terraform import rancher_stack.foo 1e149 $ terraform import rancher_stack.foo 1e149