Merge remote-tracking branch 'upstream/master' into feature/datadisk-change_name

This commit is contained in:
Vijay Rajah 2020-04-02 21:53:47 +05:30
commit 5340af4d49
84 changed files with 2884 additions and 438 deletions

View File

@ -4,6 +4,7 @@ comment:
require_changes: true # only comment on changes in coverage
require_base: yes # [yes :: must have a base report to post]
require_head: yes # [yes :: must have a head report to post]
after_n_builds: 3 # wait for all OS test coverage builds to post comment
branches: # branch names that can post comment
- "master"

View File

@ -275,7 +275,7 @@ Run golangci-lint on a single pkg or directory; PKG_NAME expands to /builder/ama
make lint PKG_NAME=builder/amazon
```
Note: linting on Travis uses the `--new-from-rev=origin/master` flag to only lint new files added within a branch or pull-request. To run this check locally you can use the `ci-lint` make target. See [golangci-lint in CI](https://github.com/golangci/golangci-lint#faq) for more information.
Note: linting on Travis uses the `--new-from-rev` flag to only lint new files added within a branch or pull-request. To run this check locally you can use the `ci-lint` make target. See [golangci-lint in CI](https://github.com/golangci/golangci-lint#faq) for more information.
```
make ci-lint

View File

@ -1,21 +1,34 @@
## 1.5.5 (Upcoming)
## 1.5.6 (Upcoming)
### Bug Fixes:
* core: Make sure CLI variables supersede variables from var files [GH-8964]
* builder/qemu: Remove `net_device` pre-validation [GH-8979]
## 1.5.5 (March 25,2020)
### IMPROVEMENTS:
* builder/azure: Add support for configurable KeyVault SKU [GH-8879]
* builder/hyperv: Add `first_boot_device` setting to allow the selection of the
initial device or device class used for booting the VM. [GH-8714]
* builder/hyperv: Fix Hyper-V compacted disk size comparison [GH-8811]
* builder/openstack: Add new `image_auto_accept_members` option [GH-8931]
* builder/proxmox: Add ability to specify vga adapter [GH-8892]
* builder/proxmox: Add onboot directive support [GH-8935]
* builder/tencentcloud: Show tencentcloud image id after copy to desination
region. [GH-8763]
* builder/vmware-iso: Add `cleanup_remote_cache` config option to [GH-8917]
* builder/vmware-iso: Do not perform dial test of NIC when ssh bastion is
required [GH-8877]
* builder/vsphere-clone: Add ability to export VM to OVF file [GH-8764]
* builder/vsphere-iso: Add ability to define multiple disks. [GH-8787]
* builder/vsphere-iso: Add ability to export VM to OVF file [GH-8764]
* builder/vsphere-iso: Add support for eagerly zeroed / scrubbed disks.
[GH-8756]
* builder/vsphere-iso: Add the remote iso first so that it is first in boot
order, and clarify boot behavior. [GH-8732]
* communicator/ssh: Add flag to enable support for keyboard-interactive auth to
connect bastion [GH-8847]
* core/hcl2: Add support for singular blocks [GH-8889]
* core/hcl2: Add support in HCL2 configs for dynamic blocks, document for loops
and splat expressions [GH-8720]
* core/hcl2: Fix HCL2 local variables decoding to allow local usage within
@ -29,17 +42,28 @@
### Bug Fixes:
* bilder/proxmox: Bump proxmox-api-go to fix upstream bug where users hit open
file limit. [GH-8800]
* builder/azure: Fix `winrm_password` attribution and allow users to set
`winrm_username` [GH-8928]
* builder/azure: Fix azure key vault cleanup failure [GH-8905]
* builder/azure: Fix HCL2 bug that prevented Azure and other builders from
loading properly. [GH-8785]
* builder/googlecompute: Fix WinRMPassword template engine. [GH-8890]
* builder/googlecompute: Replace deprecated "sshKeys" metadata with "ssh-keys"
to fix SSH authentication issue [GH-8942]
* builder/proxmox: Add new validation to catch that template_name cannot
contain spaces. [GH-8799]
* builder/vagrant: Fix path validation in ssh config step. [GH-8826]
* builder/virtualbox-vm: Fix crash when VM has no snapshots. [GH-8906]
* builder/virtualbox: Remove all floppy controllers before adding a new one.
[GH-8828]
* builder/vsphere-clone: Fix issue preventing the cloning of VMs with the same
name in different folders [GH-8938]
* builder/vsphere-iso: Fix issue preventing the creation of VMs with the same
name in different folders [GH-8938]
* builder/vsphere: Fix network object interface panic. [GH-8753]
* core/hcl2: Fix crash when an unset variable is used [GH-8837]
* core/hcl2: Fix logic for parsing literal value variables [GH-8834]
* core/hcl2: Make sure locals are evaluated only after variables are. [GH-8918]
* core: Fix "build" template engine interpolation for certain fields in certain
provisioners. [GH-8771]
* core: Fix bug where user var recursion could fail intermittently when used
@ -48,7 +72,6 @@
can disable plugins by renaming the extension [GH-8735]
* provisioner/shell: "inline" config option is now a template engine. [GH-8883]
## 1.5.4 (February 14, 2020)
no-change release to fix code-signing on OSX binaries. Since checksums for these
binaries has changed, we are releasing a second time to prevent confusion.

View File

@ -82,7 +82,7 @@ lint: install-lint-deps ## Lint Go code
ci-lint: install-lint-deps ## On ci only lint newly added Go source files
@echo "==> Running linter on newly added Go source files..."
GO111MODULE=on golangci-lint run --new-from-rev=origin/master ./...
GO111MODULE=on golangci-lint run --new-from-rev=`git merge-base master HEAD` ./...
fmt: ## Format Go code

View File

@ -49,24 +49,6 @@ type StepRunSpotInstance struct {
func (s *StepRunSpotInstance) CreateTemplateData(userData *string, az string,
state multistep.StateBag, marketOptions *ec2.LaunchTemplateInstanceMarketOptionsRequest) *ec2.RequestLaunchTemplateData {
blockDeviceMappings := s.LaunchMappings.BuildEC2BlockDeviceMappings()
if s.NoEphemeral {
// This is only relevant for windows guests. Ephemeral drives by
// default are assigned to drive names xvdca-xvdcz.
// When vms are launched from the AWS console, they're automatically
// removed from the block devices if the user hasn't said to use them,
// but the SDK does not perform this cleanup. The following code just
// manually removes the ephemeral drives from the mapping so that they
// don't clutter up console views and cause confusion.
log.Printf("no_ephemeral was set, so creating drives xvdca-xvdcz as empty mappings")
DefaultEphemeralDeviceLetters := "abcdefghijklmnopqrstuvwxyz"
for _, letter := range DefaultEphemeralDeviceLetters {
bd := &ec2.BlockDeviceMapping{
DeviceName: aws.String("xvdc" + string(letter)),
NoDevice: aws.String(""),
}
blockDeviceMappings = append(blockDeviceMappings, bd)
}
}
// Convert the BlockDeviceMapping into a
// LaunchTemplateBlockDeviceMappingRequest. These structs are identical,
// except for the EBS field -- on one, that field contains a
@ -80,11 +62,29 @@ func (s *StepRunSpotInstance) CreateTemplateData(userData *string, az string,
launchRequest := &ec2.LaunchTemplateBlockDeviceMappingRequest{
DeviceName: mapping.DeviceName,
Ebs: (*ec2.LaunchTemplateEbsBlockDeviceRequest)(mapping.Ebs),
NoDevice: mapping.NoDevice,
VirtualName: mapping.VirtualName,
}
launchMappingRequests = append(launchMappingRequests, launchRequest)
}
if s.NoEphemeral {
// This is only relevant for windows guests. Ephemeral drives by
// default are assigned to drive names xvdca-xvdcz.
// When vms are launched from the AWS console, they're automatically
// removed from the block devices if the user hasn't said to use them,
// but the SDK does not perform this cleanup. The following code just
// manually removes the ephemeral drives from the mapping so that they
// don't clutter up console views and cause confusion.
log.Printf("no_ephemeral was set, so creating drives xvdca-xvdcz as empty mappings")
DefaultEphemeralDeviceLetters := "abcdefghijklmnopqrstuvwxyz"
for _, letter := range DefaultEphemeralDeviceLetters {
launchRequest := &ec2.LaunchTemplateBlockDeviceMappingRequest{
DeviceName: aws.String("xvdc" + string(letter)),
NoDevice: aws.String(""),
}
launchMappingRequests = append(launchMappingRequests, launchRequest)
}
}
iamInstanceProfile := aws.String(state.Get("iamInstanceProfile").(string))

View File

@ -94,3 +94,42 @@ func TestCreateTemplateData(t *testing.T) {
t.Fatalf("Template shouldn't contain instance profile if iamInstanceProfile is unset.")
}
}
func TestCreateTemplateData_NoEphemeral(t *testing.T) {
state := tStateSpot()
stepRunSpotInstance := getBasicStep()
stepRunSpotInstance.NoEphemeral = true
template := stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
&ec2.LaunchTemplateInstanceMarketOptionsRequest{})
if len(template.BlockDeviceMappings) != 26 {
t.Fatalf("Should have created 26 mappings to keep ephemeral drives from appearing.")
}
// Now check that noEphemeral doesn't mess with the mappings in real life.
// state = tStateSpot()
// stepRunSpotInstance = getBasicStep()
// stepRunSpotInstance.NoEphemeral = true
// mappings := []*ec2.InstanceBlockDeviceMapping{
// &ec2.InstanceBlockDeviceMapping{
// DeviceName: "xvda",
// Ebs: {
// DeleteOnTermination: true,
// Status: "attaching",
// VolumeId: "vol-044cd49c330f21c05",
// },
// },
// &ec2.InstanceBlockDeviceMapping{
// DeviceName: "/dev/xvdf",
// Ebs: {
// DeleteOnTermination: false,
// Status: "attaching",
// VolumeId: "vol-0eefaf2d6ae35827e",
// },
// },
// }
// template = stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
// &ec2.LaunchTemplateInstanceMarketOptionsRequest{})
// if len(*template.BlockDeviceMappings) != 26 {
// t.Fatalf("Should have created 26 mappings to keep ephemeral drives from appearing.")
// }
}

View File

@ -254,7 +254,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
WinRMConfig: func(multistep.StateBag) (*communicator.WinRMConfig, error) {
return &communicator.WinRMConfig{
Username: b.config.UserName,
Password: b.config.Comm.WinRMPassword,
Password: b.config.Password,
}, nil
},
},

View File

