github.com/GoogleCloudPlatform/terraformer@v0.8.18/providers/openstack/compute.go (about)

     1  // Copyright 2018 The Terraformer Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package openstack
    16  
    17  import (
    18  	"log"
    19  	"sort"
    20  	"strconv"
    21  	"strings"
    22  
    23  	"github.com/GoogleCloudPlatform/terraformer/terraformutils"
    24  	"github.com/gophercloud/gophercloud"
    25  	"github.com/gophercloud/gophercloud/openstack"
    26  	"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes"
    27  	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
    28  	"github.com/gophercloud/gophercloud/pagination"
    29  )
    30  
    31  type ComputeGenerator struct {
    32  	OpenStackService
    33  }
    34  
    35  // createResources iterate on all openstack_compute_instance_v2
    36  func (g *ComputeGenerator) createResources(list *pagination.Pager, volclient *gophercloud.ServiceClient) []terraformutils.Resource {
    37  	resources := []terraformutils.Resource{}
    38  
    39  	err := list.EachPage(func(page pagination.Page) (bool, error) {
    40  		servers, err := servers.ExtractServers(page)
    41  		if err != nil {
    42  			return false, err
    43  		}
    44  
    45  		for _, s := range servers {
    46  			var bds = []map[string]interface{}{}
    47  			var vol []volumes.Volume
    48  			t := map[string]interface{}{}
    49  			if volclient != nil {
    50  				for _, av := range s.AttachedVolumes {
    51  					onevol, err := volumes.Get(volclient, av.ID).Extract()
    52  					if err == nil {
    53  						vol = append(vol, *onevol)
    54  					}
    55  				}
    56  
    57  				sort.SliceStable(vol, func(i, j int) bool {
    58  					return vol[i].Attachments[0].Device < vol[j].Attachments[0].Device
    59  				})
    60  
    61  				var bindex = 0
    62  				var dependsOn = ""
    63  				for _, v := range vol {
    64  					if v.Bootable == "true" && v.VolumeImageMetadata != nil {
    65  						bds = append(bds, map[string]interface{}{
    66  							"source_type":           "image",
    67  							"uuid":                  v.VolumeImageMetadata["image_id"],
    68  							"volume_size":           strconv.Itoa(v.Size),
    69  							"boot_index":            strconv.Itoa(bindex),
    70  							"destination_type":      "volume",
    71  							"delete_on_termination": "false",
    72  						})
    73  						bindex++
    74  					} else {
    75  						tv := map[string]interface{}{}
    76  						if dependsOn != "" {
    77  							tv["depends_on"] = []string{dependsOn}
    78  						}
    79  
    80  						name := s.Name + strings.ReplaceAll(v.Attachments[0].Device, "/dev/", "")
    81  						rid := s.ID + "/" + v.ID
    82  						resource := terraformutils.NewResource(
    83  							rid,
    84  							name,
    85  							"openstack_compute_volume_attach_v2",
    86  							"openstack",
    87  							map[string]string{},
    88  							[]string{},
    89  							tv,
    90  						)
    91  						dependsOn = "openstack_compute_volume_attach_v2." + terraformutils.TfSanitize(name)
    92  						tv["instance_name"] = terraformutils.TfSanitize(s.Name)
    93  						if v.Name == "" {
    94  							v.Name = v.ID
    95  						}
    96  						tv["volume_name"] = terraformutils.TfSanitize(v.Name)
    97  						resources = append(resources, resource)
    98  					}
    99  				}
   100  			}
   101  
   102  			if len(bds) > 0 {
   103  				t = map[string]interface{}{"block_device": bds}
   104  			}
   105  
   106  			resource := terraformutils.NewResource(
   107  				s.ID,
   108  				s.Name,
   109  				"openstack_compute_instance_v2",
   110  				"openstack",
   111  				map[string]string{},
   112  				[]string{},
   113  				t,
   114  			)
   115  
   116  			resources = append(resources, resource)
   117  		}
   118  
   119  		return true, nil
   120  	})
   121  	if err != nil {
   122  		log.Println(err)
   123  	}
   124  	return resources
   125  }
   126  
   127  // Generate TerraformResources from OpenStack API,
   128  func (g *ComputeGenerator) InitResources() error {
   129  	opts, err := openstack.AuthOptionsFromEnv()
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	provider, err := openstack.AuthenticatedClient(opts)
   135  	if err != nil {
   136  		return err
   137  	}
   138  
   139  	client, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{
   140  		Region: g.GetArgs()["region"].(string),
   141  	})
   142  	if err != nil {
   143  		return err
   144  	}
   145  
   146  	list := servers.List(client, nil)
   147  	volclient, err := openstack.NewBlockStorageV3(provider, gophercloud.EndpointOpts{
   148  		Region: g.GetArgs()["region"].(string)})
   149  	if err != nil {
   150  		log.Println("VolumeImageMetadata requires blockStorage API v3")
   151  		volclient = nil
   152  	}
   153  	g.Resources = g.createResources(&list, volclient)
   154  
   155  	return nil
   156  }
   157  
   158  func (g *ComputeGenerator) PostConvertHook() error {
   159  	for i, r := range g.Resources {
   160  		if r.InstanceInfo.Type == "openstack_compute_volume_attach_v2" {
   161  			g.Resources[i].Item["volume_id"] = "${openstack_blockstorage_volume_v3." + r.AdditionalFields["volume_name"].(string) + ".id}"
   162  			g.Resources[i].Item["instance_id"] = "${openstack_compute_instance_v2." + r.AdditionalFields["instance_name"].(string) + ".id}"
   163  			delete(g.Resources[i].Item, "volume_name")
   164  			delete(g.Resources[i].Item, "instance_name")
   165  			delete(g.Resources[i].Item, "device")
   166  		}
   167  		if r.InstanceInfo.Type != "openstack_compute_instance_v2" {
   168  			continue
   169  		}
   170  
   171  		// Copy "all_metadata.%" to "metadata.%"
   172  		for k, v := range g.Resources[i].InstanceState.Attributes {
   173  			if strings.HasPrefix(k, "all_metadata") {
   174  				newKey := strings.Replace(k, "all_metadata", "metadata", 1)
   175  				g.Resources[i].InstanceState.Attributes[newKey] = v
   176  			}
   177  		}
   178  		// Replace "all_metadata" to "metadata"
   179  		// because "all_metadata" field cannot be set as resource argument
   180  		for k, v := range g.Resources[i].Item {
   181  			if strings.HasPrefix(k, "all_metadata") {
   182  				newKey := strings.Replace(k, "all_metadata", "metadata", 1)
   183  				g.Resources[i].Item[newKey] = v
   184  				delete(g.Resources[i].Item, k)
   185  			}
   186  		}
   187  		if r.AdditionalFields["block_device"] != nil {
   188  			bds := r.AdditionalFields["block_device"].([]map[string]interface{})
   189  			for bi, bd := range bds {
   190  				for k, v := range bd {
   191  					g.Resources[i].InstanceState.Attributes["block_device."+strconv.Itoa(bi)+"."+k] = v.(string)
   192  				}
   193  			}
   194  
   195  			g.Resources[i].InstanceState.Attributes["block_device.#"] = strconv.Itoa(len(bds))
   196  		}
   197  	}
   198  
   199  	return nil
   200  }