github.com/skyscape-cloud-services/terraform@v0.9.2-0.20170609144644-7ece028a1747/builtin/providers/rancher/resource_rancher_environment.go (about)

     1  package rancher
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"time"
     7  
     8  	"github.com/hashicorp/terraform/helper/resource"
     9  	"github.com/hashicorp/terraform/helper/schema"
    10  	"github.com/hashicorp/terraform/helper/validation"
    11  	rancherClient "github.com/rancher/go-rancher/v2"
    12  )
    13  
    14  var (
    15  	defaultProjectTemplates = map[string]string{
    16  		"mesos":      "",
    17  		"kubernetes": "",
    18  		"windows":    "",
    19  		"swarm":      "",
    20  		"cattle":     "",
    21  	}
    22  )
    23  
    24  func resourceRancherEnvironment() *schema.Resource {
    25  	return &schema.Resource{
    26  		Create: resourceRancherEnvironmentCreate,
    27  		Read:   resourceRancherEnvironmentRead,
    28  		Update: resourceRancherEnvironmentUpdate,
    29  		Delete: resourceRancherEnvironmentDelete,
    30  		Importer: &schema.ResourceImporter{
    31  			State: schema.ImportStatePassthrough,
    32  		},
    33  
    34  		Schema: map[string]*schema.Schema{
    35  			"id": &schema.Schema{
    36  				Type:     schema.TypeString,
    37  				Computed: true,
    38  			},
    39  			"name": &schema.Schema{
    40  				Type:     schema.TypeString,
    41  				Required: true,
    42  			},
    43  			"orchestration": &schema.Schema{
    44  				Type:          schema.TypeString,
    45  				Optional:      true,
    46  				ValidateFunc:  validation.StringInSlice([]string{"cattle", "kubernetes", "mesos", "swarm", "windows"}, true),
    47  				Computed:      true,
    48  				ConflictsWith: []string{"project_template_id"},
    49  			},
    50  			"project_template_id": &schema.Schema{
    51  				Type:          schema.TypeString,
    52  				Optional:      true,
    53  				Computed:      true,
    54  				ConflictsWith: []string{"orchestration"},
    55  			},
    56  			"description": &schema.Schema{
    57  				Type:     schema.TypeString,
    58  				Optional: true,
    59  			},
    60  			"member": &schema.Schema{
    61  				Type:     schema.TypeSet,
    62  				Optional: true,
    63  				Computed: true,
    64  				Elem: &schema.Resource{
    65  					Schema: map[string]*schema.Schema{
    66  						"external_id_type": {
    67  							Type:     schema.TypeString,
    68  							Required: true,
    69  						},
    70  						"external_id": {
    71  							Type:     schema.TypeString,
    72  							Required: true,
    73  						},
    74  						"role": {
    75  							Type:     schema.TypeString,
    76  							Required: true,
    77  						},
    78  					},
    79  				},
    80  			},
    81  		},
    82  	}
    83  }
    84  
    85  func resourceRancherEnvironmentCreate(d *schema.ResourceData, meta interface{}) error {
    86  	log.Printf("[INFO] Creating Environment: %s", d.Id())
    87  	populateProjectTemplateIDs(meta.(*Config))
    88  
    89  	client, err := meta.(*Config).GlobalClient()
    90  	if err != nil {
    91  		return err
    92  	}
    93  
    94  	name := d.Get("name").(string)
    95  	description := d.Get("description").(string)
    96  	orchestration := d.Get("orchestration").(string)
    97  	projectTemplateID := d.Get("project_template_id").(string)
    98  
    99  	projectTemplateID, err = getProjectTemplateID(orchestration, projectTemplateID)
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	data := map[string]interface{}{
   105  		"name":              &name,
   106  		"description":       &description,
   107  		"projectTemplateId": &projectTemplateID,
   108  	}
   109  
   110  	var newEnv rancherClient.Project
   111  	if err := client.Create("project", data, &newEnv); err != nil {
   112  		return err
   113  	}
   114  
   115  	stateConf := &resource.StateChangeConf{
   116  		Pending:    []string{"active", "removed", "removing"},
   117  		Target:     []string{"active"},
   118  		Refresh:    EnvironmentStateRefreshFunc(client, newEnv.Id),
   119  		Timeout:    10 * time.Minute,
   120  		Delay:      1 * time.Second,
   121  		MinTimeout: 3 * time.Second,
   122  	}
   123  	_, waitErr := stateConf.WaitForState()
   124  	if waitErr != nil {
   125  		return fmt.Errorf(
   126  			"Error waiting for environment (%s) to be created: %s", newEnv.Id, waitErr)
   127  	}
   128  
   129  	d.SetId(newEnv.Id)
   130  	log.Printf("[INFO] Environment ID: %s", d.Id())
   131  
   132  	// Add members
   133  	if v, ok := d.GetOk("member"); ok {
   134  		envClient, err := meta.(*Config).EnvironmentClient(d.Id())
   135  		if err != nil {
   136  			return err
   137  		}
   138  		members := makeProjectMembers(v.([]interface{}))
   139  		_, err = envClient.Project.ActionSetmembers(&newEnv, &rancherClient.SetProjectMembersInput{
   140  			Members: members,
   141  		})
   142  		if err != nil {
   143  			return err
   144  		}
   145  	}
   146  
   147  	return resourceRancherEnvironmentRead(d, meta)
   148  }
   149  
   150  func resourceRancherEnvironmentRead(d *schema.ResourceData, meta interface{}) error {
   151  	log.Printf("[INFO] Refreshing Environment: %s", d.Id())
   152  	client, err := meta.(*Config).GlobalClient()
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	env, err := client.Project.ById(d.Id())
   158  	if err != nil {
   159  		return err
   160  	}
   161  
   162  	if env == nil {
   163  		log.Printf("[INFO] Environment %s not found", d.Id())
   164  		d.SetId("")
   165  		return nil
   166  	}
   167  
   168  	if removed(env.State) {
   169  		log.Printf("[INFO] Environment %s was removed on %v", d.Id(), env.Removed)
   170  		d.SetId("")
   171  		return nil
   172  	}
   173  
   174  	log.Printf("[INFO] Environment Name: %s", env.Name)
   175  
   176  	d.Set("description", env.Description)
   177  	d.Set("name", env.Name)
   178  	d.Set("orchestration", getActiveOrchestration(env))
   179  	d.Set("project_template_id", env.ProjectTemplateId)
   180  
   181  	envClient, err := meta.(*Config).EnvironmentClient(d.Id())
   182  	if err != nil {
   183  		return err
   184  	}
   185  
   186  	members, _ := envClient.ProjectMember.List(NewListOpts())
   187  
   188  	d.Set("member", normalizeMembers(members.Data))
   189  	return nil
   190  }
   191  
   192  func resourceRancherEnvironmentUpdate(d *schema.ResourceData, meta interface{}) error {
   193  	populateProjectTemplateIDs(meta.(*Config))
   194  
   195  	client, err := meta.(*Config).GlobalClient()
   196  	if err != nil {
   197  		return err
   198  	}
   199  
   200  	name := d.Get("name").(string)
   201  	description := d.Get("description").(string)
   202  	orchestration := d.Get("orchestration").(string)
   203  	projectTemplateID := d.Get("project_template_id").(string)
   204  
   205  	projectTemplateID, err = getProjectTemplateID(orchestration, projectTemplateID)
   206  	if err != nil {
   207  		return err
   208  	}
   209  
   210  	data := map[string]interface{}{
   211  		"name":                &name,
   212  		"description":         &description,
   213  		"project_template_id": &projectTemplateID,
   214  	}
   215  
   216  	var newEnv rancherClient.Project
   217  	env, err := client.Project.ById(d.Id())
   218  	if err != nil {
   219  		return err
   220  	}
   221  
   222  	if err := client.Update("project", &env.Resource, data, &newEnv); err != nil {
   223  		return err
   224  	}
   225  
   226  	// Update members
   227  	envClient, err := meta.(*Config).EnvironmentClient(d.Id())
   228  	if err != nil {
   229  		return err
   230  	}
   231  	members := d.Get("member").(*schema.Set).List()
   232  	_, err = envClient.Project.ActionSetmembers(&newEnv, &rancherClient.SetProjectMembersInput{
   233  		Members: makeProjectMembers(members),
   234  	})
   235  	if err != nil {
   236  		return err
   237  	}
   238  
   239  	return resourceRancherEnvironmentRead(d, meta)
   240  }
   241  
   242  func resourceRancherEnvironmentDelete(d *schema.ResourceData, meta interface{}) error {
   243  	log.Printf("[INFO] Deleting Environment: %s", d.Id())
   244  	id := d.Id()
   245  	client, err := meta.(*Config).GlobalClient()
   246  	if err != nil {
   247  		return err
   248  	}
   249  
   250  	env, err := client.Project.ById(id)
   251  	if err != nil {
   252  		return err
   253  	}
   254  
   255  	if err := client.Project.Delete(env); err != nil {
   256  		return fmt.Errorf("Error deleting Environment: %s", err)
   257  	}
   258  
   259  	log.Printf("[DEBUG] Waiting for environment (%s) to be removed", id)
   260  
   261  	stateConf := &resource.StateChangeConf{
   262  		Pending:    []string{"active", "removed", "removing"},
   263  		Target:     []string{"removed"},
   264  		Refresh:    EnvironmentStateRefreshFunc(client, id),
   265  		Timeout:    10 * time.Minute,
   266  		Delay:      1 * time.Second,
   267  		MinTimeout: 3 * time.Second,
   268  	}
   269  
   270  	_, waitErr := stateConf.WaitForState()
   271  	if waitErr != nil {
   272  		return fmt.Errorf(
   273  			"Error waiting for environment (%s) to be removed: %s", id, waitErr)
   274  	}
   275  
   276  	d.SetId("")
   277  	return nil
   278  }
   279  
   280  func getProjectTemplateID(orchestration, templateID string) (string, error) {
   281  	id := templateID
   282  	if templateID == "" && orchestration == "" {
   283  		return "", fmt.Errorf("Need either 'orchestration' or 'project_template_id'")
   284  	}
   285  
   286  	if templateID == "" && orchestration != "" {
   287  		ok := false
   288  		id, ok = defaultProjectTemplates[orchestration]
   289  		if !ok {
   290  			return "", fmt.Errorf("Invalid orchestration: %s", orchestration)
   291  		}
   292  	}
   293  
   294  	return id, nil
   295  }
   296  
   297  func normalizeMembers(in []rancherClient.ProjectMember) (out []interface{}) {
   298  	for _, m := range in {
   299  		mm := map[string]string{
   300  			"external_id_type": m.ExternalIdType,
   301  			"external_id":      m.ExternalId,
   302  			"role":             m.Role,
   303  		}
   304  		out = append(out, mm)
   305  	}
   306  	return
   307  }
   308  
   309  func makeProjectMembers(in []interface{}) (out []rancherClient.ProjectMember) {
   310  	for _, m := range in {
   311  		mMap := m.(map[string]interface{})
   312  		mm := rancherClient.ProjectMember{
   313  			ExternalIdType: mMap["external_id_type"].(string),
   314  			ExternalId:     mMap["external_id"].(string),
   315  			Role:           mMap["role"].(string),
   316  		}
   317  		out = append(out, mm)
   318  	}
   319  	return
   320  }
   321  
   322  // EnvironmentStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
   323  // a Rancher Environment.
   324  func EnvironmentStateRefreshFunc(client *rancherClient.RancherClient, environmentID string) resource.StateRefreshFunc {
   325  	return func() (interface{}, string, error) {
   326  		env, err := client.Project.ById(environmentID)
   327  
   328  		if err != nil {
   329  			return nil, "", err
   330  		}
   331  
   332  		// Env not returned, or State not set...
   333  		if env == nil || env.State == "" {
   334  			// This makes it so user level API keys can be used instead of just admin
   335  			env = &rancherClient.Project{
   336  				State: "removed",
   337  			}
   338  		}
   339  
   340  		return env, env.State, nil
   341  	}
   342  }