@ -18,6 +18,8 @@ import (
"strings"
"time"
"github.com/hashicorp/packer/common/random"
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
"github.com/Azure/go-autorest/autorest/to"
"github.com/masterzen/winrm"
@ -529,7 +531,10 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
provideDefaultValues(c)
setRuntimeValues(c)
setUserNamePassword(c)
err = setUserNamePassword(c)
if err != nil {
return nil, err
}
// copy singular blocks
for _, kv := range c.AzureTag {
@ -653,23 +658,34 @@ func setRuntimeValues(c *Config) {
c.tmpKeyVaultName = tempName.KeyVaultName
}
func setUserNamePassword(c *Config) {
func setUserNamePassword(c *Config) error {
// SSH comm
if c.Comm.SSHUsername == "" {
c.Comm.SSHUsername = DefaultUserName
}
c.UserName = c.Comm.SSHUsername
if c.Comm.SSHPassword != "" {
c.Password = c.Comm.SSHPassword
return
return nil
}
// Configure password settings using Azure generated credentials
c.Password = c.tmpAdminPassword
if c.Comm.WinRMPassword == "" {
c.Comm.WinRMPassword = c.Password
// WinRM comm
if c.Comm.WinRMUser == "" {
c.Comm.WinRMUser = DefaultUserName
}
c.UserName = c.Comm.WinRMUser
if c.Comm.WinRMPassword == "" {
// Configure password settings using Azure generated credentials
c.Comm.WinRMPassword = c.tmpAdminPassword
}
if !isValidPassword(c.Comm.WinRMPassword) {
return fmt.Errorf("The supplied \"winrm_password\" must be between 8-123 characters long and must satisfy at least 3 from the following: \n1) Contains an uppercase character \n2) Contains a lowercase character\n3) Contains a numeric digit\n4) Contains a special character\n5) Control characters are not allowed")
}
c.Password = c.Comm.WinRMPassword
return nil
}
func setCustomData(c *Config) error {
@ -1039,6 +1055,34 @@ func isValidAzureName(re *regexp.Regexp, rgn string) bool {
!strings.HasSuffix(rgn, "-")
}
// The supplied password must be between 8-123 characters long and must satisfy at least 3 of password complexity requirements from the following:
// 1) Contains an uppercase character
// 2) Contains a lowercase character
// 3) Contains a numeric digit
// 4) Contains a special character
// 5) Control characters are not allowed (a very specific case - not included in this validation)
func isValidPassword(password string) bool {
if !(len(password) >= 8 && len(password) <= 123) {
return false
}
requirements := 0
if strings.ContainsAny(password, random.PossibleNumbers) {
requirements++
}
if strings.ContainsAny(password, random.PossibleLowerCase) {
requirements++
}
if strings.ContainsAny(password, random.PossibleUpperCase) {
requirements++
}
if strings.ContainsAny(password, random.PossibleSpecialCharacter) {
requirements++
}
return requirements >= 3
}
func (c *Config) validateLocationZoneResiliency(say func(s string)) {
// Docs on regions that support Availibility Zones:
// https://docs.microsoft.com/en-us/azure/availability-zones/az-overview#regions-that-support-availability-zones

View File

@ -71,22 +71,6 @@ func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) {
t.Fatalf("newConfig failed: %s", err)
}
if c.Password != "override_password" {
t.Errorf("Expected 'Password' to be set to 'override_password', but found %q!", c.Password)
}
if c.Comm.SSHPassword != "override_password" {
t.Errorf("Expected 'c.Comm.SSHPassword' to be set to 'override_password', but found %q!", c.Comm.SSHPassword)
}
if c.UserName != "override_username" {
t.Errorf("Expected 'UserName' to be set to 'override_username', but found %q!", c.UserName)
}
if c.Comm.SSHUsername != "override_username" {
t.Errorf("Expected 'c.Comm.SSHUsername' to be set to 'override_username', but found %q!", c.Comm.SSHUsername)
}
if c.VMSize != "override_vm_size" {
t.Errorf("Expected 'vm_size' to be set to 'override_vm_size', but found %q!", c.VMSize)
}
@ -98,6 +82,43 @@ func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) {
if c.diskCachingType != compute.CachingTypesNone {
t.Errorf("Expected 'disk_caching_type' to be set to 'None', but found %q!", c.diskCachingType)
}
// SSH comm
if c.Password != "override_password" {
t.Errorf("Expected 'Password' to be set to 'override_password', but found %q!", c.Password)
}
if c.Comm.SSHPassword != "override_password" {
t.Errorf("Expected 'c.Comm.SSHPassword' to be set to 'override_password', but found %q!", c.Comm.SSHPassword)
}
if c.UserName != "override_username" {
t.Errorf("Expected 'UserName' to be set to 'override_username', but found %q!", c.UserName)
}
if c.Comm.SSHUsername != "override_username" {
t.Errorf("Expected 'c.Comm.SSHUsername' to be set to 'override_username', but found %q!", c.Comm.SSHUsername)
}
// Winrm comm
c = Config{}
builderValues = getArmBuilderConfiguration()
builderValues["communicator"] = "winrm"
builderValues["winrm_password"] = "Override_winrm_password1"
builderValues["winrm_username"] = "override_winrm_username"
_, err = c.Prepare(builderValues, getPackerConfiguration())
if err != nil {
t.Fatalf("newConfig failed: %s", err)
}
if c.Password != "Override_winrm_password1" {
t.Errorf("Expected 'Password' to be set to 'Override_winrm_password1', but found %q!", c.Password)
}
if c.Comm.WinRMPassword != "Override_winrm_password1" {
t.Errorf("Expected 'c.Comm.WinRMPassword' to be set to 'Override_winrm_password1', but found %q!", c.Comm.SSHPassword)
}
if c.UserName != "override_winrm_username" {
t.Errorf("Expected 'UserName' to be set to 'override_winrm_username', but found %q!", c.UserName)
}
if c.Comm.WinRMUser != "override_winrm_username" {
t.Errorf("Expected 'c.Comm.WinRMUser' to be set to 'override_winrm_username', but found %q!", c.Comm.SSHUsername)
}
}
func TestConfigShouldDefaultVMSizeToStandardA1(t *testing.T) {
@ -574,7 +595,7 @@ func TestWinRMConfigShouldSetRoundTripDecorator(t *testing.T) {
config := getArmBuilderConfiguration()
config["communicator"] = "winrm"
config["winrm_username"] = "username"
config["winrm_password"] = "password"
config["winrm_password"] = "Password123"
var c Config
_, err := c.Prepare(config, getPackerConfiguration())
@ -1920,6 +1941,60 @@ func Test_GivenZoneSupportingResiliency_ConfigValidate_ShouldNotWarn(t *testing.
}
}
func TestConfig_PrepareProvidedWinRMPassword(t *testing.T) {
config := getArmBuilderConfiguration()
config["communicator"] = "winrm"
var c Config
tc := []struct {
name string
password string
shouldFail bool
}{
{
name: "password should be longer than 8 characters",
password: "packer",
shouldFail: true,
},
{
name: "password should be shorter than 123 characters",
password: "1Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
shouldFail: true,
},
{
name: "password should have valid size but only lower and upper case letters",
password: "AAAbbbCCC",
shouldFail: true,
},
{
name: "password should have valid size but only digits and upper case letters",
password: "AAA12345",
shouldFail: true,
},
{
name: "password should have valid size, digits, upper and lower case letters",
password: "AAA12345bbb",
shouldFail: false,
},
{
name: "password should have valid size, digits, special characters and lower case letters",
password: "//12345bbb",
shouldFail: false,
},
}
for _, tt := range tc {
t.Run(tt.name, func(t *testing.T) {
config["winrm_password"] = tt.password
_, err := c.Prepare(config)
fail := err != nil
if tt.shouldFail != fail {
t.Fatalf("bad: %s. Expected fail is: %t but it was %t", tt.name, tt.shouldFail, fail)
}
})
}
}
func getArmBuilderConfiguration() map[string]string {
m := make(map[string]string)
for _, v := range requiredConfigValues {

View File

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io/ioutil"
"strings"
"time"
"github.com/hashicorp/packer/helper/multistep"
@ -30,8 +31,9 @@ func (c *Config) createInstanceMetadata(sourceImage *Image, sshPublicKey string)
// supplied public key. This is possible if a private_key_file was
// specified.
if sshPublicKey != "" {
sshMetaKey := "sshKeys"
sshKeys := fmt.Sprintf("%s:%s", c.Comm.SSHUsername, sshPublicKey)
sshMetaKey := "ssh-keys"
sshPublicKey = strings.TrimSuffix(sshPublicKey, "\n")
sshKeys := fmt.Sprintf("%s:%s %s", c.Comm.SSHUsername, sshPublicKey, c.Comm.SSHUsername)
if confSshKeys, exists := instanceMetadata[sshMetaKey]; exists {
sshKeys = fmt.Sprintf("%s\n%s", sshKeys, confSshKeys)
}

View File

@ -308,14 +308,14 @@ func TestCreateInstanceMetadata(t *testing.T) {
assert.True(t, err == nil, "Metadata creation should have succeeded.")
// ensure our key is listed
assert.True(t, strings.Contains(metadata["sshKeys"], key), "Instance metadata should contain provided key")
assert.True(t, strings.Contains(metadata["ssh-keys"], key), "Instance metadata should contain provided key")
}
func TestCreateInstanceMetadata_noPublicKey(t *testing.T) {
state := testState(t)
c := state.Get("config").(*Config)
image := StubImage("test-image", "test-project", []string{}, 100)
sshKeys := c.Metadata["sshKeys"]
sshKeys := c.Metadata["ssh-keys"]
// create our metadata
metadata, err := c.createInstanceMetadata(image, "")
@ -323,7 +323,7 @@ func TestCreateInstanceMetadata_noPublicKey(t *testing.T) {
assert.True(t, err == nil, "Metadata creation should have succeeded.")
// ensure the ssh metadata hasn't changed
assert.Equal(t, metadata["sshKeys"], sshKeys, "Instance metadata should not have been modified")
assert.Equal(t, metadata["ssh-keys"], sshKeys, "Instance metadata should not have been modified")
}
func TestCreateInstanceMetadata_metadataFile(t *testing.T) {

View File

@ -40,6 +40,7 @@ type FlatConfig struct {
ImageMetadata map[string]string `mapstructure:"metadata" required:"false" cty:"metadata"`
ImageVisibility *images.ImageVisibility `mapstructure:"image_visibility" required:"false" cty:"image_visibility"`
ImageMembers []string `mapstructure:"image_members" required:"false" cty:"image_members"`
ImageAutoAcceptMembers *bool `mapstructure:"image_auto_accept_members" required:"false" cty:"image_auto_accept_members"`
ImageDiskFormat *string `mapstructure:"image_disk_format" required:"false" cty:"image_disk_format"`
ImageTags []string `mapstructure:"image_tags" required:"false" cty:"image_tags"`
ImageMinDisk *int `mapstructure:"image_min_disk" required:"false" cty:"image_min_disk"`
@ -158,6 +159,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"metadata": &hcldec.BlockAttrsSpec{TypeName: "metadata", ElementType: cty.String, Required: false},
"image_visibility": &hcldec.AttrSpec{Name: "image_visibility", Type: cty.String, Required: false},
"image_members": &hcldec.AttrSpec{Name: "image_members", Type: cty.List(cty.String), Required: false},
"image_auto_accept_members": &hcldec.AttrSpec{Name: "image_auto_accept_members", Type: cty.Bool, Required: false},
"image_disk_format": &hcldec.AttrSpec{Name: "image_disk_format", Type: cty.String, Required: false},
"image_tags": &hcldec.AttrSpec{Name: "image_tags", Type: cty.List(cty.String), Required: false},
"image_min_disk": &hcldec.AttrSpec{Name: "image_min_disk", Type: cty.Number, Required: false},

View File

@ -22,6 +22,10 @@ type ImageConfig struct {
// usually a project (also called the "tenant") with whom the image is
// shared.
ImageMembers []string `mapstructure:"image_members" required:"false"`
// When true, perform the image accept so the members can see the image in their
// project. This requires a user with priveleges both in the build project and
// in the members provided. Defaults to false.
ImageAutoAcceptMembers bool `mapstructure:"image_auto_accept_members" required:"false"`
// Disk format of the resulting image. This option works if
// use_blockstorage_volume is true.
ImageDiskFormat string `mapstructure:"image_disk_format" required:"false"`

View File

@ -37,6 +37,18 @@ func (s *stepAddImageMembers) Run(ctx context.Context, state multistep.StateBag)
}
}
if config.ImageAutoAcceptMembers {
for _, member := range config.ImageMembers {
ui.Say(fmt.Sprintf("Accepting image %s for member '%s'", imageId, member))
r := members.Update(imageClient, imageId, member, members.UpdateOpts{Status: "accepted"})
if _, err = r.Extract(); err != nil {
err = fmt.Errorf("Error accepting image for member: %s", err)
state.Put("error", err)
return multistep.ActionHalt
}
}
}
return multistep.ActionContinue
}

View File

@ -19,6 +19,7 @@ import (
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
ocicommon "github.com/oracle/oci-go-sdk/common"
ociauth "github.com/oracle/oci-go-sdk/common/auth"
)
type Config struct {
@ -27,6 +28,18 @@ type Config struct {
configProvider ocicommon.ConfigurationProvider
// Instance Principals (OPTIONAL)
// If set to true the following can't have non empty values
// - AccessCfgFile
// - AccessCfgFileAccount
// - UserID
// - TenancyID
// - Region
// - Fingerprint
// - KeyFile
// - PassPhrase
InstancePrincipals bool `mapstructure:"use_instance_principals"`
AccessCfgFile string `mapstructure:"access_cfg_file"`
AccessCfgFileAccount string `mapstructure:"access_cfg_file_account"`
@ -86,84 +99,134 @@ func (c *Config) Prepare(raws ...interface{}) error {
return fmt.Errorf("Failed to mapstructure Config: %+v", err)
}
// Determine where the SDK config is located
if c.AccessCfgFile == "" {
c.AccessCfgFile, err = getDefaultOCISettingsPath()
if err != nil {
log.Println("Default OCI settings file not found")
}
}
if c.AccessCfgFileAccount == "" {
c.AccessCfgFileAccount = "DEFAULT"
}
var keyContent []byte
if c.KeyFile != "" {
path, err := packer.ExpandUser(c.KeyFile)
if err != nil {
return err
}
// Read API signing key
keyContent, err = ioutil.ReadFile(path)
if err != nil {
return err
}
}
fileProvider, _ := ocicommon.ConfigurationProviderFromFileWithProfile(c.AccessCfgFile, c.AccessCfgFileAccount, c.PassPhrase)
if c.Region == "" {
var region string
if fileProvider != nil {
region, _ = fileProvider.Region()
}
if region == "" {
c.Region = "us-phoenix-1"
}
}
providers := []ocicommon.ConfigurationProvider{
NewRawConfigurationProvider(c.TenancyID, c.UserID, c.Region, c.Fingerprint, string(keyContent), &c.PassPhrase),
}
if fileProvider != nil {
providers = append(providers, fileProvider)
}
// Load API access configuration from SDK
configProvider, err := ocicommon.ComposingConfigurationProvider(providers)
if err != nil {
return err
}
var errs *packer.MultiError
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
errs = packer.MultiErrorAppend(errs, es...)
}
if userOCID, _ := configProvider.UserOCID(); userOCID == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("'user_ocid' must be specified"))
}
var tenancyOCID string
tenancyOCID, _ := configProvider.TenancyOCID()
if tenancyOCID == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("'tenancy_ocid' must be specified"))
}
if c.InstancePrincipals {
// We could go through all keys in one go and report that the below set
// of keys cannot coexist with use_instance_principals but decided to
// split them and report them seperately so that the user sees the specific
// key involved.
var message string = " cannot be present when use_instance_principals is set to true."
if c.AccessCfgFile != "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("access_cfg_file"+message))
}
if c.AccessCfgFileAccount != "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("access_cfg_file_account"+message))
}
if c.UserID != "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("user_ocid"+message))
}
if c.TenancyID != "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("tenancy_ocid"+message))
}
if c.Region != "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("region"+message))
}
if c.Fingerprint != "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("fingerprint"+message))
}
if c.KeyFile != "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("key_file"+message))
}
if c.PassPhrase != "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("pass_phrase"+message))
}
// This check is used to facilitate testing. During testing a Mock struct
// is assigned to c.configProvider otherwise testing fails because Instance
// Principals cannot be obtained.
if c.configProvider == nil {
// Even though the previous configuraion checks might fail we don't want
// to skip this step. It seems that the logic behind the checks in this
// file is to check everything even getting the configProvider.
c.configProvider, err = ociauth.InstancePrincipalConfigurationProvider()
if err != nil {
return err
}
}
tenancyOCID, err = c.configProvider.TenancyOCID()
if err != nil {
return err
}
} else {
// Determine where the SDK config is located
if c.AccessCfgFile == "" {
c.AccessCfgFile, err = getDefaultOCISettingsPath()
if err != nil {
log.Println("Default OCI settings file not found")
}
}
if fingerprint, _ := configProvider.KeyFingerprint(); fingerprint == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("'fingerprint' must be specified"))
}
if c.AccessCfgFileAccount == "" {
c.AccessCfgFileAccount = "DEFAULT"
}
if _, err := configProvider.PrivateRSAKey(); err != nil {
errs = packer.MultiErrorAppend(
errs, errors.New("'key_file' must be specified"))
}
var keyContent []byte
if c.KeyFile != "" {
path, err := packer.ExpandUser(c.KeyFile)
if err != nil {
return err
}
c.configProvider = configProvider
// Read API signing key
keyContent, err = ioutil.ReadFile(path)
if err != nil {
return err
}
}
fileProvider, _ := ocicommon.ConfigurationProviderFromFileWithProfile(c.AccessCfgFile, c.AccessCfgFileAccount, c.PassPhrase)
if c.Region == "" {
var region string
if fileProvider != nil {
region, _ = fileProvider.Region()
}
if region == "" {
c.Region = "us-phoenix-1"
}
}
providers := []ocicommon.ConfigurationProvider{
NewRawConfigurationProvider(c.TenancyID, c.UserID, c.Region, c.Fingerprint, string(keyContent), &c.PassPhrase),
}
if fileProvider != nil {
providers = append(providers, fileProvider)
}
// Load API access configuration from SDK
configProvider, err := ocicommon.ComposingConfigurationProvider(providers)
if err != nil {
return err
}
if userOCID, _ := configProvider.UserOCID(); userOCID == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("'user_ocid' must be specified"))
}
tenancyOCID, _ = configProvider.TenancyOCID()
if tenancyOCID == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("'tenancy_ocid' must be specified"))
}
if fingerprint, _ := configProvider.KeyFingerprint(); fingerprint == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("'fingerprint' must be specified"))
}
if _, err := configProvider.PrivateRSAKey(); err != nil {
errs = packer.MultiErrorAppend(
errs, errors.New("'key_file' must be specified"))
}
c.configProvider = configProvider
}
if c.AvailabilityDomain == "" {
errs = packer.MultiErrorAppend(

View File

@ -57,6 +57,7 @@ type FlatConfig struct {
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"`
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"`
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"`
InstancePrincipals *bool `mapstructure:"use_instance_principals" cty:"use_instance_principals"`
AccessCfgFile *string `mapstructure:"access_cfg_file" cty:"access_cfg_file"`
AccessCfgFileAccount *string `mapstructure:"access_cfg_file_account" cty:"access_cfg_file_account"`
UserID *string `mapstructure:"user_ocid" cty:"user_ocid"`
@ -140,6 +141,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
"use_instance_principals": &hcldec.AttrSpec{Name: "use_instance_principals", Type: cty.Bool, Required: false},
"access_cfg_file": &hcldec.AttrSpec{Name: "access_cfg_file", Type: cty.String, Required: false},
"access_cfg_file_account": &hcldec.AttrSpec{Name: "access_cfg_file_account", Type: cty.String, Required: false},
"user_ocid": &hcldec.AttrSpec{Name: "user_ocid", Type: cty.String, Required: false},

View File

@ -15,6 +15,7 @@ import (
func testConfig(accessConfFile *os.File) map[string]interface{} {
return map[string]interface{}{
"availability_domain": "aaaa:PHX-AD-3",
"access_cfg_file": accessConfFile.Name(),
@ -252,6 +253,36 @@ func TestConfig(t *testing.T) {
t.Errorf("Expected ConfigProvider.KeyFingerprint: %s, got %s", expected, fingerprint)
}
})
// Test the correct errors are produced when certain template keys
// are present alongside use_instance_principals key.
invalidKeys := []string{
"access_cfg_file",
"access_cfg_file_account",
"user_ocid",
"tenancy_ocid",
"region",
"fingerprint",
"key_file",
"pass_phrase",
}
for _, k := range invalidKeys {
t.Run(k+"_mixed_with_use_instance_principals", func(t *testing.T) {
raw := testConfig(cfgFile)
raw["use_instance_principals"] = "true"
raw[k] = "some_random_value"
var c Config
c.configProvider = instancePrincipalConfigurationProviderMock{}
errs := c.Prepare(raw)
if !strings.Contains(errs.Error(), k) {
t.Errorf("Expected '%s' to contain '%s'", errs.Error(), k)
}
})
}
}
// BaseTestConfig creates the base (DEFAULT) config including a temporary key

View File

@ -0,0 +1,34 @@
package oci
import (
"crypto/rand"
"crypto/rsa"
)
// Mock struct to be used during testing to obtain Instance Principals.
type instancePrincipalConfigurationProviderMock struct {
}
func (p instancePrincipalConfigurationProviderMock) PrivateRSAKey() (*rsa.PrivateKey, error) {
return rsa.GenerateKey(rand.Reader, 1024)
}
func (p instancePrincipalConfigurationProviderMock) KeyID() (string, error) {
return "some_random_key_id", nil
}
func (p instancePrincipalConfigurationProviderMock) TenancyOCID() (string, error) {
return "some_random_tenancy", nil
}
func (p instancePrincipalConfigurationProviderMock) UserOCID() (string, error) {
return "", nil
}
func (p instancePrincipalConfigurationProviderMock) KeyFingerprint() (string, error) {
return "", nil
}
func (p instancePrincipalConfigurationProviderMock) Region() (string, error) {
return "some_random_region", nil
}

View File

@ -52,6 +52,7 @@ type Config struct {
ISOStoragePool string `mapstructure:"iso_storage_pool"`
Agent bool `mapstructure:"qemu_agent"`
SCSIController string `mapstructure:"scsi_controller"`
Onboot bool `mapstructure:"onboot"`
TemplateName string `mapstructure:"template_name"`
TemplateDescription string `mapstructure:"template_description"`

View File

@ -91,6 +91,7 @@ type FlatConfig struct {
ISOStoragePool *string `mapstructure:"iso_storage_pool" cty:"iso_storage_pool"`
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent"`
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller"`
Onboot *bool `mapstructure:"onboot" cty:"onboot"`
TemplateName *string `mapstructure:"template_name" cty:"template_name"`
TemplateDescription *string `mapstructure:"template_description" cty:"template_description"`
UnmountISO *bool `mapstructure:"unmount_iso" cty:"unmount_iso"`
@ -190,6 +191,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"iso_storage_pool": &hcldec.AttrSpec{Name: "iso_storage_pool", Type: cty.String, Required: false},
"qemu_agent": &hcldec.AttrSpec{Name: "qemu_agent", Type: cty.Bool, Required: false},
"scsi_controller": &hcldec.AttrSpec{Name: "scsi_controller", Type: cty.String, Required: false},
"onboot": &hcldec.AttrSpec{Name: "onboot", Type: cty.Bool, Required: false},
"template_name": &hcldec.AttrSpec{Name: "template_name", Type: cty.String, Required: false},
"template_description": &hcldec.AttrSpec{Name: "template_description", Type: cty.String, Required: false},
"unmount_iso": &hcldec.AttrSpec{Name: "unmount_iso", Type: cty.Bool, Required: false},

View File

@ -44,6 +44,7 @@ func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multist
QemuNetworks: generateProxmoxNetworkAdapters(c.NICs),
QemuDisks: generateProxmoxDisks(c.Disks),
Scsihw: c.SCSIController,
Onboot: c.Onboot,
}
if c.VMID == 0 {

View File

@ -39,31 +39,6 @@ var accels = map[string]struct{}{
"whpx": {},
}
var netDevice = map[string]bool{
"ne2k_pci": true,
"i82551": true,
"i82557b": true,
"i82559er": true,
"rtl8139": true,
"e1000": true,
"pcnet": true,
"virtio": true,
"virtio-net": true,
"virtio-net-pci": true,
"usb-net": true,
"i82559a": true,
"i82559b": true,
"i82559c": true,
"i82550": true,
"i82562": true,
"i82557a": true,
"i82557c": true,
"i82801": true,
"vmxnet3": true,
"i82558a": true,
"i82558b": true,
}
var diskInterface = map[string]bool{
"ide": true,
"scsi": true,
@ -526,11 +501,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
errs, errors.New("invalid accelerator, only 'kvm', 'tcg', 'xen', 'hax', 'hvf', 'whpx', or 'none' are allowed"))
}
if _, ok := netDevice[b.config.NetDevice]; !ok {
errs = packer.MultiErrorAppend(
errs, errors.New("unrecognized network device type"))
}
if _, ok := diskInterface[b.config.DiskInterface]; !ok {
errs = packer.MultiErrorAppend(
errs, errors.New("unrecognized disk interface type"))

View File

@ -16,6 +16,9 @@ import (
)
type DriverConfig struct {
// When set to true, Packer will cleanup the cache folder where the ISO file is stored during the build on the remote machine.
// By default, this is set to false.
CleanUpRemoteCache bool `mapstructure:"cleanup_remote_cache" required:"false"`
// Path to "VMware Fusion.app". By default this is
// /Applications/VMware Fusion.app but this setting allows you to
// customize this.

View File

@ -26,6 +26,9 @@ type RemoteDriverMock struct {
UploadErr error
DownloadErr error
RemovedCachePath string
CacheRemoved bool
ReloadVMErr error
}
@ -66,6 +69,8 @@ func (d *RemoteDriverMock) Download(src, dst string) error {
}
func (d *RemoteDriverMock) RemoveCache(localPath string) error {
d.RemovedCachePath = localPath
d.CacheRemoved = true
return nil
}

View File

@ -0,0 +1,40 @@
package common
import (
"testing"
"github.com/hashicorp/packer/helper/multistep"
)
func TestStepRemoteUpload_Cleanup(t *testing.T) {
state := new(multistep.BasicStateBag)
driver := new(RemoteDriverMock)
state.Put("driver", driver)
state.Put("path_key", "packer_cache")
// Should clean up cache
s := &StepRemoteUpload{
Key: "path_key",
DoCleanup: true,
}
s.Cleanup(state)
if !driver.CacheRemoved {
t.Fatalf("bad: remote cache was not removed")
}
if driver.RemovedCachePath != "packer_cache" {
t.Fatalf("bad: removed cache path was expected to be packer_cache but was %s", driver.RemovedCachePath)
}
// Should NOT clean up cache
s = &StepRemoteUpload{
Key: "path_key",
}
driver = new(RemoteDriverMock)
state.Put("driver", driver)
s.Cleanup(state)
if driver.CacheRemoved {
t.Fatalf("bad: remote cache was removed but was expected to not")
}
}

View File

@ -98,6 +98,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&vmwcommon.StepRemoteUpload{
Key: "iso_path",
Message: "Uploading ISO to remote machine...",
DoCleanup: b.config.DriverConfig.CleanUpRemoteCache,
Checksum: b.config.ISOChecksum,
ChecksumType: b.config.ISOChecksumType,
},

View File

@ -34,6 +34,7 @@ type FlatConfig struct {
BootCommand []string `mapstructure:"boot_command" cty:"boot_command"`
DisableVNC *bool `mapstructure:"disable_vnc" cty:"disable_vnc"`
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval"`
CleanUpRemoteCache *bool `mapstructure:"cleanup_remote_cache" required:"false" cty:"cleanup_remote_cache"`
FusionAppPath *string `mapstructure:"fusion_app_path" required:"false" cty:"fusion_app_path"`
RemoteType *string `mapstructure:"remote_type" required:"false" cty:"remote_type"`
RemoteDatastore *string `mapstructure:"remote_datastore" required:"false" cty:"remote_datastore"`
@ -165,6 +166,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
"disable_vnc": &hcldec.AttrSpec{Name: "disable_vnc", Type: cty.Bool, Required: false},
"boot_key_interval": &hcldec.AttrSpec{Name: "boot_key_interval", Type: cty.String, Required: false},
"cleanup_remote_cache": &hcldec.AttrSpec{Name: "cleanup_remote_cache", Type: cty.Bool, Required: false},
"fusion_app_path": &hcldec.AttrSpec{Name: "fusion_app_path", Type: cty.String, Required: false},
"remote_type": &hcldec.AttrSpec{Name: "remote_type", Type: cty.String, Required: false},
"remote_datastore": &hcldec.AttrSpec{Name: "remote_datastore", Type: cty.String, Required: false},

View File

@ -27,6 +27,7 @@ type FlatConfig struct {
BootCommand []string `mapstructure:"boot_command" cty:"boot_command"`
DisableVNC *bool `mapstructure:"disable_vnc" cty:"disable_vnc"`
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval"`
CleanUpRemoteCache *bool `mapstructure:"cleanup_remote_cache" required:"false" cty:"cleanup_remote_cache"`
FusionAppPath *string `mapstructure:"fusion_app_path" required:"false" cty:"fusion_app_path"`
RemoteType *string `mapstructure:"remote_type" required:"false" cty:"remote_type"`
RemoteDatastore *string `mapstructure:"remote_datastore" required:"false" cty:"remote_datastore"`
@ -134,6 +135,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
"disable_vnc": &hcldec.AttrSpec{Name: "disable_vnc", Type: cty.Bool, Required: false},
"boot_key_interval": &hcldec.AttrSpec{Name: "boot_key_interval", Type: cty.String, Required: false},
"cleanup_remote_cache": &hcldec.AttrSpec{Name: "cleanup_remote_cache", Type: cty.Bool, Required: false},
"fusion_app_path": &hcldec.AttrSpec{Name: "fusion_app_path", Type: cty.String, Required: false},
"remote_type": &hcldec.AttrSpec{Name: "remote_type", Type: cty.String, Required: false},
"remote_datastore": &hcldec.AttrSpec{Name: "remote_datastore", Type: cty.String, Required: false},

View File

@ -82,6 +82,17 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
},
)
if b.config.Export != nil {
steps = append(steps, &common.StepExport{
Name: b.config.Export.Name,
Force: b.config.Export.Force,
Images: b.config.Export.Images,
Manifest: b.config.Export.Manifest,
OutputDir: b.config.Export.OutputDir.OutputDir,
Options: b.config.Export.Options,
})
}
b.runner = packerCommon.NewRunner(steps, b.config.PackerConfig, ui)
b.runner.Run(ctx, state)

View File

@ -32,6 +32,8 @@ type Config struct {
// Convert VM to a template. Defaults to `false`.
ConvertToTemplate bool `mapstructure:"convert_to_template"`
Export *common.ExportConfig `mapstructure:"export"`
ctx interpolate.Context
}
@ -53,6 +55,9 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend(errs, c.WaitIpConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare()...)
if c.Export != nil {
errs = packer.MultiErrorAppend(errs, c.Export.Prepare(&c.ctx, &c.LocationConfig, &c.PackerConfig)...)
}
if len(errs.Errors) > 0 {
return nil, errs

View File

@ -3,95 +3,97 @@ package clone
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer/builder/vsphere/common"
"github.com/zclconf/go-cty/cty"
)
// FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"`
VCenterServer *string `mapstructure:"vcenter_server" cty:"vcenter_server"`
Username *string `mapstructure:"username" cty:"username"`
Password *string `mapstructure:"password" cty:"password"`
InsecureConnection *bool `mapstructure:"insecure_connection" cty:"insecure_connection"`
Datacenter *string `mapstructure:"datacenter" cty:"datacenter"`
Template *string `mapstructure:"template" cty:"template"`
DiskSize *int64 `mapstructure:"disk_size" cty:"disk_size"`
LinkedClone *bool `mapstructure:"linked_clone" cty:"linked_clone"`
Network *string `mapstructure:"network" cty:"network"`
Notes *string `mapstructure:"notes" cty:"notes"`
VMName *string `mapstructure:"vm_name" cty:"vm_name"`
Folder *string `mapstructure:"folder" cty:"folder"`
Cluster *string `mapstructure:"cluster" cty:"cluster"`
Host *string `mapstructure:"host" cty:"host"`
ResourcePool *string `mapstructure:"resource_pool" cty:"resource_pool"`
Datastore *string `mapstructure:"datastore" cty:"datastore"`
CPUs *int32 `mapstructure:"CPUs" cty:"CPUs"`
CpuCores *int32 `mapstructure:"cpu_cores" cty:"cpu_cores"`
CPUReservation *int64 `mapstructure:"CPU_reservation" cty:"CPU_reservation"`
CPULimit *int64 `mapstructure:"CPU_limit" cty:"CPU_limit"`
CpuHotAddEnabled *bool `mapstructure:"CPU_hot_plug" cty:"CPU_hot_plug"`
RAM *int64 `mapstructure:"RAM" cty:"RAM"`
RAMReservation *int64 `mapstructure:"RAM_reservation" cty:"RAM_reservation"`
RAMReserveAll *bool `mapstructure:"RAM_reserve_all" cty:"RAM_reserve_all"`
MemoryHotAddEnabled *bool `mapstructure:"RAM_hot_plug" cty:"RAM_hot_plug"`
VideoRAM *int64 `mapstructure:"video_ram" cty:"video_ram"`
NestedHV *bool `mapstructure:"NestedHV" cty:"NestedHV"`
ConfigParams map[string]string `mapstructure:"configuration_parameters" cty:"configuration_parameters"`
BootOrder *string `mapstructure:"boot_order" cty:"boot_order"`
WaitTimeout *string `mapstructure:"ip_wait_timeout" cty:"ip_wait_timeout"`
SettleTimeout *string `mapstructure:"ip_settle_timeout" cty:"ip_settle_timeout"`
Type *string `mapstructure:"communicator" cty:"communicator"`
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"`
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"`
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"`
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"`
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"`
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"`
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_name"`
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"`
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"`
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"`
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"`
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"`
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"`
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"`
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"`
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"`
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"`
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"`
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"`
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive"`
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"`
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"`
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"`
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"`
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"`
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"`
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"`
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"`
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"`
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"`
SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"`
SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"`
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"`
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"`
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"`
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"`
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"`
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"`
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"`
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"`
Command *string `mapstructure:"shutdown_command" cty:"shutdown_command"`
Timeout *string `mapstructure:"shutdown_timeout" cty:"shutdown_timeout"`
CreateSnapshot *bool `mapstructure:"create_snapshot" cty:"create_snapshot"`
ConvertToTemplate *bool `mapstructure:"convert_to_template" cty:"convert_to_template"`
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"`
VCenterServer *string `mapstructure:"vcenter_server" cty:"vcenter_server"`
Username *string `mapstructure:"username" cty:"username"`
Password *string `mapstructure:"password" cty:"password"`
InsecureConnection *bool `mapstructure:"insecure_connection" cty:"insecure_connection"`
Datacenter *string `mapstructure:"datacenter" cty:"datacenter"`
Template *string `mapstructure:"template" cty:"template"`
DiskSize *int64 `mapstructure:"disk_size" cty:"disk_size"`
LinkedClone *bool `mapstructure:"linked_clone" cty:"linked_clone"`
Network *string `mapstructure:"network" cty:"network"`
Notes *string `mapstructure:"notes" cty:"notes"`
VMName *string `mapstructure:"vm_name" cty:"vm_name"`
Folder *string `mapstructure:"folder" cty:"folder"`
Cluster *string `mapstructure:"cluster" cty:"cluster"`
Host *string `mapstructure:"host" cty:"host"`
ResourcePool *string `mapstructure:"resource_pool" cty:"resource_pool"`
Datastore *string `mapstructure:"datastore" cty:"datastore"`
CPUs *int32 `mapstructure:"CPUs" cty:"CPUs"`
CpuCores *int32 `mapstructure:"cpu_cores" cty:"cpu_cores"`
CPUReservation *int64 `mapstructure:"CPU_reservation" cty:"CPU_reservation"`
CPULimit *int64 `mapstructure:"CPU_limit" cty:"CPU_limit"`
CpuHotAddEnabled *bool `mapstructure:"CPU_hot_plug" cty:"CPU_hot_plug"`
RAM *int64 `mapstructure:"RAM" cty:"RAM"`
RAMReservation *int64 `mapstructure:"RAM_reservation" cty:"RAM_reservation"`
RAMReserveAll *bool `mapstructure:"RAM_reserve_all" cty:"RAM_reserve_all"`
MemoryHotAddEnabled *bool `mapstructure:"RAM_hot_plug" cty:"RAM_hot_plug"`
VideoRAM *int64 `mapstructure:"video_ram" cty:"video_ram"`
NestedHV *bool `mapstructure:"NestedHV" cty:"NestedHV"`
ConfigParams map[string]string `mapstructure:"configuration_parameters" cty:"configuration_parameters"`
BootOrder *string `mapstructure:"boot_order" cty:"boot_order"`
WaitTimeout *string `mapstructure:"ip_wait_timeout" cty:"ip_wait_timeout"`
SettleTimeout *string `mapstructure:"ip_settle_timeout" cty:"ip_settle_timeout"`
Type *string `mapstructure:"communicator" cty:"communicator"`
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"`
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"`
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"`
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"`
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"`
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"`
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_name"`
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"`
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"`
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"`
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"`
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"`
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"`
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"`
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"`
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"`
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"`
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"`
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"`
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive"`
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"`
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"`
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"`
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"`
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"`
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"`
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"`
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"`
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"`
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"`
SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"`
SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"`
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"`
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"`
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"`
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"`
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"`
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"`
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"`
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"`
Command *string `mapstructure:"shutdown_command" cty:"shutdown_command"`
Timeout *string `mapstructure:"shutdown_timeout" cty:"shutdown_timeout"`
CreateSnapshot *bool `mapstructure:"create_snapshot" cty:"create_snapshot"`
ConvertToTemplate *bool `mapstructure:"convert_to_template" cty:"convert_to_template"`
Export *common.FlatExportConfig `mapstructure:"export" cty:"export"`
}
// FlatMapstructure returns a new FlatConfig.
@ -189,6 +191,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
"create_snapshot": &hcldec.AttrSpec{Name: "create_snapshot", Type: cty.Bool, Required: false},
"convert_to_template": &hcldec.AttrSpec{Name: "convert_to_template", Type: cty.Bool, Required: false},
"export": &hcldec.BlockSpec{TypeName: "export", Nested: hcldec.ObjectSpec((*common.FlatExportConfig)(nil).HCL2Spec())},
}
return s
}

View File

@ -49,17 +49,18 @@ type StepCloneVM struct {
func (s *StepCloneVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
d := state.Get("driver").(*driver.Driver)
vmPath := fmt.Sprintf("%s/%s", s.Location.Folder, s.Location.VMName)
vm, err := d.FindVM(s.Location.VMName)
vm, err := d.FindVM(vmPath)
if s.Force == false && err == nil {
state.Put("error", fmt.Errorf("%s already exists, you can use -force flag to destroy it", s.Location.VMName))
state.Put("error", fmt.Errorf("%s already exists, you can use -force flag to destroy it", vmPath))
return multistep.ActionHalt
} else if s.Force == true && err == nil {
ui.Say(fmt.Sprintf("the vm/template %s already exists, but deleting it due to -force flag", s.Location.VMName))
ui.Say(fmt.Sprintf("the vm/template %s already exists, but deleting it due to -force flag", vmPath))
err := vm.Destroy()
if err != nil {
state.Put("error", fmt.Errorf("error destroying %s: %v", s.Location.VMName, err))
state.Put("error", fmt.Errorf("error destroying %s: %v", vmPath, err))
}
}

View File

@ -0,0 +1,30 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type OutputConfig
package common
import (
"fmt"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/template/interpolate"
)
type OutputConfig struct {
// This setting specifies the directory that
// artifacts from the build, such as the virtual machine files and disks,
// will be output to. The path to the directory may be relative or
// absolute. If relative, the path is relative to the working directory
// packer is executed from. This directory must not exist or, if
// created, must be empty prior to running the builder. By default this is
// "output-BUILDNAME" where "BUILDNAME" is the name of the build.
OutputDir string `mapstructure:"output_directory" required:"false"`
}
func (c *OutputConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig) []error {
if c.OutputDir == "" {
c.OutputDir = fmt.Sprintf("output-%s", pc.PackerBuildName)
}
return nil
}

View File

@ -0,0 +1,30 @@
// Code generated by "mapstructure-to-hcl2 -type OutputConfig"; DO NOT EDIT.
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatOutputConfig is an auto-generated flat version of OutputConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatOutputConfig struct {
OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory"`
}
// FlatMapstructure returns a new FlatOutputConfig.
// FlatOutputConfig is an auto-generated flat version of OutputConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*OutputConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatOutputConfig)
}
// HCL2Spec returns the hcl spec of a OutputConfig.
// This spec is used by HCL to read the fields of OutputConfig.
// The decoded values from this spec will then be applied to a FlatOutputConfig.
func (*FlatOutputConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false},
}
return s
}

View File

@ -0,0 +1,304 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type ExportConfig
package common
import (
"bytes"
"context"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"fmt"
"hash"
"io"
"os"
"path/filepath"
"strings"
"github.com/hashicorp/packer/builder/vsphere/driver"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/pkg/errors"
"github.com/vmware/govmomi/nfc"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
// You may optionally export an ovf from VSphere to the instance running Packer.
//
// Example usage:
//
// ```json
// ...
// "vm_name": "example-ubuntu",
// ...
// "export": {
// "force": true,
// "output_directory": "./output_vsphere"
// },
// ```
// The above configuration would create the following files:
//
// ```
// ./output_vsphere/example-ubuntu-disk-0.vmdk
// ./output_vsphere/example-ubuntu.mf
// ./output_vsphere/example-ubuntu.ovf
// ```
type ExportConfig struct {
// name of the ovf. defaults to the name of the VM
Name string `mapstructure:"name"`
// overwrite ovf if it exists
Force bool `mapstructure:"force"`
// include iso and img image files that are attached to the VM
Images bool `mapstructure:"images"`
// generate manifest using sha1, sha256, sha512. Defaults to 'sha256'. Use 'none' for no manifest.
Manifest string `mapstructure:"manifest"`
OutputDir OutputConfig `mapstructure:",squash"`
// Advanced ovf export options. Options can include:
// * mac - MAC address is exported for all ethernet devices
// * uuid - UUID is exported for all virtual machines
// * extraconfig - all extra configuration options are exported for a virtual machine
// * nodevicesubtypes - resource subtypes for CD/DVD drives, floppy drives, and serial and parallel ports are not exported
//
// For example, adding the following export config option would output the mac addresses for all Ethernet devices in the ovf file:
//
// ```json
// ...
// "export": {
// "options": ["mac"]
// },
// ```
Options []string `mapstructure:"options"`
}
var sha = map[string]func() hash.Hash{
"none": nil,
"sha1": sha1.New,
"sha256": sha256.New,
"sha512": sha512.New,
}
func (c *ExportConfig) Prepare(ctx *interpolate.Context, lc *LocationConfig, pc *common.PackerConfig) []error {
var errs *packer.MultiError
errs = packer.MultiErrorAppend(errs, c.OutputDir.Prepare(ctx, pc)...)
// manifest should default to sha256
if c.Manifest == "" {
c.Manifest = "sha256"
}
if _, ok := sha[c.Manifest]; !ok {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("unknown hash: %s. available options include available options being 'none', 'sha1', 'sha256', 'sha512'", c.Manifest))
}
if c.Name == "" {
c.Name = lc.VMName
}
target := getTarget(c.OutputDir.OutputDir, c.Name)
if !c.Force {
if _, err := os.Stat(target); err == nil {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("file already exists: %s", target))
}
}
if err := os.MkdirAll(c.OutputDir.OutputDir, 0750); err != nil {
errs = packer.MultiErrorAppend(errs, errors.Wrap(err, "unable to make directory for export"))
}
if errs != nil && len(errs.Errors) > 0 {
return errs.Errors
}
return nil
}
func getTarget(dir string, name string) string {
return filepath.Join(dir, name+".ovf")
}
type StepExport struct {
Name string
Force bool
Images bool
Manifest string
OutputDir string
Options []string
mf bytes.Buffer
}
func (s *StepExport) Cleanup(multistep.StateBag) {
}
func (s *StepExport) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
vm := state.Get("vm").(*driver.VirtualMachine)
ui.Message("Starting export...")
lease, err := vm.Export()
if err != nil {
state.Put("error", errors.Wrap(err, "error exporting vm"))
return multistep.ActionHalt
}
info, err := lease.Wait(ctx, nil)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
u := lease.StartUpdater(ctx, info)
defer u.Done()
cdp := types.OvfCreateDescriptorParams{
Name: s.Name,
}
m := vm.NewOvfManager()
if len(s.Options) > 0 {
exportOptions, err := vm.GetOvfExportOptions(m)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
var unknown []string
for _, option := range s.Options {
found := false
for _, exportOpt := range exportOptions {
if exportOpt.Option == option {
found = true
break
}
}
if !found {
unknown = append(unknown, option)
}
cdp.ExportOption = append(cdp.ExportOption, option)
}
// only printing error message because the unknown options are just ignored by vcenter
if len(unknown) > 0 {
ui.Error(fmt.Sprintf("unknown export options %s", strings.Join(unknown, ",")))
}
}
for _, i := range info.Items {
if !s.include(&i) {
continue
}
if !strings.HasPrefix(i.Path, s.Name) {
i.Path = s.Name + "-" + i.Path
}
ui.Message("Downloading: " + i.File().Path)
err = s.Download(ctx, lease, i)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
ui.Message("Exporting file: " + i.File().Path)
cdp.OvfFiles = append(cdp.OvfFiles, i.File())
}
if err = lease.Complete(ctx); err != nil {
state.Put("error", errors.Wrap(err, "unable to complete lease"))
return multistep.ActionHalt
}
desc, err := vm.CreateDescriptor(m, cdp)
if err != nil {
state.Put("error", errors.Wrap(err, "unable to create descriptor"))
return multistep.ActionHalt
}
target := getTarget(s.OutputDir, s.Name)
file, err := os.Create(target)
if err != nil {
state.Put("error", errors.Wrap(err, "unable to create file: "+target))
return multistep.ActionHalt
}
var w io.Writer = file
h, ok := s.newHash()
if ok {
w = io.MultiWriter(file, h)
}
ui.Message("Writing ovf...")
_, err = io.WriteString(w, desc.OvfDescriptor)
if err != nil {
state.Put("error", errors.Wrap(err, "unable to write descriptor"))
return multistep.ActionHalt
}
if err = file.Close(); err != nil {
state.Put("error", errors.Wrap(err, "unable to close descriptor"))
return multistep.ActionHalt
}
if s.Manifest == "none" {
// manifest does not need to be created, return
return multistep.ActionContinue
}
ui.Message("Creating manifest...")
s.addHash(filepath.Base(target), h)
file, err = os.Create(filepath.Join(s.OutputDir, s.Name+".mf"))
if err != nil {
state.Put("error", errors.Wrap(err, "unable to create manifest"))
return multistep.ActionHalt
}
_, err = io.Copy(file, &s.mf)
if err != nil {
state.Put("error", errors.Wrap(err, "unable to write manifest"))
return multistep.ActionHalt
}
err = file.Close()
if err != nil {
state.Put("error", errors.Wrap(err, "unable to close file"))
return multistep.ActionHalt
}
ui.Message("Finished exporting...")
return multistep.ActionContinue
}
func (s *StepExport) include(item *nfc.FileItem) bool {
if s.Images {
return true
}
return filepath.Ext(item.Path) == ".vmdk"
}
func (s *StepExport) newHash() (hash.Hash, bool) {
// check if function is nil to handle the 'none' case
if h, ok := sha[s.Manifest]; ok && h != nil {
return h(), true
}
return nil, false
}
func (s *StepExport) addHash(p string, h hash.Hash) {
_, _ = fmt.Fprintf(&s.mf, "%s(%s)= %x\n", strings.ToUpper(s.Manifest), p, h.Sum(nil))
}
func (s *StepExport) Download(ctx context.Context, lease *nfc.Lease, item nfc.FileItem) error {
path := filepath.Join(s.OutputDir, item.Path)
opts := soap.Download{}
if h, ok := s.newHash(); ok {
opts.Writer = h
defer s.addHash(item.Path, h)
}
return lease.DownloadFile(ctx, path, item, opts)
}

