github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/clc/provider.go (about)

     1  package clc
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strconv"
     7  
     8  	clc "github.com/CenturyLinkCloud/clc-sdk"
     9  	"github.com/CenturyLinkCloud/clc-sdk/api"
    10  	"github.com/CenturyLinkCloud/clc-sdk/group"
    11  	"github.com/CenturyLinkCloud/clc-sdk/server"
    12  	"github.com/CenturyLinkCloud/clc-sdk/status"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  	"github.com/hashicorp/terraform/terraform"
    15  )
    16  
    17  // Provider implements ResourceProvider for CLC
    18  func Provider() terraform.ResourceProvider {
    19  	return &schema.Provider{
    20  		Schema: map[string]*schema.Schema{
    21  			"username": &schema.Schema{
    22  				Type:        schema.TypeString,
    23  				Required:    true,
    24  				DefaultFunc: schema.EnvDefaultFunc("CLC_USERNAME", nil),
    25  				Description: "Your CLC username",
    26  			},
    27  			"password": &schema.Schema{
    28  				Type:        schema.TypeString,
    29  				Required:    true,
    30  				DefaultFunc: schema.EnvDefaultFunc("CLC_PASSWORD", nil),
    31  				Description: "Your CLC password",
    32  			},
    33  			"account": &schema.Schema{
    34  				Type:        schema.TypeString,
    35  				Optional:    true,
    36  				DefaultFunc: schema.EnvDefaultFunc("CLC_ACCOUNT", ""),
    37  				Description: "Account alias override",
    38  			},
    39  		},
    40  
    41  		ResourcesMap: map[string]*schema.Resource{
    42  			"clc_server":             resourceCLCServer(),
    43  			"clc_group":              resourceCLCGroup(),
    44  			"clc_public_ip":          resourceCLCPublicIP(),
    45  			"clc_load_balancer":      resourceCLCLoadBalancer(),
    46  			"clc_load_balancer_pool": resourceCLCLoadBalancerPool(),
    47  		},
    48  
    49  		ConfigureFunc: providerConfigure,
    50  	}
    51  }
    52  
    53  func providerConfigure(d *schema.ResourceData) (interface{}, error) {
    54  	un := d.Get("username").(string)
    55  	pw := d.Get("password").(string)
    56  
    57  	config, err := api.NewConfig(un, pw)
    58  	if err != nil {
    59  		return nil, fmt.Errorf("Failed to create CLC config with provided details: %v", err)
    60  	}
    61  	config.UserAgent = fmt.Sprintf("terraform-clc terraform/%s", terraform.Version)
    62  	// user requested alias override or sub-account
    63  	if al := d.Get("account").(string); al != "" {
    64  		config.Alias = al
    65  	}
    66  
    67  	client := clc.New(config)
    68  	if err := client.Authenticate(); err != nil {
    69  		return nil, fmt.Errorf("Failed authenticated with provided credentials: %v", err)
    70  	}
    71  
    72  	alerts, err := client.Alert.GetAll()
    73  	if err != nil {
    74  		return nil, fmt.Errorf("Failed to connect to the CLC api because %s", err)
    75  	}
    76  	for _, a := range alerts.Items {
    77  		log.Printf("[WARN] Received alert: %v", a)
    78  	}
    79  	return client, nil
    80  }
    81  
    82  // package utility functions
    83  
    84  func waitStatus(client *clc.Client, id string) error {
    85  	// block until queue is processed and server is up
    86  	poll := make(chan *status.Response, 1)
    87  	err := client.Status.Poll(id, poll)
    88  	if err != nil {
    89  		return nil
    90  	}
    91  	status := <-poll
    92  	log.Printf("[DEBUG] status %v", status)
    93  	if status.Failed() {
    94  		return fmt.Errorf("unsuccessful job %v failed with status: %v", id, status.Status)
    95  	}
    96  	return nil
    97  }
    98  
    99  func dcGroups(dcname string, client *clc.Client) (map[string]string, error) {
   100  	dc, _ := client.DC.Get(dcname)
   101  	_, id := dc.Links.GetID("group")
   102  	m := map[string]string{}
   103  	resp, _ := client.Group.Get(id)
   104  	m[resp.Name] = resp.ID // top
   105  	m[resp.ID] = resp.ID
   106  	for _, x := range resp.Groups {
   107  		deepGroups(x, &m)
   108  	}
   109  	return m, nil
   110  }
   111  
   112  func deepGroups(g group.Groups, m *map[string]string) {
   113  	(*m)[g.Name] = g.ID
   114  	(*m)[g.ID] = g.ID
   115  	for _, sg := range g.Groups {
   116  		deepGroups(sg, m)
   117  	}
   118  }
   119  
   120  // resolveGroupByNameOrId takes a reference to a group (either name or guid)
   121  // and returns the guid of the group
   122  func resolveGroupByNameOrId(ref, dc string, client *clc.Client) (string, error) {
   123  	m, err := dcGroups(dc, client)
   124  	if err != nil {
   125  		return "", fmt.Errorf("Failed pulling groups in location %v - %v", dc, err)
   126  	}
   127  	if id, ok := m[ref]; ok {
   128  		return id, nil
   129  	}
   130  	return "", fmt.Errorf("Failed resolving group '%v' in location %v", ref, dc)
   131  }
   132  
   133  func stateFromString(st string) server.PowerState {
   134  	switch st {
   135  	case "on", "started":
   136  		return server.On
   137  	case "off", "stopped":
   138  		return server.Off
   139  	case "pause", "paused":
   140  		return server.Pause
   141  	case "reboot":
   142  		return server.Reboot
   143  	case "reset":
   144  		return server.Reset
   145  	case "shutdown":
   146  		return server.ShutDown
   147  	case "start_maintenance":
   148  		return server.StartMaintenance
   149  	case "stop_maintenance":
   150  		return server.StopMaintenance
   151  	}
   152  	return -1
   153  }
   154  
   155  func parseCustomFields(d *schema.ResourceData) ([]api.Customfields, error) {
   156  	var fields []api.Customfields
   157  	if v := d.Get("custom_fields"); v != nil {
   158  		for _, v := range v.([]interface{}) {
   159  			m := v.(map[string]interface{})
   160  			f := api.Customfields{
   161  				ID:    m["id"].(string),
   162  				Value: m["value"].(string),
   163  			}
   164  			fields = append(fields, f)
   165  		}
   166  	}
   167  	return fields, nil
   168  }
   169  
   170  func parseAdditionalDisks(d *schema.ResourceData) ([]server.Disk, error) {
   171  	// some complexity here: create has a different format than update
   172  	// on-create: { path, sizeGB, type }
   173  	// on-update: { diskId, sizeGB, (path), (type=partitioned) }
   174  	var disks []server.Disk
   175  	if v := d.Get("additional_disks"); v != nil {
   176  		for _, v := range v.([]interface{}) {
   177  			m := v.(map[string]interface{})
   178  			ty := m["type"].(string)
   179  			var pa string
   180  			if nil != m["path"] {
   181  				pa = m["path"].(string)
   182  			}
   183  			sz, err := strconv.Atoi(m["size_gb"].(string))
   184  			if err != nil {
   185  				log.Printf("[WARN] Failed parsing size '%v'. skipping", m["size_gb"])
   186  				return nil, fmt.Errorf("Unable to parse %v as int", m["size_gb"])
   187  			}
   188  			if ty != "raw" && ty != "partitioned" {
   189  				return nil, fmt.Errorf("Expected type of { raw | partitioned }. received %v", ty)
   190  			}
   191  			if ty == "raw" && pa != "" {
   192  				return nil, fmt.Errorf("Path can not be specified for raw disks")
   193  			}
   194  			disk := server.Disk{
   195  				SizeGB: sz,
   196  				Type:   ty,
   197  			}
   198  			if pa != "" {
   199  				disk.Path = pa
   200  			}
   201  			disks = append(disks, disk)
   202  		}
   203  	}
   204  	return disks, nil
   205  }
   206  
   207  func parsePackages(d *schema.ResourceData) ([]server.Package, error) {
   208  	var pkgs []server.Package
   209  	if e := d.Get("packages"); e != nil {
   210  		for _, e := range e.([]interface{}) {
   211  			m := e.(map[string]interface{})
   212  			id := m["id"].(string)
   213  			delete(m, "id")
   214  			ms := make(map[string]string)
   215  			for k, v := range m {
   216  				if s, ok := v.(string); ok {
   217  					ms[k] = s
   218  				}
   219  			}
   220  			p := server.Package{
   221  				ID:     id,
   222  				Params: ms,
   223  			}
   224  			pkgs = append(pkgs, p)
   225  		}
   226  	}
   227  	return pkgs, nil
   228  }