github.com/raghuse92/packer@v1.3.2/builder/openstack/run_config.go (about) 1 package openstack 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" 8 "github.com/hashicorp/packer/common/uuid" 9 "github.com/hashicorp/packer/helper/communicator" 10 "github.com/hashicorp/packer/template/interpolate" 11 ) 12 13 // RunConfig contains configuration for running an instance from a source 14 // image and details on how to access that launched image. 15 type RunConfig struct { 16 Comm communicator.Config `mapstructure:",squash"` 17 18 SourceImage string `mapstructure:"source_image"` 19 SourceImageName string `mapstructure:"source_image_name"` 20 SourceImageFilters ImageFilter `mapstructure:"source_image_filter"` 21 Flavor string `mapstructure:"flavor"` 22 AvailabilityZone string `mapstructure:"availability_zone"` 23 RackconnectWait bool `mapstructure:"rackconnect_wait"` 24 FloatingIPNetwork string `mapstructure:"floating_ip_network"` 25 FloatingIP string `mapstructure:"floating_ip"` 26 ReuseIPs bool `mapstructure:"reuse_ips"` 27 SecurityGroups []string `mapstructure:"security_groups"` 28 Networks []string `mapstructure:"networks"` 29 Ports []string `mapstructure:"ports"` 30 UserData string `mapstructure:"user_data"` 31 UserDataFile string `mapstructure:"user_data_file"` 32 InstanceName string `mapstructure:"instance_name"` 33 InstanceMetadata map[string]string `mapstructure:"instance_metadata"` 34 35 ConfigDrive bool `mapstructure:"config_drive"` 36 37 // Used for BC, value will be passed to the "floating_ip_network" 38 FloatingIPPool string `mapstructure:"floating_ip_pool"` 39 40 UseBlockStorageVolume bool `mapstructure:"use_blockstorage_volume"` 41 VolumeName string `mapstructure:"volume_name"` 42 VolumeType string `mapstructure:"volume_type"` 43 VolumeAvailabilityZone string `mapstructure:"volume_availability_zone"` 44 45 // Not really used, but here for BC 46 OpenstackProvider string `mapstructure:"openstack_provider"` 47 UseFloatingIp bool `mapstructure:"use_floating_ip"` 48 49 sourceImageOpts images.ListOpts 50 } 51 52 type ImageFilter struct { 53 Filters ImageFilterOptions `mapstructure:"filters"` 54 MostRecent bool `mapstructure:"most_recent"` 55 } 56 57 type ImageFilterOptions struct { 58 Name string `mapstructure:"name"` 59 Owner string `mapstructure:"owner"` 60 Tags []string `mapstructure:"tags"` 61 Visibility string `mapstructure:"visibility"` 62 } 63 64 func (f *ImageFilterOptions) Empty() bool { 65 return f.Name == "" && f.Owner == "" && len(f.Tags) == 0 && f.Visibility == "" 66 } 67 68 func (f *ImageFilterOptions) Build() (*images.ListOpts, error) { 69 opts := images.ListOpts{} 70 // Set defaults for status, member_status, and sort 71 opts.Status = images.ImageStatusActive 72 opts.MemberStatus = images.ImageMemberStatusAccepted 73 opts.Sort = "created_at:desc" 74 75 var err error 76 77 if f.Name != "" { 78 opts.Name = f.Name 79 } 80 if f.Owner != "" { 81 opts.Owner = f.Owner 82 } 83 if len(f.Tags) > 0 { 84 opts.Tags = f.Tags 85 } 86 if f.Visibility != "" { 87 v, err := getImageVisibility(f.Visibility) 88 if err == nil { 89 opts.Visibility = *v 90 } 91 } 92 93 return &opts, err 94 } 95 96 func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { 97 // If we are not given an explicit ssh_keypair_name or 98 // ssh_private_key_file, then create a temporary one, but only if the 99 // temporary_key_pair_name has not been provided and we are not using 100 // ssh_password. 101 if c.Comm.SSHKeyPairName == "" && c.Comm.SSHTemporaryKeyPairName == "" && 102 c.Comm.SSHPrivateKeyFile == "" && c.Comm.SSHPassword == "" { 103 104 c.Comm.SSHTemporaryKeyPairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID()) 105 } 106 107 if c.FloatingIPPool != "" && c.FloatingIPNetwork == "" { 108 c.FloatingIPNetwork = c.FloatingIPPool 109 } 110 111 // Validation 112 errs := c.Comm.Prepare(ctx) 113 114 if c.Comm.SSHKeyPairName != "" { 115 if c.Comm.Type == "winrm" && c.Comm.WinRMPassword == "" && c.Comm.SSHPrivateKeyFile == "" { 116 errs = append(errs, errors.New("A ssh_private_key_file must be provided to retrieve the winrm password when using ssh_keypair_name.")) 117 } else if c.Comm.SSHPrivateKeyFile == "" && !c.Comm.SSHAgentAuth { 118 errs = append(errs, errors.New("A ssh_private_key_file must be provided or ssh_agent_auth enabled when ssh_keypair_name is specified.")) 119 } 120 } 121 122 if c.SourceImage == "" && c.SourceImageName == "" && c.SourceImageFilters.Filters.Empty() { 123 errs = append(errs, errors.New("Either a source_image, a source_image_name, or source_image_filter must be specified")) 124 } else if len(c.SourceImage) > 0 && len(c.SourceImageName) > 0 { 125 errs = append(errs, errors.New("Only a source_image or a source_image_name can be specified, not both.")) 126 } 127 128 if c.Flavor == "" { 129 errs = append(errs, errors.New("A flavor must be specified")) 130 } 131 132 if c.Comm.SSHIPVersion != "" && c.Comm.SSHIPVersion != "4" && c.Comm.SSHIPVersion != "6" { 133 errs = append(errs, errors.New("SSH IP version must be either 4 or 6")) 134 } 135 136 for key, value := range c.InstanceMetadata { 137 if len(key) > 255 { 138 errs = append(errs, fmt.Errorf("Instance metadata key too long (max 255 bytes): %s", key)) 139 } 140 if len(value) > 255 { 141 errs = append(errs, fmt.Errorf("Instance metadata value too long (max 255 bytes): %s", value)) 142 } 143 } 144 145 if c.UseBlockStorageVolume { 146 // Use Compute instance availability zone for the Block Storage volume if 147 // it's not provided. 148 if c.VolumeAvailabilityZone == "" { 149 c.VolumeAvailabilityZone = c.AvailabilityZone 150 } 151 152 // Use random name for the Block Storage volume if it's not provided. 153 if c.VolumeName == "" { 154 c.VolumeName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID()) 155 } 156 } 157 158 // if neither ID or image name is provided outside the filter, build the filter 159 if len(c.SourceImage) == 0 && len(c.SourceImageName) == 0 { 160 161 listOpts, filterErr := c.SourceImageFilters.Filters.Build() 162 163 if filterErr != nil { 164 errs = append(errs, filterErr) 165 } 166 c.sourceImageOpts = *listOpts 167 } 168 169 return errs 170 } 171 172 // Retrieve the specific ImageVisibility using the exported const from images 173 func getImageVisibility(visibility string) (*images.ImageVisibility, error) { 174 visibilities := [...]images.ImageVisibility{ 175 images.ImageVisibilityPublic, 176 images.ImageVisibilityPrivate, 177 images.ImageVisibilityCommunity, 178 images.ImageVisibilityShared, 179 } 180 181 for _, v := range visibilities { 182 if string(v) == visibility { 183 return &v, nil 184 } 185 } 186 187 return nil, fmt.Errorf("Not a valid visibility: %s", visibility) 188 }