View File

@ -0,0 +1,40 @@
// Code generated by "mapstructure-to-hcl2 -type ExportConfig"; DO NOT EDIT.
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatExportConfig is an auto-generated flat version of ExportConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatExportConfig struct {
Name *string `mapstructure:"name" cty:"name"`
Force *bool `mapstructure:"force" cty:"force"`
Images *bool `mapstructure:"images" cty:"images"`
Manifest *string `mapstructure:"manifest" cty:"manifest"`
OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory"`
Options []string `mapstructure:"options" cty:"options"`
}
// FlatMapstructure returns a new FlatExportConfig.
// FlatExportConfig is an auto-generated flat version of ExportConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*ExportConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatExportConfig)
}
// HCL2Spec returns the hcl spec of a ExportConfig.
// This spec is used by HCL to read the fields of ExportConfig.
// The decoded values from this spec will then be applied to a FlatExportConfig.
func (*FlatExportConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false},
"force": &hcldec.AttrSpec{Name: "force", Type: cty.Bool, Required: false},
"images": &hcldec.AttrSpec{Name: "images", Type: cty.Bool, Required: false},
"manifest": &hcldec.AttrSpec{Name: "manifest", Type: cty.String, Required: false},
"output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false},
"options": &hcldec.AttrSpec{Name: "options", Type: cty.List(cty.String), Required: false},
}
return s
}

