github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/builder/openstack/step_allocate_ip.go (about)

     1  package openstack
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips"
     8  	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
     9  	"github.com/gophercloud/gophercloud/pagination"
    10  	"github.com/hashicorp/packer/helper/multistep"
    11  	"github.com/hashicorp/packer/packer"
    12  )
    13  
    14  type StepAllocateIp struct {
    15  	FloatingIpPool string
    16  	FloatingIp     string
    17  	ReuseIps       bool
    18  }
    19  
    20  func (s *StepAllocateIp) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
    21  	ui := state.Get("ui").(packer.Ui)
    22  	config := state.Get("config").(Config)
    23  	server := state.Get("server").(*servers.Server)
    24  
    25  	// We need the v2 compute client
    26  	client, err := config.computeV2Client()
    27  	if err != nil {
    28  		err = fmt.Errorf("Error initializing compute client: %s", err)
    29  		state.Put("error", err)
    30  		return multistep.ActionHalt
    31  	}
    32  
    33  	var instanceIp floatingips.FloatingIP
    34  
    35  	// This is here in case we error out before putting instanceIp into the
    36  	// statebag below, because it is requested by Cleanup()
    37  	state.Put("access_ip", &instanceIp)
    38  
    39  	if s.FloatingIp != "" {
    40  		instanceIp.IP = s.FloatingIp
    41  	} else if s.FloatingIpPool != "" {
    42  		// If ReuseIps is set to true and we have a free floating IP in
    43  		// the pool, use it first rather than creating one
    44  		if s.ReuseIps {
    45  			ui.Say(fmt.Sprintf("Searching for unassociated floating IP in pool %s", s.FloatingIpPool))
    46  			pager := floatingips.List(client)
    47  			err := pager.EachPage(func(page pagination.Page) (bool, error) {
    48  				candidates, err := floatingips.ExtractFloatingIPs(page)
    49  
    50  				if err != nil {
    51  					return false, err // stop and throw error out
    52  				}
    53  
    54  				for _, candidate := range candidates {
    55  					if candidate.Pool != s.FloatingIpPool || candidate.InstanceID != "" {
    56  						continue // move to next in list
    57  					}
    58  
    59  					// In correct pool and able to be allocated
    60  					instanceIp.IP = candidate.IP
    61  					ui.Message(fmt.Sprintf("Selected floating IP: %s", instanceIp.IP))
    62  					state.Put("floatingip_istemp", false)
    63  					return false, nil // stop iterating over pages
    64  				}
    65  				return true, nil // try the next page
    66  			})
    67  
    68  			if err != nil {
    69  				err := fmt.Errorf("Error searching for floating ip from pool '%s'", s.FloatingIpPool)
    70  				state.Put("error", err)
    71  				ui.Error(err.Error())
    72  				return multistep.ActionHalt
    73  			}
    74  		}
    75  
    76  		if instanceIp.IP == "" {
    77  			ui.Say(fmt.Sprintf("Creating floating IP..."))
    78  			ui.Message(fmt.Sprintf("Pool: %s", s.FloatingIpPool))
    79  			newIp, err := floatingips.Create(client, floatingips.CreateOpts{
    80  				Pool: s.FloatingIpPool,
    81  			}).Extract()
    82  			if err != nil {
    83  				err := fmt.Errorf("Error creating floating ip from pool '%s'", s.FloatingIpPool)
    84  				state.Put("error", err)
    85  				ui.Error(err.Error())
    86  				return multistep.ActionHalt
    87  			}
    88  
    89  			instanceIp = *newIp
    90  			ui.Message(fmt.Sprintf("Created floating IP: %s", instanceIp.IP))
    91  			state.Put("floatingip_istemp", true)
    92  		}
    93  	}
    94  
    95  	if instanceIp.IP != "" {
    96  		ui.Say(fmt.Sprintf("Associating floating IP with server..."))
    97  		ui.Message(fmt.Sprintf("IP: %s", instanceIp.IP))
    98  		err := floatingips.AssociateInstance(client, server.ID, floatingips.AssociateOpts{
    99  			FloatingIP: instanceIp.IP,
   100  		}).ExtractErr()
   101  		if err != nil {
   102  			err := fmt.Errorf(
   103  				"Error associating floating IP %s with instance: %s",
   104  				instanceIp.IP, err)
   105  			state.Put("error", err)
   106  			ui.Error(err.Error())
   107  			return multistep.ActionHalt
   108  		}
   109  
   110  		ui.Message(fmt.Sprintf(
   111  			"Added floating IP %s to instance!", instanceIp.IP))
   112  	}
   113  
   114  	state.Put("access_ip", &instanceIp)
   115  	return multistep.ActionContinue
   116  }
   117  
   118  func (s *StepAllocateIp) Cleanup(state multistep.StateBag) {
   119  	config := state.Get("config").(Config)
   120  	ui := state.Get("ui").(packer.Ui)
   121  	instanceIp := state.Get("access_ip").(*floatingips.FloatingIP)
   122  
   123  	// Don't delete pool addresses we didn't allocate
   124  	if state.Get("floatingip_istemp") == false {
   125  		return
   126  	}
   127  
   128  	// We need the v2 compute client
   129  	client, err := config.computeV2Client()
   130  	if err != nil {
   131  		ui.Error(fmt.Sprintf(
   132  			"Error deleting temporary floating IP %s", instanceIp.IP))
   133  		return
   134  	}
   135  
   136  	if s.FloatingIpPool != "" && instanceIp.ID != "" {
   137  		if err := floatingips.Delete(client, instanceIp.ID).ExtractErr(); err != nil {
   138  			ui.Error(fmt.Sprintf(
   139  				"Error deleting temporary floating IP %s", instanceIp.IP))
   140  			return
   141  		}
   142  
   143  		ui.Say(fmt.Sprintf("Deleted temporary floating IP %s", instanceIp.IP))
   144  	}
   145  }