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 }