View File

@ -8,6 +8,11 @@ import (
"strings"
"time"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/nfc"
"github.com/vmware/govmomi/ovf"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
@ -688,6 +693,27 @@ func (vm *VirtualMachine) AddConfigParams(params map[string]string) error {
return err
}
func (vm *VirtualMachine) Export() (*nfc.Lease, error) {
return vm.vm.Export(vm.driver.ctx)
}
func (vm *VirtualMachine) CreateDescriptor(m *ovf.Manager, cdp types.OvfCreateDescriptorParams) (*types.OvfCreateDescriptorResult, error) {
return m.CreateDescriptor(vm.driver.ctx, vm.vm, cdp)
}
func (vm *VirtualMachine) NewOvfManager() *ovf.Manager {
return ovf.NewManager(vm.vm.Client())
}
func (vm *VirtualMachine) GetOvfExportOptions(m *ovf.Manager) ([]types.OvfOptionInfo, error) {
var mgr mo.OvfManager
err := property.DefaultCollector(vm.vm.Client()).RetrieveOne(vm.driver.ctx, m.Reference(), nil, &mgr)
if err != nil {
return nil, err
}
return mgr.OvfExportOption, nil
}
func findNetworkAdapter(l object.VirtualDeviceList) (types.BaseVirtualEthernetCard, error) {
c := l.SelectByType((*types.VirtualEthernetCard)(nil))
if len(c) == 0 {

View File

@ -132,6 +132,17 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
},
)
if b.config.Export != nil {
steps = append(steps, &common.StepExport{
Name: b.config.Export.Name,
Force: b.config.Export.Force,
Images: b.config.Export.Images,
Manifest: b.config.Export.Manifest,
OutputDir: b.config.Export.OutputDir.OutputDir,
Options: b.config.Export.Options,
})
}
b.runner = packerCommon.NewRunner(steps, b.config.PackerConfig, ui)
b.runner.Run(ctx, state)

View File

@ -39,6 +39,9 @@ type Config struct {
CreateSnapshot bool `mapstructure:"create_snapshot"`
// Convert VM to a template. Defaults to `false`.
ConvertToTemplate bool `mapstructure:"convert_to_template"`
// Configuration for exporting VM to an ovf file.
// The VM will not be exported if no [Export Configuration](#export-configuration) is specified.
Export *common.ExportConfig `mapstructure:"export"`
ctx interpolate.Context
}
@ -77,6 +80,9 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend(errs, c.WaitIpConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare()...)
if c.Export != nil {
errs = packer.MultiErrorAppend(errs, c.Export.Prepare(&c.ctx, &c.LocationConfig, &c.PackerConfig)...)
}
if len(errs.Errors) > 0 {
return warnings, errs

View File

@ -3,122 +3,124 @@ package iso
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer/builder/vsphere/common"
"github.com/zclconf/go-cty/cty"
)
// FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"`
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory"`
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min"`
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max"`
VCenterServer *string `mapstructure:"vcenter_server" cty:"vcenter_server"`
Username *string `mapstructure:"username" cty:"username"`
Password *string `mapstructure:"password" cty:"password"`
InsecureConnection *bool `mapstructure:"insecure_connection" cty:"insecure_connection"`
Datacenter *string `mapstructure:"datacenter" cty:"datacenter"`
Version *uint `mapstructure:"vm_version" cty:"vm_version"`
GuestOSType *string `mapstructure:"guest_os_type" cty:"guest_os_type"`
Firmware *string `mapstructure:"firmware" cty:"firmware"`
DiskControllerType *string `mapstructure:"disk_controller_type" cty:"disk_controller_type"`
DiskSize *int64 `mapstructure:"disk_size" cty:"disk_size"`
DiskThinProvisioned *bool `mapstructure:"disk_thin_provisioned" cty:"disk_thin_provisioned"`
DiskEagerlyScrub *bool `mapstructure:"disk_eagerly_scrub" cty:"disk_eagerly_scrub"`
Storage []FlatDiskConfig `mapstructure:"storage" cty:"storage"`
Network *string `mapstructure:"network" cty:"network"`
NetworkCard *string `mapstructure:"network_card" cty:"network_card"`
NICs []FlatNIC `mapstructure:"network_adapters" cty:"network_adapters"`
USBController *bool `mapstructure:"usb_controller" cty:"usb_controller"`
Notes *string `mapstructure:"notes" cty:"notes"`
VMName *string `mapstructure:"vm_name" cty:"vm_name"`
Folder *string `mapstructure:"folder" cty:"folder"`
Cluster *string `mapstructure:"cluster" cty:"cluster"`
Host *string `mapstructure:"host" cty:"host"`
ResourcePool *string `mapstructure:"resource_pool" cty:"resource_pool"`
Datastore *string `mapstructure:"datastore" cty:"datastore"`
CPUs *int32 `mapstructure:"CPUs" cty:"CPUs"`
CpuCores *int32 `mapstructure:"cpu_cores" cty:"cpu_cores"`
CPUReservation *int64 `mapstructure:"CPU_reservation" cty:"CPU_reservation"`
CPULimit *int64 `mapstructure:"CPU_limit" cty:"CPU_limit"`
CpuHotAddEnabled *bool `mapstructure:"CPU_hot_plug" cty:"CPU_hot_plug"`
RAM *int64 `mapstructure:"RAM" cty:"RAM"`
RAMReservation *int64 `mapstructure:"RAM_reservation" cty:"RAM_reservation"`
RAMReserveAll *bool `mapstructure:"RAM_reserve_all" cty:"RAM_reserve_all"`
MemoryHotAddEnabled *bool `mapstructure:"RAM_hot_plug" cty:"RAM_hot_plug"`
VideoRAM *int64 `mapstructure:"video_ram" cty:"video_ram"`
NestedHV *bool `mapstructure:"NestedHV" cty:"NestedHV"`
ConfigParams map[string]string `mapstructure:"configuration_parameters" cty:"configuration_parameters"`
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum"`
ISOChecksumURL *string `mapstructure:"iso_checksum_url" cty:"iso_checksum_url"`
ISOChecksumType *string `mapstructure:"iso_checksum_type" cty:"iso_checksum_type"`
RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url"`
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls"`
TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path"`
TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension"`
CdromType *string `mapstructure:"cdrom_type" cty:"cdrom_type"`
ISOPaths []string `mapstructure:"iso_paths" cty:"iso_paths"`
RemoveCdrom *bool `mapstructure:"remove_cdrom" cty:"remove_cdrom"`
FloppyIMGPath *string `mapstructure:"floppy_img_path" cty:"floppy_img_path"`
FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files"`
FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs"`
BootOrder *string `mapstructure:"boot_order" cty:"boot_order"`
BootCommand []string `mapstructure:"boot_command" cty:"boot_command"`
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait"`
HTTPIP *string `mapstructure:"http_ip" cty:"http_ip"`
WaitTimeout *string `mapstructure:"ip_wait_timeout" cty:"ip_wait_timeout"`
SettleTimeout *string `mapstructure:"ip_settle_timeout" cty:"ip_settle_timeout"`
Type *string `mapstructure:"communicator" cty:"communicator"`
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"`
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"`
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"`
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"`
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"`
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"`
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_name"`
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"`
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"`
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"`
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"`
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"`
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"`
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"`
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"`
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"`
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"`
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"`
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"`
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive"`
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"`
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"`
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"`
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"`
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"`
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"`
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"`
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"`
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"`
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"`
SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"`
SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"`
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"`
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"`
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"`
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"`
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"`
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"`
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"`
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"`
Command *string `mapstructure:"shutdown_command" cty:"shutdown_command"`
Timeout *string `mapstructure:"shutdown_timeout" cty:"shutdown_timeout"`
CreateSnapshot *bool `mapstructure:"create_snapshot" cty:"create_snapshot"`
ConvertToTemplate *bool `mapstructure:"convert_to_template" cty:"convert_to_template"`
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"`
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory"`
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min"`
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max"`
VCenterServer *string `mapstructure:"vcenter_server" cty:"vcenter_server"`
Username *string `mapstructure:"username" cty:"username"`
Password *string `mapstructure:"password" cty:"password"`
InsecureConnection *bool `mapstructure:"insecure_connection" cty:"insecure_connection"`
Datacenter *string `mapstructure:"datacenter" cty:"datacenter"`
Version *uint `mapstructure:"vm_version" cty:"vm_version"`
GuestOSType *string `mapstructure:"guest_os_type" cty:"guest_os_type"`
Firmware *string `mapstructure:"firmware" cty:"firmware"`
DiskControllerType *string `mapstructure:"disk_controller_type" cty:"disk_controller_type"`
DiskSize *int64 `mapstructure:"disk_size" cty:"disk_size"`
DiskThinProvisioned *bool `mapstructure:"disk_thin_provisioned" cty:"disk_thin_provisioned"`
DiskEagerlyScrub *bool `mapstructure:"disk_eagerly_scrub" cty:"disk_eagerly_scrub"`
Storage []FlatDiskConfig `mapstructure:"storage" cty:"storage"`
Network *string `mapstructure:"network" cty:"network"`
NetworkCard *string `mapstructure:"network_card" cty:"network_card"`
NICs []FlatNIC `mapstructure:"network_adapters" cty:"network_adapters"`
USBController *bool `mapstructure:"usb_controller" cty:"usb_controller"`
Notes *string `mapstructure:"notes" cty:"notes"`
VMName *string `mapstructure:"vm_name" cty:"vm_name"`
Folder *string `mapstructure:"folder" cty:"folder"`
Cluster *string `mapstructure:"cluster" cty:"cluster"`
Host *string `mapstructure:"host" cty:"host"`
ResourcePool *string `mapstructure:"resource_pool" cty:"resource_pool"`
Datastore *string `mapstructure:"datastore" cty:"datastore"`
CPUs *int32 `mapstructure:"CPUs" cty:"CPUs"`
CpuCores *int32 `mapstructure:"cpu_cores" cty:"cpu_cores"`
CPUReservation *int64 `mapstructure:"CPU_reservation" cty:"CPU_reservation"`
CPULimit *int64 `mapstructure:"CPU_limit" cty:"CPU_limit"`
CpuHotAddEnabled *bool `mapstructure:"CPU_hot_plug" cty:"CPU_hot_plug"`
RAM *int64 `mapstructure:"RAM" cty:"RAM"`
RAMReservation *int64 `mapstructure:"RAM_reservation" cty:"RAM_reservation"`
RAMReserveAll *bool `mapstructure:"RAM_reserve_all" cty:"RAM_reserve_all"`
MemoryHotAddEnabled *bool `mapstructure:"RAM_hot_plug" cty:"RAM_hot_plug"`
VideoRAM *int64 `mapstructure:"video_ram" cty:"video_ram"`
NestedHV *bool `mapstructure:"NestedHV" cty:"NestedHV"`
ConfigParams map[string]string `mapstructure:"configuration_parameters" cty:"configuration_parameters"`
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum"`
ISOChecksumURL *string `mapstructure:"iso_checksum_url" cty:"iso_checksum_url"`
ISOChecksumType *string `mapstructure:"iso_checksum_type" cty:"iso_checksum_type"`
RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url"`
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls"`
TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path"`
TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension"`
CdromType *string `mapstructure:"cdrom_type" cty:"cdrom_type"`
ISOPaths []string `mapstructure:"iso_paths" cty:"iso_paths"`
RemoveCdrom *bool `mapstructure:"remove_cdrom" cty:"remove_cdrom"`
FloppyIMGPath *string `mapstructure:"floppy_img_path" cty:"floppy_img_path"`
FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files"`
FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs"`
BootOrder *string `mapstructure:"boot_order" cty:"boot_order"`
BootCommand []string `mapstructure:"boot_command" cty:"boot_command"`
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait"`
HTTPIP *string `mapstructure:"http_ip" cty:"http_ip"`
WaitTimeout *string `mapstructure:"ip_wait_timeout" cty:"ip_wait_timeout"`
SettleTimeout *string `mapstructure:"ip_settle_timeout" cty:"ip_settle_timeout"`
Type *string `mapstructure:"communicator" cty:"communicator"`
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"`
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"`
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"`
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"`
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"`
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"`
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_name"`
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"`
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"`
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"`
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"`
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"`
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"`
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"`
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"`
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"`
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"`
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"`
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"`
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive"`
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"`
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"`
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"`
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"`
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"`
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"`
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"`
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"`
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"`
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"`
SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"`
SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"`
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"`
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"`
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"`
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"`
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"`
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"`
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"`
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"`
Command *string `mapstructure:"shutdown_command" cty:"shutdown_command"`
Timeout *string `mapstructure:"shutdown_timeout" cty:"shutdown_timeout"`
CreateSnapshot *bool `mapstructure:"create_snapshot" cty:"create_snapshot"`
ConvertToTemplate *bool `mapstructure:"convert_to_template" cty:"convert_to_template"`
Export *common.FlatExportConfig `mapstructure:"export" cty:"export"`
}
// FlatMapstructure returns a new FlatConfig.
@ -243,6 +245,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
"create_snapshot": &hcldec.AttrSpec{Name: "create_snapshot", Type: cty.Bool, Required: false},
"convert_to_template": &hcldec.AttrSpec{Name: "convert_to_template", Type: cty.Bool, Required: false},
"export": &hcldec.BlockSpec{TypeName: "export", Nested: hcldec.ObjectSpec((*common.FlatExportConfig)(nil).HCL2Spec())},
}
return s
}

View File

@ -70,8 +70,14 @@ type CreateConfig struct {
func (c *CreateConfig) Prepare() []error {
var errs []error
if c.DiskSize == 0 {
errs = append(errs, fmt.Errorf("'disk_size' is required"))
if len(c.Storage) > 0 {
for i, storage := range c.Storage {
if storage.DiskSize == 0 {
errs = append(errs, fmt.Errorf("storage[%d].'disk_size' is required", i))
}
}
} else if c.DiskSize == 0 {
errs = append(errs, fmt.Errorf("'disk_size' or 'storage' is required"))
}
if c.GuestOSType == "" {
@ -94,17 +100,18 @@ type StepCreateVM struct {
func (s *StepCreateVM) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
d := state.Get("driver").(*driver.Driver)
vmPath := fmt.Sprintf("%s/%s", s.Location.Folder, s.Location.VMName)
vm, err := d.FindVM(s.Location.VMName)
vm, err := d.FindVM(vmPath)
if s.Force == false && err == nil {
state.Put("error", fmt.Errorf("%s already exists, you can use -force flag to destroy it: %v", s.Location.VMName, err))
state.Put("error", fmt.Errorf("%s already exists, you can use -force flag to destroy it: %v", vmPath, err))
return multistep.ActionHalt
} else if s.Force == true && err == nil {
ui.Say(fmt.Sprintf("the vm/template %s already exists, but deleting it due to -force flag", s.Location.VMName))
ui.Say(fmt.Sprintf("the vm/template %s already exists, but deleting it due to -force flag", vmPath))
err := vm.Destroy()
if err != nil {
state.Put("error", fmt.Errorf("error destroying %s: %v", s.Location.VMName, err))
state.Put("error", fmt.Errorf("error destroying %s: %v", vmPath, err))
}
}

View File

@ -17,6 +17,115 @@ import (
shell_local "github.com/hashicorp/packer/provisioner/shell-local"
)
func TestBuild_VarArgs(t *testing.T) {
tc := []struct {
name string
args []string
expectedCode int
fileCheck
}{
{
name: "json - json varfile sets an apple env var",
args: []string{
"-var-file=" + filepath.Join(testFixture("var-arg"), "apple.json"),
filepath.Join(testFixture("var-arg"), "fruit_builder.json"),
},
fileCheck: fileCheck{expected: []string{"apple.txt"}},
},
{
name: "json - json varfile sets an apple env var, " +
"override with banana cli var",
args: []string{
"-var", "fruit=banana",
"-var-file=" + filepath.Join(testFixture("var-arg"), "apple.json"),
filepath.Join(testFixture("var-arg"), "fruit_builder.json"),
},
fileCheck: fileCheck{expected: []string{"banana.txt"}},
},
{
name: "json - arg sets a pear env var",
args: []string{
"-var=fruit=pear",
filepath.Join(testFixture("var-arg"), "fruit_builder.json"),
},
fileCheck: fileCheck{expected: []string{"pear.txt"}},
},
{
name: "json - inexistent var file errs",
args: []string{
"-var-file=" + filepath.Join(testFixture("var-arg"), "potato.json"),
filepath.Join(testFixture("var-arg"), "fruit_builder.json"),
},
expectedCode: 1,
fileCheck: fileCheck{notExpected: []string{"potato.txt"}},
},
{
name: "hcl - inexistent json var file errs",
args: []string{
"-var-file=" + filepath.Join(testFixture("var-arg"), "potato.json"),
testFixture("var-arg"),
},
expectedCode: 1,
fileCheck: fileCheck{notExpected: []string{"potato.txt"}},
},
{
name: "hcl - inexistent hcl var file errs",
args: []string{
"-var-file=" + filepath.Join(testFixture("var-arg"), "potato.hcl"),
testFixture("var-arg"),
},
expectedCode: 1,
fileCheck: fileCheck{notExpected: []string{"potato.hcl"}},
},
{
name: "hcl - auto varfile sets a chocolate env var",
args: []string{
testFixture("var-arg"),
},
fileCheck: fileCheck{expected: []string{"chocolate.txt"}},
},
{
name: "hcl - hcl varfile sets a apple env var",
args: []string{
"-var-file=" + filepath.Join(testFixture("var-arg"), "apple.hcl"),
testFixture("var-arg"),
},
fileCheck: fileCheck{expected: []string{"apple.txt"}},
},
{
name: "hcl - json varfile sets a apple env var",
args: []string{
"-var-file=" + filepath.Join(testFixture("var-arg"), "apple.json"),
testFixture("var-arg"),
},
fileCheck: fileCheck{expected: []string{"apple.txt"}},
},
{
name: "hcl - arg sets a tomato env var",
args: []string{
"-var=fruit=tomato",
testFixture("var-arg"),
},
fileCheck: fileCheck{expected: []string{"tomato.txt"}},
},
}
for _, tt := range tc {
t.Run(tt.name, func(t *testing.T) {
run(t, tt.args, tt.expectedCode)
defer cleanup()
tt.fileCheck.verify(t)
})
}
}
func TestBuildOnlyFileCommaFlags(t *testing.T) {
c := &BuildCommand{
Meta: testMetaFile(t),
@ -217,6 +326,35 @@ func TestBuildWithNonExistingBuilder(t *testing.T) {
}
}
func run(t *testing.T, args []string, expectedCode int) {
t.Helper()
c := &BuildCommand{
Meta: testMetaFile(t),
}
if code := c.Run(args); code != expectedCode {
fatalCommand(t, c.Meta)
}
}
type fileCheck struct {
expected, notExpected []string
}
func (fc fileCheck) verify(t *testing.T) {
for _, f := range fc.expected {
if !fileExists(f) {
t.Errorf("Expected to find %s", f)
}
}
for _, f := range fc.notExpected {
if fileExists(f) {
t.Errorf("Expected to not find %s", f)
}
}
}
// fileExists returns true if the filename is found
func fileExists(filename string) bool {
if _, err := os.Stat(filename); err == nil {
@ -272,6 +410,7 @@ func cleanup(moreFiles ...string) {
os.RemoveAll("lilas.txt")
os.RemoveAll("campanules.txt")
os.RemoveAll("ducky.txt")
os.RemoveAll("banana.txt")
for _, file := range moreFiles {
os.RemoveAll(file)
}

View File

@ -44,14 +44,23 @@ func (m *Meta) Core(tpl *template.Template) (*packer.Core, error) {
config.Template = tpl
fj := &kvflag.FlagJSON{}
// First populate fj with contents from var files
for _, file := range m.varFiles {
err := fj.Set(file)
if err != nil {
return nil, err
}
}
// Now read fj values back into flagvars and set as config.Variables. Only
// add to flagVars if the key doesn't already exist, because flagVars comes
// from the command line and should not be overridden by variable files.
if m.flagVars == nil {
m.flagVars = map[string]string{}
}
for k, v := range *fj {
m.flagVars[k] = v
if _, exists := m.flagVars[k]; !exists {
m.flagVars[k] = v
}
}
config.Variables = m.flagVars

View File

@ -0,0 +1,2 @@
fruit = "apple"

View File

@ -0,0 +1,3 @@
{
"fruit": "apple"
}

View File

@ -0,0 +1,2 @@
fruit = "chocolate" // is that even a fruit !?

View File

@ -0,0 +1,20 @@
{
"variables": {
"fruit": ""
},
"builders": [
{
"communicator": "none",
"type": "null"
}
],
"post-processors": [
[
{
"name": "apple",
"type": "shell-local",
"inline": [ "echo {{ user `fruit` }} > {{ user `fruit` }}.txt" ]
}
]
]
}

View File

@ -0,0 +1,22 @@
variable "fruit" {
type = string
}
locals {
fruit = var.fruit
}
source "null" "builder" {
communicator = "none"
}
build {
sources = [
"source.null.builder",
]
provisioner "shell-local" {
inline = ["echo ${local.fruit} > ${local.fruit}.txt"]
}
}

View File

@ -25,11 +25,6 @@ import (
// * HTTP
// * Amazon S3
//
//
// \~&gt; On Windows, using a symlink to refer to local files is currently
// unsupported. Packer will always copy a local iso into the Packer cache
// directory.
//
// Examples:
// go-getter can guess the checksum type based on `iso_checksum` len.
//

View File

@ -7,9 +7,10 @@ import (
)
var (
PossibleNumbers = "0123456789"
PossibleLowerCase = "abcdefghijklmnopqrstuvwxyz"
PossibleUpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
PossibleNumbers = "0123456789"
PossibleLowerCase = "abcdefghijklmnopqrstuvwxyz"
PossibleUpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
PossibleSpecialCharacter = " !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
PossibleAlphaNum = PossibleNumbers + PossibleLowerCase + PossibleUpperCase
PossibleAlphaNumLower = PossibleNumbers + PossibleLowerCase

View File

@ -94,18 +94,17 @@ func (p *Parser) parse(filename string, varFiles []string, argVars map[string]st
// Decode variable blocks so that they are available later on. Here locals
// can use input variables so we decode them firsthand.
var locals []*Local
{
for _, file := range files {
diags = append(diags, cfg.decodeInputVariables(file)...)
}
var locals []*Local
for _, file := range files {
moreLocals, morediags := cfg.parseLocalVariables(file)
diags = append(diags, morediags...)
locals = append(locals, moreLocals...)
}
diags = append(diags, cfg.evaluateLocalVariables(locals)...)
}
// parse var files
@ -130,11 +129,17 @@ func (p *Parser) parse(filename string, varFiles []string, argVars map[string]st
for _, filename := range hclVarFiles {
f, moreDiags := p.ParseHCLFile(filename)
diags = append(diags, moreDiags...)
if moreDiags.HasErrors() {
continue
}
varFiles = append(varFiles, f)
}
for _, filename := range jsonVarFiles {
f, moreDiags := p.ParseJSONFile(filename)
diags = append(diags, moreDiags...)
if moreDiags.HasErrors() {
continue
}
varFiles = append(varFiles, f)
}
@ -145,6 +150,7 @@ func (p *Parser) parse(filename string, varFiles []string, argVars map[string]st
diags = append(diags, moreDiags...)
_, moreDiags = cfg.LocalVariables.Values()
diags = append(diags, moreDiags...)
diags = append(diags, cfg.evaluateLocalVariables(locals)...)
// decode the actual content
for _, file := range files {

View File

@ -30,7 +30,7 @@ fi
get_prs(){
# git log v0.10.2...c3861d167533fb797b0fae0c380806625712e5f7 |
git log HEAD...${LAST_RELEASE} |
git log HEAD...${LAST_RELEASE} --first-parent --oneline --grep="Merge pull request #[0-9]\+" --grep="(#[0-9]\+)$" |
grep -o "#\([0-9]\+\)" | awk -F\# '{print $2}' | while read line
do
grep -q "GH-${line}" CHANGELOG.md

View File

@ -0,0 +1,158 @@
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
package auth
import (
"bytes"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"github.com/oracle/oci-go-sdk/common"
"sync"
)
// x509CertificateRetriever provides an X509 certificate with the RSA private key
type x509CertificateRetriever interface {
Refresh() error
CertificatePemRaw() []byte
Certificate() *x509.Certificate
PrivateKeyPemRaw() []byte
PrivateKey() *rsa.PrivateKey
}
// urlBasedX509CertificateRetriever retrieves PEM-encoded X509 certificates from the given URLs.
type urlBasedX509CertificateRetriever struct {
certURL string
privateKeyURL string
passphrase string
certificatePemRaw []byte
certificate *x509.Certificate
privateKeyPemRaw []byte
privateKey *rsa.PrivateKey
mux sync.Mutex
}
func newURLBasedX509CertificateRetriever(certURL, privateKeyURL, passphrase string) x509CertificateRetriever {
return &urlBasedX509CertificateRetriever{
certURL: certURL,
privateKeyURL: privateKeyURL,
passphrase: passphrase,
mux: sync.Mutex{},
}
}
// Refresh() is failure atomic, i.e., CertificatePemRaw(), Certificate(), PrivateKeyPemRaw(), and PrivateKey() would
// return their previous values if Refresh() fails.
func (r *urlBasedX509CertificateRetriever) Refresh() error {
common.Debugln("Refreshing certificate")
r.mux.Lock()
defer r.mux.Unlock()
var err error
var certificatePemRaw []byte
var certificate *x509.Certificate
if certificatePemRaw, certificate, err = r.renewCertificate(r.certURL); err != nil {
return fmt.Errorf("failed to renew certificate: %s", err.Error())
}
var privateKeyPemRaw []byte
var privateKey *rsa.PrivateKey
if r.privateKeyURL != "" {
if privateKeyPemRaw, privateKey, err = r.renewPrivateKey(r.privateKeyURL, r.passphrase); err != nil {
return fmt.Errorf("failed to renew private key: %s", err.Error())
}
}
r.certificatePemRaw = certificatePemRaw
r.certificate = certificate
r.privateKeyPemRaw = privateKeyPemRaw
r.privateKey = privateKey
return nil
}
func (r *urlBasedX509CertificateRetriever) renewCertificate(url string) (certificatePemRaw []byte, certificate *x509.Certificate, err error) {
var body bytes.Buffer
if body, err = httpGet(url); err != nil {
return nil, nil, fmt.Errorf("failed to get certificate from %s: %s", url, err.Error())
}
certificatePemRaw = body.Bytes()
var block *pem.Block
block, _ = pem.Decode(certificatePemRaw)
if block == nil {
return nil, nil, fmt.Errorf("failed to parse the new certificate, not valid pem data")
}
if certificate, err = x509.ParseCertificate(block.Bytes); err != nil {
return nil, nil, fmt.Errorf("failed to parse the new certificate: %s", err.Error())
}
return certificatePemRaw, certificate, nil
}
func (r *urlBasedX509CertificateRetriever) renewPrivateKey(url, passphrase string) (privateKeyPemRaw []byte, privateKey *rsa.PrivateKey, err error) {
var body bytes.Buffer
if body, err = httpGet(url); err != nil {
return nil, nil, fmt.Errorf("failed to get private key from %s: %s", url, err.Error())
}
privateKeyPemRaw = body.Bytes()
if privateKey, err = common.PrivateKeyFromBytes(privateKeyPemRaw, &passphrase); err != nil {
return nil, nil, fmt.Errorf("failed to parse the new private key: %s", err.Error())
}
return privateKeyPemRaw, privateKey, nil
}
func (r *urlBasedX509CertificateRetriever) CertificatePemRaw() []byte {
r.mux.Lock()
defer r.mux.Unlock()
if r.certificatePemRaw == nil {
return nil
}
c := make([]byte, len(r.certificatePemRaw))
copy(c, r.certificatePemRaw)
return c
}
func (r *urlBasedX509CertificateRetriever) Certificate() *x509.Certificate {
r.mux.Lock()
defer r.mux.Unlock()
if r.certificate == nil {
return nil
}
c := *r.certificate
return &c
}
func (r *urlBasedX509CertificateRetriever) PrivateKeyPemRaw() []byte {
r.mux.Lock()
defer r.mux.Unlock()
if r.privateKeyPemRaw == nil {
return nil
}
c := make([]byte, len(r.privateKeyPemRaw))
copy(c, r.privateKeyPemRaw)
return c
}
func (r *urlBasedX509CertificateRetriever) PrivateKey() *rsa.PrivateKey {
r.mux.Lock()
defer r.mux.Unlock()
if r.privateKey == nil {
return nil
}
c := *r.privateKey
return &c
}

View File

@ -0,0 +1,61 @@
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
package auth
import (
"crypto/rsa"
"fmt"
"github.com/oracle/oci-go-sdk/common"
)
type instancePrincipalConfigurationProvider struct {
keyProvider *instancePrincipalKeyProvider
region *common.Region
}
//InstancePrincipalConfigurationProvider returns a configuration for instance principals
func InstancePrincipalConfigurationProvider() (common.ConfigurationProvider, error) {
var err error
var keyProvider *instancePrincipalKeyProvider
if keyProvider, err = newInstancePrincipalKeyProvider(); err != nil {
return nil, fmt.Errorf("failed to create a new key provider for instance principal: %s", err.Error())
}
return instancePrincipalConfigurationProvider{keyProvider: keyProvider, region: nil}, nil
}
//InstancePrincipalConfigurationProviderForRegion returns a configuration for instance principals with a given region
func InstancePrincipalConfigurationProviderForRegion(region common.Region) (common.ConfigurationProvider, error) {
var err error
var keyProvider *instancePrincipalKeyProvider
if keyProvider, err = newInstancePrincipalKeyProvider(); err != nil {
return nil, fmt.Errorf("failed to create a new key provider for instance principal: %s", err.Error())
}
return instancePrincipalConfigurationProvider{keyProvider: keyProvider, region: &region}, nil
}
func (p instancePrincipalConfigurationProvider) PrivateRSAKey() (*rsa.PrivateKey, error) {
return p.keyProvider.PrivateRSAKey()
}
func (p instancePrincipalConfigurationProvider) KeyID() (string, error) {
return p.keyProvider.KeyID()
}
func (p instancePrincipalConfigurationProvider) TenancyOCID() (string, error) {
return p.keyProvider.TenancyOCID()
}
func (p instancePrincipalConfigurationProvider) UserOCID() (string, error) {
return "", nil
}
func (p instancePrincipalConfigurationProvider) KeyFingerprint() (string, error) {
return "", nil
}
func (p instancePrincipalConfigurationProvider) Region() (string, error) {
if p.region == nil {
return string(p.keyProvider.RegionForFederationClient()), nil
}
return string(*p.region), nil
}

View File

@ -0,0 +1,292 @@
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
// Package auth provides supporting functions and structs for authentication
package auth
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"github.com/oracle/oci-go-sdk/common"
"net/http"
"strings"
"sync"
)
// federationClient is a client to retrieve the security token for an instance principal necessary to sign a request.
// It also provides the private key whose corresponding public key is used to retrieve the security token.
type federationClient interface {
PrivateKey() (*rsa.PrivateKey, error)
SecurityToken() (string, error)
}
// x509FederationClient retrieves a security token from Auth service.
type x509FederationClient struct {
tenancyID string
sessionKeySupplier sessionKeySupplier
leafCertificateRetriever x509CertificateRetriever
intermediateCertificateRetrievers []x509CertificateRetriever
securityToken securityToken
authClient *common.BaseClient
mux sync.Mutex
}
func newX509FederationClient(region common.Region, tenancyID string, leafCertificateRetriever x509CertificateRetriever, intermediateCertificateRetrievers []x509CertificateRetriever) federationClient {
client := &x509FederationClient{
tenancyID: tenancyID,
leafCertificateRetriever: leafCertificateRetriever,
intermediateCertificateRetrievers: intermediateCertificateRetrievers,
}
client.sessionKeySupplier = newSessionKeySupplier()
client.authClient = newAuthClient(region, client)
return client
}
var (
genericHeaders = []string{"date", "(request-target)"} // "host" is not needed for the federation endpoint. Don't ask me why.
bodyHeaders = []string{"content-length", "content-type", "x-content-sha256"}
)
func newAuthClient(region common.Region, provider common.KeyProvider) *common.BaseClient {
signer := common.RequestSigner(provider, genericHeaders, bodyHeaders)
client := common.DefaultBaseClientWithSigner(signer)
if region == common.RegionSEA {
client.Host = "https://auth.r1.oracleiaas.com"
} else {
client.Host = fmt.Sprintf(common.DefaultHostURLTemplate, "auth", string(region))
}
client.BasePath = "v1/x509"
return &client
}
// For authClient to sign requests to X509 Federation Endpoint
func (c *x509FederationClient) KeyID() (string, error) {
tenancy := c.tenancyID
fingerprint := fingerprint(c.leafCertificateRetriever.Certificate())
return fmt.Sprintf("%s/fed-x509/%s", tenancy, fingerprint), nil
}
// For authClient to sign requests to X509 Federation Endpoint
func (c *x509FederationClient) PrivateRSAKey() (*rsa.PrivateKey, error) {
return c.leafCertificateRetriever.PrivateKey(), nil
}
func (c *x509FederationClient) PrivateKey() (*rsa.PrivateKey, error) {
c.mux.Lock()
defer c.mux.Unlock()
if err := c.renewSecurityTokenIfNotValid(); err != nil {
return nil, err
}
return c.sessionKeySupplier.PrivateKey(), nil
}
func (c *x509FederationClient) SecurityToken() (token string, err error) {
c.mux.Lock()
defer c.mux.Unlock()
if err = c.renewSecurityTokenIfNotValid(); err != nil {
return "", err
}
return c.securityToken.String(), nil
}
func (c *x509FederationClient) renewSecurityTokenIfNotValid() (err error) {
if c.securityToken == nil || !c.securityToken.Valid() {
if err = c.renewSecurityToken(); err != nil {
return fmt.Errorf("failed to renew security token: %s", err.Error())
}
}
return nil
}
func (c *x509FederationClient) renewSecurityToken() (err error) {
if err = c.sessionKeySupplier.Refresh(); err != nil {
return fmt.Errorf("failed to refresh session key: %s", err.Error())
}
if err = c.leafCertificateRetriever.Refresh(); err != nil {
return fmt.Errorf("failed to refresh leaf certificate: %s", err.Error())
}
updatedTenancyID := extractTenancyIDFromCertificate(c.leafCertificateRetriever.Certificate())
if c.tenancyID != updatedTenancyID {
err = fmt.Errorf("unexpected update of tenancy OCID in the leaf certificate. Previous tenancy: %s, Updated: %s", c.tenancyID, updatedTenancyID)
return
}
for _, retriever := range c.intermediateCertificateRetrievers {
if err = retriever.Refresh(); err != nil {
return fmt.Errorf("failed to refresh intermediate certificate: %s", err.Error())
}
}
if c.securityToken, err = c.getSecurityToken(); err != nil {
return fmt.Errorf("failed to get security token: %s", err.Error())
}
return nil
}
func (c *x509FederationClient) getSecurityToken() (securityToken, error) {
request := c.makeX509FederationRequest()
var err error
var httpRequest http.Request
if httpRequest, err = common.MakeDefaultHTTPRequestWithTaggedStruct(http.MethodPost, "", request); err != nil {
return nil, fmt.Errorf("failed to make http request: %s", err.Error())
}
var httpResponse *http.Response
defer common.CloseBodyIfValid(httpResponse)
if httpResponse, err = c.authClient.Call(context.Background(), &httpRequest); err != nil {
return nil, fmt.Errorf("failed to call: %s", err.Error())
}
response := x509FederationResponse{}
if err = common.UnmarshalResponse(httpResponse, &response); err != nil {
return nil, fmt.Errorf("failed to unmarshal the response: %s", err.Error())
}
return newInstancePrincipalToken(response.Token.Token)
}
type x509FederationRequest struct {
X509FederationDetails `contributesTo:"body"`
}
// X509FederationDetails x509 federation details
type X509FederationDetails struct {
Certificate string `mandatory:"true" json:"certificate,omitempty"`
PublicKey string `mandatory:"true" json:"publicKey,omitempty"`
IntermediateCertificates []string `mandatory:"false" json:"intermediateCertificates,omitempty"`
}
type x509FederationResponse struct {
Token `presentIn:"body"`
}
// Token token
type Token struct {
Token string `mandatory:"true" json:"token,omitempty"`
}
func (c *x509FederationClient) makeX509FederationRequest() *x509FederationRequest {
certificate := c.sanitizeCertificateString(string(c.leafCertificateRetriever.CertificatePemRaw()))
publicKey := c.sanitizeCertificateString(string(c.sessionKeySupplier.PublicKeyPemRaw()))
var intermediateCertificates []string
for _, retriever := range c.intermediateCertificateRetrievers {
intermediateCertificates = append(intermediateCertificates, c.sanitizeCertificateString(string(retriever.CertificatePemRaw())))
}
details := X509FederationDetails{
Certificate: certificate,
PublicKey: publicKey,
IntermediateCertificates: intermediateCertificates,
}
return &x509FederationRequest{details}
}
func (c *x509FederationClient) sanitizeCertificateString(certString string) string {
certString = strings.Replace(certString, "-----BEGIN CERTIFICATE-----", "", -1)
certString = strings.Replace(certString, "-----END CERTIFICATE-----", "", -1)
certString = strings.Replace(certString, "-----BEGIN PUBLIC KEY-----", "", -1)
certString = strings.Replace(certString, "-----END PUBLIC KEY-----", "", -1)
certString = strings.Replace(certString, "\n", "", -1)
return certString
}
// sessionKeySupplier provides an RSA keypair which can be re-generated by calling Refresh().
type sessionKeySupplier interface {
Refresh() error
PrivateKey() *rsa.PrivateKey
PublicKeyPemRaw() []byte
}
// inMemorySessionKeySupplier implements sessionKeySupplier to vend an RSA keypair.
// Refresh() generates a new RSA keypair with a random source, and keeps it in memory.
//
// inMemorySessionKeySupplier is not thread-safe.
type inMemorySessionKeySupplier struct {
keySize int
privateKey *rsa.PrivateKey
publicKeyPemRaw []byte
}
// newSessionKeySupplier creates and returns a sessionKeySupplier instance which generates key pairs of size 2048.
func newSessionKeySupplier() sessionKeySupplier {
return &inMemorySessionKeySupplier{keySize: 2048}
}
// Refresh() is failure atomic, i.e., PrivateKey() and PublicKeyPemRaw() would return their previous values
// if Refresh() fails.
func (s *inMemorySessionKeySupplier) Refresh() (err error) {
common.Debugln("Refreshing session key")
var privateKey *rsa.PrivateKey
privateKey, err = rsa.GenerateKey(rand.Reader, s.keySize)
if err != nil {
return fmt.Errorf("failed to generate a new keypair: %s", err)
}
var publicKeyAsnBytes []byte
if publicKeyAsnBytes, err = x509.MarshalPKIXPublicKey(privateKey.Public()); err != nil {
return fmt.Errorf("failed to marshal the public part of the new keypair: %s", err.Error())
}
publicKeyPemRaw := pem.EncodeToMemory(&pem.Block{
Type: "PUBLIC KEY",
Bytes: publicKeyAsnBytes,
})
s.privateKey = privateKey
s.publicKeyPemRaw = publicKeyPemRaw
return nil
}
func (s *inMemorySessionKeySupplier) PrivateKey() *rsa.PrivateKey {
if s.privateKey == nil {
return nil
}
c := *s.privateKey
return &c
}
func (s *inMemorySessionKeySupplier) PublicKeyPemRaw() []byte {
if s.publicKeyPemRaw == nil {
return nil
}
c := make([]byte, len(s.publicKeyPemRaw))
copy(c, s.publicKeyPemRaw)
return c
}
type securityToken interface {
fmt.Stringer
Valid() bool
}
type instancePrincipalToken struct {
tokenString string
jwtToken *jwtToken
}
func newInstancePrincipalToken(tokenString string) (newToken securityToken, err error) {
var jwtToken *jwtToken
if jwtToken, err = parseJwt(tokenString); err != nil {
return nil, fmt.Errorf("failed to parse the token string \"%s\": %s", tokenString, err.Error())
}
return &instancePrincipalToken{tokenString, jwtToken}, nil
}
func (t *instancePrincipalToken) String() string {
return t.tokenString
}
func (t *instancePrincipalToken) Valid() bool {
return !t.jwtToken.expired()
}

View File

@ -0,0 +1,100 @@
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
package auth
import (
"bytes"
"crypto/rsa"
"fmt"
"github.com/oracle/oci-go-sdk/common"
)
const (
regionURL = `http://169.254.169.254/opc/v1/instance/region`
leafCertificateURL = `http://169.254.169.254/opc/v1/identity/cert.pem`
leafCertificateKeyURL = `http://169.254.169.254/opc/v1/identity/key.pem`
leafCertificateKeyPassphrase = `` // No passphrase for the private key for Compute instances
intermediateCertificateURL = `http://169.254.169.254/opc/v1/identity/intermediate.pem`
intermediateCertificateKeyURL = ``
intermediateCertificateKeyPassphrase = `` // No passphrase for the private key for Compute instances
)
// instancePrincipalKeyProvider implements KeyProvider to provide a key ID and its corresponding private key
// for an instance principal by getting a security token via x509FederationClient.
//
// The region name of the endpoint for x509FederationClient is obtained from the metadata service on the compute
// instance.
type instancePrincipalKeyProvider struct {
regionForFederationClient common.Region
federationClient federationClient
tenancyID string
}
// newInstancePrincipalKeyProvider creates and returns an instancePrincipalKeyProvider instance based on
// x509FederationClient.
//
// NOTE: There is a race condition between PrivateRSAKey() and KeyID(). These two pieces are tightly coupled; KeyID
// includes a security token obtained from Auth service by giving a public key which is paired with PrivateRSAKey.
// The x509FederationClient caches the security token in memory until it is expired. Thus, even if a client obtains a
// KeyID that is not expired at the moment, the PrivateRSAKey that the client acquires at a next moment could be
// invalid because the KeyID could be already expired.
func newInstancePrincipalKeyProvider() (provider *instancePrincipalKeyProvider, err error) {
var region common.Region
if region, err = getRegionForFederationClient(regionURL); err != nil {
err = fmt.Errorf("failed to get the region name from %s: %s", regionURL, err.Error())
common.Logln(err)
return nil, err
}
leafCertificateRetriever := newURLBasedX509CertificateRetriever(
leafCertificateURL, leafCertificateKeyURL, leafCertificateKeyPassphrase)
intermediateCertificateRetrievers := []x509CertificateRetriever{
newURLBasedX509CertificateRetriever(
intermediateCertificateURL, intermediateCertificateKeyURL, intermediateCertificateKeyPassphrase),
}
if err = leafCertificateRetriever.Refresh(); err != nil {
err = fmt.Errorf("failed to refresh the leaf certificate: %s", err.Error())
return nil, err
}
tenancyID := extractTenancyIDFromCertificate(leafCertificateRetriever.Certificate())
federationClient := newX509FederationClient(
region, tenancyID, leafCertificateRetriever, intermediateCertificateRetrievers)
provider = &instancePrincipalKeyProvider{regionForFederationClient: region, federationClient: federationClient, tenancyID: tenancyID}
return
}
func getRegionForFederationClient(url string) (r common.Region, err error) {
var body bytes.Buffer
if body, err = httpGet(url); err != nil {
return
}
return common.StringToRegion(body.String()), nil
}
func (p *instancePrincipalKeyProvider) RegionForFederationClient() common.Region {
return p.regionForFederationClient
}
func (p *instancePrincipalKeyProvider) PrivateRSAKey() (privateKey *rsa.PrivateKey, err error) {
if privateKey, err = p.federationClient.PrivateKey(); err != nil {
err = fmt.Errorf("failed to get private key: %s", err.Error())
return nil, err
}
return privateKey, nil
}
func (p *instancePrincipalKeyProvider) KeyID() (string, error) {
var securityToken string
var err error
if securityToken, err = p.federationClient.SecurityToken(); err != nil {
return "", fmt.Errorf("failed to get security token: %s", err.Error())
}
return fmt.Sprintf("ST$%s", securityToken), nil
}
func (p *instancePrincipalKeyProvider) TenancyOCID() (string, error) {
return p.tenancyID, nil
}

61
vendor/github.com/oracle/oci-go-sdk/common/auth/jwt.go generated vendored Normal file
View File

@ -0,0 +1,61 @@
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
package auth
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"time"
)
type jwtToken struct {
raw string
header map[string]interface{}
payload map[string]interface{}
}
func (t *jwtToken) expired() bool {
exp := int64(t.payload["exp"].(float64))
return exp <= time.Now().Unix()
}
func parseJwt(tokenString string) (*jwtToken, error) {
parts := strings.Split(tokenString, ".")
if len(parts) != 3 {
return nil, fmt.Errorf("the given token string contains an invalid number of parts")
}
token := &jwtToken{raw: tokenString}
var err error
// Parse Header part
var headerBytes []byte
if headerBytes, err = decodePart(parts[0]); err != nil {
return nil, fmt.Errorf("failed to decode the header bytes: %s", err.Error())
}
if err = json.Unmarshal(headerBytes, &token.header); err != nil {
return nil, err
}
// Parse Payload part
var payloadBytes []byte
if payloadBytes, err = decodePart(parts[1]); err != nil {
return nil, fmt.Errorf("failed to decode the payload bytes: %s", err.Error())
}
decoder := json.NewDecoder(bytes.NewBuffer(payloadBytes))
if err = decoder.Decode(&token.payload); err != nil {
return nil, fmt.Errorf("failed to decode the payload json: %s", err.Error())
}
return token, nil
}
func decodePart(partString string) ([]byte, error) {
if l := len(partString) % 4; 0 < l {
partString += strings.Repeat("=", 4-l)
}
return base64.URLEncoding.DecodeString(partString)
}

View File

@ -0,0 +1,64 @@
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
package auth
import (
"bytes"
"crypto/sha1"
"crypto/x509"
"fmt"
"github.com/oracle/oci-go-sdk/common"
"net/http"
"net/http/httputil"
"strings"
)
// httpGet makes a simple HTTP GET request to the given URL, expecting only "200 OK" status code.
// This is basically for the Instance Metadata Service.
func httpGet(url string) (body bytes.Buffer, err error) {
var response *http.Response
if response, err = http.Get(url); err != nil {
return
}
common.IfDebug(func() {
if dump, e := httputil.DumpResponse(response, true); e == nil {
common.Logf("Dump Response %v", string(dump))
} else {
common.Debugln(e)
}
})
defer response.Body.Close()
if _, err = body.ReadFrom(response.Body); err != nil {
return
}
if response.StatusCode != http.StatusOK {
err = fmt.Errorf("HTTP Get failed: URL: %s, Status: %s, Message: %s",
url, response.Status, body.String())
return
}
return
}
func extractTenancyIDFromCertificate(cert *x509.Certificate) string {
for _, nameAttr := range cert.Subject.Names {
value := nameAttr.Value.(string)
if strings.HasPrefix(value, "opc-tenant:") {
return value[len("opc-tenant:"):]
}
}
return ""
}
func fingerprint(certificate *x509.Certificate) string {
fingerprint := sha1.Sum(certificate.Raw)
return colonSeparatedString(fingerprint)
}
func colonSeparatedString(fingerprint [sha1.Size]byte) string {
spaceSeparated := fmt.Sprintf("% x", fingerprint)
return strings.Replace(spaceSeparated, " ", ":", -1)
}

78
vendor/github.com/vmware/govmomi/ovf/cim.go generated vendored Normal file
View File

@ -0,0 +1,78 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
/*
Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_VirtualSystemSettingData.xsd
*/
type CIMVirtualSystemSettingData struct {
ElementName string `xml:"ElementName"`
InstanceID string `xml:"InstanceID"`
AutomaticRecoveryAction *uint8 `xml:"AutomaticRecoveryAction"`
AutomaticShutdownAction *uint8 `xml:"AutomaticShutdownAction"`
AutomaticStartupAction *uint8 `xml:"AutomaticStartupAction"`
AutomaticStartupActionDelay *string `xml:"AutomaticStartupActionDelay>Interval"`
AutomaticStartupActionSequenceNumber *uint16 `xml:"AutomaticStartupActionSequenceNumber"`
Caption *string `xml:"Caption"`
ConfigurationDataRoot *string `xml:"ConfigurationDataRoot"`
ConfigurationFile *string `xml:"ConfigurationFile"`
ConfigurationID *string `xml:"ConfigurationID"`
CreationTime *string `xml:"CreationTime"`
Description *string `xml:"Description"`
LogDataRoot *string `xml:"LogDataRoot"`
Notes []string `xml:"Notes"`
RecoveryFile *string `xml:"RecoveryFile"`
SnapshotDataRoot *string `xml:"SnapshotDataRoot"`
SuspendDataRoot *string `xml:"SuspendDataRoot"`
SwapFileDataRoot *string `xml:"SwapFileDataRoot"`
VirtualSystemIdentifier *string `xml:"VirtualSystemIdentifier"`
VirtualSystemType *string `xml:"VirtualSystemType"`
}
/*
Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_ResourceAllocationSettingData.xsd
*/
type CIMResourceAllocationSettingData struct {
ElementName string `xml:"ElementName"`
InstanceID string `xml:"InstanceID"`
ResourceType *uint16 `xml:"ResourceType"`
OtherResourceType *string `xml:"OtherResourceType"`
ResourceSubType *string `xml:"ResourceSubType"`
AddressOnParent *string `xml:"AddressOnParent"`
Address *string `xml:"Address"`
AllocationUnits *string `xml:"AllocationUnits"`
AutomaticAllocation *bool `xml:"AutomaticAllocation"`
AutomaticDeallocation *bool `xml:"AutomaticDeallocation"`
Caption *string `xml:"Caption"`
Connection []string `xml:"Connection"`
ConsumerVisibility *uint16 `xml:"ConsumerVisibility"`
Description *string `xml:"Description"`
HostResource []string `xml:"HostResource"`
Limit *uint64 `xml:"Limit"`
MappingBehavior *uint `xml:"MappingBehavior"`
Parent *string `xml:"Parent"`
PoolID *string `xml:"PoolID"`
Reservation *uint64 `xml:"Reservation"`
VirtualQuantity *uint `xml:"VirtualQuantity"`
VirtualQuantityUnits *string `xml:"VirtualQuantityUnits"`
Weight *uint `xml:"Weight"`
}

25
vendor/github.com/vmware/govmomi/ovf/doc.go generated vendored Normal file
View File

@ -0,0 +1,25 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Package ovf provides functionality to unmarshal and inspect the structure
of an OVF file. It is not a complete implementation of the specification and
is intended to be used to import virtual infrastructure into vSphere.
For a complete specification of the OVF standard, refer to:
https://www.dmtf.org/sites/default/files/standards/documents/DSP0243_2.1.0.pdf
*/
package ovf

99
vendor/github.com/vmware/govmomi/ovf/env.go generated vendored Normal file
View File

@ -0,0 +1,99 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
import (
"bytes"
"fmt"
"github.com/vmware/govmomi/vim25/xml"
)
const (
ovfEnvHeader = `<Environment
xmlns="http://schemas.dmtf.org/ovf/environment/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:oe="http://schemas.dmtf.org/ovf/environment/1"
xmlns:ve="http://www.vmware.com/schema/ovfenv"
oe:id=""
ve:esxId="%s">`
ovfEnvPlatformSection = `<PlatformSection>
<Kind>%s</Kind>
<Version>%s</Version>
<Vendor>%s</Vendor>
<Locale>%s</Locale>
</PlatformSection>`
ovfEnvPropertyHeader = `<PropertySection>`
ovfEnvPropertyEntry = `<Property oe:key="%s" oe:value="%s"/>`
ovfEnvPropertyFooter = `</PropertySection>`
ovfEnvFooter = `</Environment>`
)
type Env struct {
XMLName xml.Name `xml:"http://schemas.dmtf.org/ovf/environment/1 Environment"`
ID string `xml:"id,attr"`
EsxID string `xml:"http://www.vmware.com/schema/ovfenv esxId,attr"`
Platform *PlatformSection `xml:"PlatformSection"`
Property *PropertySection `xml:"PropertySection"`
}
type PlatformSection struct {
Kind string `xml:"Kind"`
Version string `xml:"Version"`
Vendor string `xml:"Vendor"`
Locale string `xml:"Locale"`
}
type PropertySection struct {
Properties []EnvProperty `xml:"Property"`
}
type EnvProperty struct {
Key string `xml:"key,attr"`
Value string `xml:"value,attr"`
}
// Marshal marshals Env to xml by using xml.Marshal.
func (e Env) Marshal() (string, error) {
x, err := xml.Marshal(e)
if err != nil {
return "", err
}
return fmt.Sprintf("%s%s", xml.Header, x), nil
}
// MarshalManual manually marshals Env to xml suitable for a vApp guest.
// It exists to overcome the lack of expressiveness in Go's XML namespaces.
func (e Env) MarshalManual() string {
var buffer bytes.Buffer
buffer.WriteString(xml.Header)
buffer.WriteString(fmt.Sprintf(ovfEnvHeader, e.EsxID))
buffer.WriteString(fmt.Sprintf(ovfEnvPlatformSection, e.Platform.Kind, e.Platform.Version, e.Platform.Vendor, e.Platform.Locale))
buffer.WriteString(fmt.Sprint(ovfEnvPropertyHeader))
for _, p := range e.Property.Properties {
buffer.WriteString(fmt.Sprintf(ovfEnvPropertyEntry, p.Key, p.Value))
}
buffer.WriteString(fmt.Sprint(ovfEnvPropertyFooter))
buffer.WriteString(fmt.Sprint(ovfEnvFooter))
return buffer.String()
}

191
vendor/github.com/vmware/govmomi/ovf/envelope.go generated vendored Normal file
View File

@ -0,0 +1,191 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
type Envelope struct {
References []File `xml:"References>File"`
// Package level meta-data
Annotation *AnnotationSection `xml:"AnnotationSection"`
Product *ProductSection `xml:"ProductSection"`
Network *NetworkSection `xml:"NetworkSection"`
Disk *DiskSection `xml:"DiskSection"`
OperatingSystem *OperatingSystemSection `xml:"OperatingSystemSection"`
Eula *EulaSection `xml:"EulaSection"`
VirtualHardware *VirtualHardwareSection `xml:"VirtualHardwareSection"`
ResourceAllocation *ResourceAllocationSection `xml:"ResourceAllocationSection"`
DeploymentOption *DeploymentOptionSection `xml:"DeploymentOptionSection"`
// Content: A VirtualSystem or a VirtualSystemCollection
VirtualSystem *VirtualSystem `xml:"VirtualSystem"`
}
type VirtualSystem struct {
Content
Annotation []AnnotationSection `xml:"AnnotationSection"`
Product []ProductSection `xml:"ProductSection"`
OperatingSystem []OperatingSystemSection `xml:"OperatingSystemSection"`
Eula []EulaSection `xml:"EulaSection"`
VirtualHardware []VirtualHardwareSection `xml:"VirtualHardwareSection"`
}
type File struct {
ID string `xml:"id,attr"`
Href string `xml:"href,attr"`
Size uint `xml:"size,attr"`
Compression *string `xml:"compression,attr"`
ChunkSize *int `xml:"chunkSize,attr"`
}
type Content struct {
ID string `xml:"id,attr"`
Info string `xml:"Info"`
Name *string `xml:"Name"`
}
type Section struct {
Required *bool `xml:"required,attr"`
Info string `xml:"Info"`
}
type AnnotationSection struct {
Section
Annotation string `xml:"Annotation"`
}
type ProductSection struct {
Section
Class *string `xml:"class,attr"`
Instance *string `xml:"instance,attr"`
Product string `xml:"Product"`
Vendor string `xml:"Vendor"`
Version string `xml:"Version"`
FullVersion string `xml:"FullVersion"`
ProductURL string `xml:"ProductUrl"`
VendorURL string `xml:"VendorUrl"`
AppURL string `xml:"AppUrl"`
Property []Property `xml:"Property"`
}
type Property struct {
Key string `xml:"key,attr"`
Type string `xml:"type,attr"`
Qualifiers *string `xml:"qualifiers,attr"`
UserConfigurable *bool `xml:"userConfigurable,attr"`
Default *string `xml:"value,attr"`
Password *bool `xml:"password,attr"`
Label *string `xml:"Label"`
Description *string `xml:"Description"`
Values []PropertyConfigurationValue `xml:"Value"`
}
type PropertyConfigurationValue struct {
Value string `xml:"value,attr"`
Configuration *string `xml:"configuration,attr"`
}
type NetworkSection struct {
Section
Networks []Network `xml:"Network"`
}
type Network struct {
Name string `xml:"name,attr"`
Description string `xml:"Description"`
}
type DiskSection struct {
Section
Disks []VirtualDiskDesc `xml:"Disk"`
}
type VirtualDiskDesc struct {
DiskID string `xml:"diskId,attr"`
FileRef *string `xml:"fileRef,attr"`
Capacity string `xml:"capacity,attr"`
CapacityAllocationUnits *string `xml:"capacityAllocationUnits,attr"`
Format *string `xml:"format,attr"`
PopulatedSize *int `xml:"populatedSize,attr"`
ParentRef *string `xml:"parentRef,attr"`
}
type OperatingSystemSection struct {
Section
ID int16 `xml:"id,attr"`
Version *string `xml:"version,attr"`
OSType *string `xml:"osType,attr"`
Description *string `xml:"Description"`
}
type EulaSection struct {
Section
License string `xml:"License"`
}
type VirtualHardwareSection struct {
Section
ID *string `xml:"id,attr"`
Transport *string `xml:"transport,attr"`
System *VirtualSystemSettingData `xml:"System"`
Item []ResourceAllocationSettingData `xml:"Item"`
}
type VirtualSystemSettingData struct {
CIMVirtualSystemSettingData
}
type ResourceAllocationSettingData struct {
CIMResourceAllocationSettingData
Required *bool `xml:"required,attr"`
Configuration *string `xml:"configuration,attr"`
Bound *string `xml:"bound,attr"`
}
type ResourceAllocationSection struct {
Section
Item []ResourceAllocationSettingData `xml:"Item"`
}
type DeploymentOptionSection struct {
Section
Configuration []DeploymentOptionConfiguration `xml:"Configuration"`
}
type DeploymentOptionConfiguration struct {
ID string `xml:"id,attr"`
Default *bool `xml:"default,attr"`
Label string `xml:"Label"`
Description string `xml:"Description"`
}

103
vendor/github.com/vmware/govmomi/ovf/manager.go generated vendored Normal file
View File

@ -0,0 +1,103 @@
/*
Copyright (c) 2015-2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
import (
"context"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
type Manager struct {
types.ManagedObjectReference
c *vim25.Client
}
func NewManager(c *vim25.Client) *Manager {
return &Manager{*c.ServiceContent.OvfManager, c}
}
// CreateDescriptor wraps methods.CreateDescriptor
func (m *Manager) CreateDescriptor(ctx context.Context, obj mo.Reference, cdp types.OvfCreateDescriptorParams) (*types.OvfCreateDescriptorResult, error) {
req := types.CreateDescriptor{
This: m.Reference(),
Obj: obj.Reference(),
Cdp: cdp,
}
res, err := methods.CreateDescriptor(ctx, m.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}
// CreateImportSpec wraps methods.CreateImportSpec
func (m *Manager) CreateImportSpec(ctx context.Context, ovfDescriptor string, resourcePool mo.Reference, datastore mo.Reference, cisp types.OvfCreateImportSpecParams) (*types.OvfCreateImportSpecResult, error) {
req := types.CreateImportSpec{
This: m.Reference(),
OvfDescriptor: ovfDescriptor,
ResourcePool: resourcePool.Reference(),
Datastore: datastore.Reference(),
Cisp: cisp,
}
res, err := methods.CreateImportSpec(ctx, m.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}
// ParseDescriptor wraps methods.ParseDescriptor
func (m *Manager) ParseDescriptor(ctx context.Context, ovfDescriptor string, pdp types.OvfParseDescriptorParams) (*types.OvfParseDescriptorResult, error) {
req := types.ParseDescriptor{
This: m.Reference(),
OvfDescriptor: ovfDescriptor,
Pdp: pdp,
}
res, err := methods.ParseDescriptor(ctx, m.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}
// ValidateHost wraps methods.ValidateHost
func (m *Manager) ValidateHost(ctx context.Context, ovfDescriptor string, host mo.Reference, vhp types.OvfValidateHostParams) (*types.OvfValidateHostResult, error) {
req := types.ValidateHost{
This: m.Reference(),
OvfDescriptor: ovfDescriptor,
Host: host.Reference(),
Vhp: vhp,
}
res, err := methods.ValidateHost(ctx, m.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}

35
vendor/github.com/vmware/govmomi/ovf/ovf.go generated vendored Normal file
View File

@ -0,0 +1,35 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
import (
"io"
"github.com/vmware/govmomi/vim25/xml"
)
func Unmarshal(r io.Reader) (*Envelope, error) {
var e Envelope
dec := xml.NewDecoder(r)
err := dec.Decode(&e)
if err != nil {
return nil, err
}
return &e, nil
}

2
vendor/modules.txt vendored
View File

@ -485,6 +485,7 @@ github.com/nu7hatch/gouuid
github.com/olekukonko/tablewriter
# github.com/oracle/oci-go-sdk v1.8.0
github.com/oracle/oci-go-sdk/common
github.com/oracle/oci-go-sdk/common/auth
github.com/oracle/oci-go-sdk/core
# github.com/outscale/osc-go v0.0.1
github.com/outscale/osc-go/oapi
@ -572,6 +573,7 @@ github.com/vmware/govmomi/find
github.com/vmware/govmomi/list
github.com/vmware/govmomi/nfc
github.com/vmware/govmomi/object
github.com/vmware/govmomi/ovf
github.com/vmware/govmomi/property
github.com/vmware/govmomi/session
github.com/vmware/govmomi/task

View File

@ -9,7 +9,7 @@ import (
var GitCommit string
// The main version number that is being run at the moment.
const Version = "1.5.5"
const Version = "1.5.6"
// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release

View File

@ -2,7 +2,7 @@ set :base_url, "https://www.packer.io/"
activate :hashicorp do |h|
h.name = "packer"
h.version = "1.5.4"
h.version = "1.5.5"
h.github_slug = "hashicorp/packer"
h.website_root = "website"
end

View File

@ -58,6 +58,9 @@ contribution here!
* [jakobadam/packer-qemu-templates](https://github.com/jakobadam/packer-qemu-templates)
\- QEMU templates for various operating systems
* [lucidone/baseliner](https://git.sr.ht/~lucidone/baseliner) - Example of using
QEMU + Ansible with Packer
## Wrappers
- [packer-config](https://github.com/ianchesal/packer-config) - a Ruby model that lets you build Packer configurations in Ruby

View File

@ -182,7 +182,7 @@ variables are available:
-&gt; **Note:** Packer uses pre-built AMIs as the source for building images.
These source AMIs may include volumes that are not flagged to be destroyed on
termination of the instance building the new image. In addition to those
volumes created by this builder, any volumes inn the source AMI which are not
volumes created by this builder, any volumes in the source AMI which are not
marked for deletion on termination will remain in your account.
## Build function template engine variables

View File

@ -33,6 +33,9 @@ authentication see the documentation on [Required Keys and
OCIDs](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/apisigningkey.htm)
([Oracle Cloud
IDs](https://docs.us-phoenix-1.oraclecloud.com/Content/General/Concepts/identifiers.htm)).
Alternatively you can use [Instance
Principals](https://docs.cloud.oracle.com/en-us/iaas/Content/Identity/Tasks/callingservicesfrominstances.htm)
in which case you don't need the above user authorization.
## Configuration Reference
@ -65,11 +68,6 @@ builder.
- `compartment_ocid` (string) - The OCID of the
[compartment](https://docs.us-phoenix-1.oraclecloud.com/Content/GSG/Tasks/choosingcompartments.htm)
- `fingerprint` (string) - Fingerprint for the OCI API signing key. Overrides
value provided by the [OCI config
file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm)
if present.
- `shape` (string) - The template that determines the number of CPUs, amount
of memory, and other resources allocated to a newly created instance.
@ -90,41 +88,51 @@ builder.
### Optional
- `use_instance_principals` (boolean) - Whether to use [Instance
Principals](https://docs.cloud.oracle.com/en-us/iaas/Content/Identity/Tasks/callingservicesfrominstances.htm)
instead of User Principals. If this key is set to true, setting any one of the `access_cfg_file`,
`access_cfg_file_account`, `region`, `tenancy_ocid`, `user_ocid`, `key_file`, `fingerprint`,
`pass_phrase` will result in configuration validation errors.
Defaults to `false`.
- `access_cfg_file` (string) - The path to the [OCI config
file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm).
This cannot be used along with the `use_instance_principals` key.
Defaults to `$HOME/.oci/config`.
- `access_cfg_file_account` (string) - The specific account in the [OCI
config
file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm)
to use. Defaults to `DEFAULT`.
- `access_cfg_file_account` (string) - The specific account in the [OCI config
file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm) to use.
This cannot be used along with the `use_instance_principals` key.
Defaults to `DEFAULT`.
- `region` (string) - An Oracle Cloud Infrastructure region. Overrides value provided by the
[OCI config file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm)
if present. This cannot be used along with the `use_instance_principals` key.
- `tenancy_ocid` (string) - The OCID of your tenancy. Overrides value provided by the [OCI config
file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm) if present.
This cannot be used along with the `use_instance_principals` key.
- `user_ocid` (string) - The OCID of the user calling the OCI API. Overrides value provided by the
[OCI config file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm)
if present. This cannot be used along with the `use_instance_principals` key.
- `key_file` (string) - Full path and filename of the OCI API signing key. Overrides value provided
by the [OCI config file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm)
if present. This cannot be used along with the `use_instance_principals` key.
- `fingerprint` (string) - Fingerprint for the OCI API signing key. Overrides value provided by the
[OCI config file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm) if
present. This cannot be used along with the `use_instance_principals` key.
- `pass_phrase` (string) - Pass phrase used to decrypt the OCI API signing key. Overrides value provided
by the [OCI config file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm)
if present. This cannot be used along with the `use_instance_principals` key.
- `image_name` (string) - The name to assign to the resulting custom image.
- `key_file` (string) - Full path and filename of the OCI API signing key.
Overrides value provided by the [OCI config
file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm)
if present.
- `pass_phrase` (string) - Pass phrase used to decrypt the OCI API signing
key. Overrides value provided by the [OCI config
file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm)
if present.
- `region` (string) - An Oracle Cloud Infrastructure region. Overrides value
provided by the [OCI config
file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm)
if present.
- `tenancy_ocid` (string) - The OCID of your tenancy. Overrides value
provided by the [OCI config
file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm)
if present.
- `user_ocid` (string) - The OCID of the user calling the OCI API. Overrides
value provided by the [OCI config
file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm)
if present.
- `instance_name` (string) - The name to assign to the instance used for the image creation process.
If not set a name of the form `instanceYYYYMMDDhhmmss` will be used.
- `use_private_ip` (boolean) - Use private ip addresses to connect to the
instance via ssh.
@ -191,3 +199,46 @@ substituted with the letter `a` and OCIDS have been shortened for brevity.
"type": "oracle-oci"
}
```
## Using Instance Principals
Here is a basic example. Note that account specific configuration has been
substituted with the letter `a` and OCIDS have been shortened for brevity.
```json
{
"use_instance_principals": "true",
"availability_domain": "aaaa:PHX-AD-1",
"base_image_ocid": "ocid1.image.oc1.phx.aaaaaaaa5yu6pw3riqtuhxzov7fdngi4tsteganmao54nq3pyxu3hxcuzmoa",
"compartment_ocid": "ocid1.compartment.oc1..aaa",
"image_name": "ExampleImage",
"shape": "VM.Standard2.1",
"ssh_username": "opc",
"subnet_ocid": "ocid1.subnet.oc1..aaa",
"type": "oracle-oci"
}
```
```
[opc@packerhost ~]$ packer build packer.json
oracle-oci: output will be in this color.
==> oracle-oci: Creating temporary ssh key for instance...
==> oracle-oci: Creating instance...
==> oracle-oci: Created instance (ocid1.instance.oc1.phx.aaa).
==> oracle-oci: Waiting for instance to enter 'RUNNING' state...
==> oracle-oci: Instance 'RUNNING'.
==> oracle-oci: Instance has IP: 10.10.10.10.
==> oracle-oci: Using ssh communicator to connect: 10.10.10.10
==> oracle-oci: Waiting for SSH to become available...
==> oracle-oci: Connected to SSH!
==> oracle-oci: Creating image from instance...
==> oracle-oci: Image created.
==> oracle-oci: Terminating instance (ocid1.instance.oc1.phx.aaa)...
==> oracle-oci: Terminated instance.
Build 'oracle-oci' finished.
==> Builds finished. The artifacts of successful builds are:
--> oracle-oci: An image was created: 'ExampleImage' (OCID: ocid1.image.oc1.phx.aaa) in region 'us-phoenix-1'
[opc@packerhost ~]$
```

View File

@ -3,7 +3,7 @@ description: |
The osc-bsusurrogate Packer builder is like the chroot builder, but does not
require running inside an Outscale virtual machine.
layout: docs
page_title: 'Outacale BSU Surrogate - Builders'
page_title: 'Outscale BSU Surrogate - Builders'
sidebar_current: 'docs-builders-osc-bsusurrogate'
---

View File

@ -3,7 +3,7 @@ description: |
The osc-bsuvolume Packer builder is like the BSU builder, but is intended to
create BSU volumes rather than a machine image.
layout: docs
page_title: 'Amazon BSU Volume - Builders'
page_title: 'Outscale BSU Volume - Builders'
sidebar_current: 'docs-builders-osc-bsuvolume'
---
@ -11,7 +11,7 @@ sidebar_current: 'docs-builders-osc-bsuvolume'
Type: `osc-bsuvolume`
The `osc-bsuvolume` Packer builder is able to create Ouscale Block Stogate Unit
The `osc-bsuvolume` Packer builder is able to create Outscale Block Stogate Unit
volumes which are prepopulated with filesystems or data.
This builder builds BSU volumes by launching an Outscale VM from a source OMI,
@ -133,7 +133,7 @@ builder.
- `snapshot_users` (array of strings) - A list of account IDs that have
access to create volumes from the snapshot(s). By default no additional
users other than the user creating the OMIS has permissions to create
users other than the user creating the OMI has permissions to create
volumes from the backing snapshot(s).
- `source_omi_filter` (object) - Filters used to populate the `source_omi` field.
@ -155,8 +155,8 @@ builder.
}
```
This selects an Ubuntu 16.04 HVM BSU OMIS from Canonical. NOTE:
This will fail unless *exactly* one OMIS is returned. In the above example,
This selects an Ubuntu 16.04 HVM BSU OMI from Canonical. NOTE:
This will fail unless *exactly* one OMI is returned. In the above example,
`most_recent` will cause this to succeed by selecting the newest image.
- `ssh_keypair_name` (string) - If specified, this is the key that will be used for SSH with the machine. The key must match a key pair name loaded up into Outscale. By default, this is blank, and Packer will generate a temporary keypair unless [`ssh_password`](../templates/communicator.html#ssh_password) is used. [`ssh_private_key_file`](../templates/communicator.html#ssh_private_key_file) or `ssh_agent_auth` must be specified when `ssh_keypair_name` is utilized.
@ -251,7 +251,7 @@ builder.
environmental variables. See the configuration reference in the section above
for more information on what environmental variables Packer will look for.
Further information on locating OMIS IDs and their relationship to VM
Further information on locating OMI's IDs and their relationship to VM
types and regions can be found in the Outscale Documentation [reference](https://wiki.outscale.net/display/EN/Official+OMIs+Reference).
## Accessing the Instance to Debug
@ -278,5 +278,5 @@ variables are available:
-&gt; **Note:** Packer uses pre-built OMIs as the source for building images.
These source OMIs may include volumes that are not flagged to be destroyed on
termination of the instance building the new image. In addition to those
volumes created by this builder, any volumes inn the source OMI which are not
volumes created by this builder, any volumes in the source OMI which are not
marked for deletion on termination will remain in your account.

View File

@ -180,6 +180,9 @@ builder.
- `unmount_iso` (bool) - If true, remove the mounted ISO from the template
after finishing. Defaults to `false`.
`onboot` (boolean) - Specifies whether a VM will be started during system
bootup. Defaults to `false`.
- `qemu_agent` (boolean) - Disables QEMU Agent option for this VM. When enabled,
then `qemu-guest-agent` must be installed on the guest. When disabled, then

View File

@ -82,6 +82,17 @@ necessary for this build to succeed and can be found further down the page.
<%= partial "partials/helper/communicator/WinRM-not-required" %>
### Export Configuration
<%= partial "partials/builder/vsphere/common/ExportConfig" %>
### Optional:
<%= partial "partials/builder/vsphere/common/ExportConfig-not-required" %>
#### Output Configuration:
<%= partial "partials/builder/vsphere/common/OutputConfig-not-required" %>
## Working with Clusters
#### Standalone Hosts
Only use the `host` option. Optionally specify a `resource_pool`:

View File

@ -106,6 +106,17 @@ from the datastore. Example:
### Floppy Configuration
<%= partial "partials/builder/vsphere/iso/FloppyConfig-not-required" %>
### Export Configuration
<%= partial "partials/builder/vsphere/common/ExportConfig" %>
### Optional:
<%= partial "partials/builder/vsphere/common/ExportConfig-not-required" %>
#### Output Configuration:
<%= partial "partials/builder/vsphere/common/OutputConfig-not-required" %>
### Extra Configuration Parameters
<%= partial "partials/builder/vsphere/common/ConfigParamsConfig-not-required" %>

View File

@ -3,24 +3,24 @@
# See https://www.netlify.com/docs/redirects/ for documentation. Please do not
# modify or delete existing redirects without first verifying internally.
/docs/installation.html /docs/install/index.html
/docs/command-line/machine-readable.html /docs/commands/index.html
/docs/command-line/introduction.html /docs/commands/index.html
/docs/templates/introduction.html /docs/templates/index.html
/docs/builders/azure-setup.html /docs/builders/azure.html
/docs/templates/veewee-to-packer.html /guides/veewee-to-packer.html
/docs/extend/developing-plugins.html /docs/extending/plugins.html
/docs/extending/developing-plugins.html /docs/extending/plugins.html
/docs/extend/builder.html /docs/extending/custom-builders.html
/docs/getting-started/setup.html /docs/getting-started/install.html
/docs/other/community.html /community-tools.html
/downloads-community.html /community-tools.html
/community /community.html
/community/index.html /community.html
/docs/other/environmental-variables.html /docs/other/environment-variables.html
/docs/platforms.html /docs/builders/index.html
/intro/platforms.html /docs/builders/index.html
/docs/templates/configuration-templates.html /docs/templates/engine.html
/docs/machine-readable/* /docs/commands/index.html
/docs/command-line/* /docs/commands/:splat
/docs/extend/* /docs/extending/:splat
/docs/installation.html /docs/install/index.html 301!
/docs/command-line/machine-readable.html /docs/commands/index.html 301!
/docs/command-line/introduction.html /docs/commands/index.html 301!
/docs/templates/introduction.html /docs/templates/index.html 301!
/docs/builders/azure-setup.html /docs/builders/azure.html 301!
/docs/templates/veewee-to-packer.html /guides/veewee-to-packer.html 301!
/docs/extend/developing-plugins.html /docs/extending/plugins.html 301!
/docs/extending/developing-plugins.html /docs/extending/plugins.html 301!
/docs/extend/builder.html /docs/extending/custom-builders.html 301!
/docs/getting-started/setup.html /docs/getting-started/install.html 301!
/docs/other/community.html /community-tools.html 301!
/downloads-community.html /community-tools.html 301!
/community /community.html 301!
/community/index.html /community.html 301!
/docs/other/environmental-variables.html /docs/other/environment-variables.html 301!
/docs/platforms.html /docs/builders/index.html 301!
/intro/platforms.html /docs/builders/index.html 301!
/docs/templates/configuration-templates.html /docs/templates/engine.html 301!
/docs/machine-readable/* /docs/commands/index.html 301!
/docs/command-line/* /docs/commands/:splat 301!
/docs/extend/* /docs/extending/:splat 301!

View File

@ -8,6 +8,10 @@
usually a project (also called the "tenant") with whom the image is
shared.
- `image_auto_accept_members` (bool) - When true, perform the image accept so the members can see the image in their
project. This requires a user with priveleges both in the build project and
in the members provided. Defaults to false.
- `image_disk_format` (string) - Disk format of the resulting image. This option works if
use_blockstorage_volume is true.

View File

@ -1,5 +1,8 @@
<!-- Code generated from the comments of the DriverConfig struct in builder/vmware/common/driver_config.go; DO NOT EDIT MANUALLY -->
- `cleanup_remote_cache` (bool) - When set to true, Packer will cleanup the cache folder where the ISO file is stored during the build on the remote machine.
By default, this is set to false.
- `fusion_app_path` (string) - Path to "VMware Fusion.app". By default this is
/Applications/VMware Fusion.app but this setting allows you to
customize this.

View File

@ -4,4 +4,5 @@
for linked clones. Defaults to `false`.
- `convert_to_template` (bool) - Convert VM to a template. Defaults to `false`.
- `export` (\*common.ExportConfig) - Export

View File

@ -0,0 +1,25 @@
<!-- Code generated from the comments of the ExportConfig struct in builder/vsphere/common/step_export.go; DO NOT EDIT MANUALLY -->
- `name` (string) - name of the ovf. defaults to the name of the VM
- `force` (bool) - overwrite ovf if it exists
- `images` (bool) - include iso and img image files that are attached to the VM
- `manifest` (string) - generate manifest using sha1, sha256, sha512. Defaults to 'sha256'. Use 'none' for no manifest.
- `options` ([]string) - Advanced ovf export options. Options can include:
* mac - MAC address is exported for all ethernet devices
* uuid - UUID is exported for all virtual machines
* extraconfig - all extra configuration options are exported for a virtual machine
* nodevicesubtypes - resource subtypes for CD/DVD drives, floppy drives, and serial and parallel ports are not exported
For example, adding the following export config option would output the mac addresses for all Ethernet devices in the ovf file:
```json
...
"export": {
"options": ["mac"]
},
```

View File

@ -0,0 +1,21 @@
<!-- Code generated from the comments of the ExportConfig struct in builder/vsphere/common/step_export.go; DO NOT EDIT MANUALLY -->
You may optionally export an ovf from VSphere to the instance running Packer.
Example usage:
```json
...
"vm_name": "example-ubuntu",
...
"export": {
"force": true,
"output_directory": "./output_vsphere"
},
```
The above configuration would create the following files:
```
./output_vsphere/example-ubuntu-disk-0.vmdk
./output_vsphere/example-ubuntu.mf
./output_vsphere/example-ubuntu.ovf
```

View File

@ -0,0 +1,10 @@
<!-- Code generated from the comments of the OutputConfig struct in builder/vsphere/common/output_config.go; DO NOT EDIT MANUALLY -->
- `output_directory` (string) - This setting specifies the directory that
artifacts from the build, such as the virtual machine files and disks,
will be output to. The path to the directory may be relative or
absolute. If relative, the path is relative to the working directory
packer is executed from. This directory must not exist or, if
created, must be empty prior to running the builder. By default this is
"output-BUILDNAME" where "BUILDNAME" is the name of the build.

View File

@ -4,4 +4,7 @@
for linked clones. Defaults to `false`.
- `convert_to_template` (bool) - Convert VM to a template. Defaults to `false`.
- `export` (\*common.ExportConfig) - Configuration for exporting VM to an ovf file.
The VM will not be exported if no [Export Configuration](#export-configuration) is specified.

View File

@ -12,10 +12,6 @@ go-getter supports the following protocols:
* HTTP
* Amazon S3
\~&gt; On Windows, using a symlink to refer to local files is currently
unsupported. Packer will always copy a local iso into the Packer cache
directory.
Examples:
go-getter can guess the checksum type based on `iso_checksum` len.