update ucloud packer
This commit is contained in:
parent
61f9d3a9fe
commit
2c513fce64
|
@ -37,6 +37,8 @@ func (c *AccessConfig) Client() (*UCloudClient, error) {
|
||||||
cfg.BaseUrl = c.BaseUrl
|
cfg.BaseUrl = c.BaseUrl
|
||||||
}
|
}
|
||||||
cfg.UserAgent = fmt.Sprintf("Packer-UCloud/%s", version.FormattedVersion())
|
cfg.UserAgent = fmt.Sprintf("Packer-UCloud/%s", version.FormattedVersion())
|
||||||
|
// set default max retry count
|
||||||
|
cfg.MaxRetries = 3
|
||||||
|
|
||||||
cred := auth.NewCredential()
|
cred := auth.NewCredential()
|
||||||
cred.PublicKey = c.PublicKey
|
cred.PublicKey = c.PublicKey
|
||||||
|
|
|
@ -18,6 +18,10 @@ const (
|
||||||
IpTypePrivate = "Private"
|
IpTypePrivate = "Private"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultCreateImageTimeOut = 3600
|
||||||
|
)
|
||||||
|
|
||||||
var BootDiskTypeMap = NewStringConverter(map[string]string{
|
var BootDiskTypeMap = NewStringConverter(map[string]string{
|
||||||
"cloud_ssd": "CLOUD_SSD",
|
"cloud_ssd": "CLOUD_SSD",
|
||||||
"local_normal": "LOCAL_NORMAL",
|
"local_normal": "LOCAL_NORMAL",
|
||||||
|
|
|
@ -17,19 +17,20 @@ type ImageDestination struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageConfig struct {
|
type ImageConfig struct {
|
||||||
ImageName string `mapstructure:"image_name"`
|
ImageName string `mapstructure:"image_name"`
|
||||||
ImageDescription string `mapstructure:"image_description"`
|
ImageDescription string `mapstructure:"image_description"`
|
||||||
ImageDestinations []ImageDestination `mapstructure:"image_copy_to_mappings"`
|
ImageDestinations []ImageDestination `mapstructure:"image_copy_to_mappings"`
|
||||||
|
WaitImageReadyTimeout int `mapstructure:"wait_image_ready_timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var imageNamePattern = regexp.MustCompile(`^[A-Za-z0-9\p{Han}-_\[\]:,.]{1,63}$`)
|
var ImageNamePattern = regexp.MustCompile(`^[A-Za-z0-9\p{Han}-_\[\]:,.]{1,63}$`)
|
||||||
|
|
||||||
func (c *ImageConfig) Prepare(ctx *interpolate.Context) []error {
|
func (c *ImageConfig) Prepare(ctx *interpolate.Context) []error {
|
||||||
var errs []error
|
var errs []error
|
||||||
imageName := c.ImageName
|
imageName := c.ImageName
|
||||||
if imageName == "" {
|
if imageName == "" {
|
||||||
errs = append(errs, fmt.Errorf("%q must be set", "image_name"))
|
errs = append(errs, fmt.Errorf("%q must be set", "image_name"))
|
||||||
} else if !imageNamePattern.MatchString(imageName) {
|
} else if !ImageNamePattern.MatchString(imageName) {
|
||||||
errs = append(errs, fmt.Errorf("expected %q to be 1-63 characters and only support chinese, english, numbers, '-_,.:[]', got %q", "image_name", imageName))
|
errs = append(errs, fmt.Errorf("expected %q to be 1-63 characters and only support chinese, english, numbers, '-_,.:[]', got %q", "image_name", imageName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +44,10 @@ func (c *ImageConfig) Prepare(ctx *interpolate.Context) []error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.WaitImageReadyTimeout <= 0 {
|
||||||
|
c.WaitImageReadyTimeout = DefaultCreateImageTimeOut
|
||||||
|
}
|
||||||
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
@ -61,7 +66,7 @@ func (imageDestination *ImageDestination) validate() []error {
|
||||||
errs = append(errs, fmt.Errorf("%q must be set", "image_copy_project"))
|
errs = append(errs, fmt.Errorf("%q must be set", "image_copy_project"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if imageDestination.Name != "" && !imageNamePattern.MatchString(imageDestination.Name) {
|
if imageDestination.Name != "" && !ImageNamePattern.MatchString(imageDestination.Name) {
|
||||||
errs = append(errs, fmt.Errorf("expected %q to be 1-63 characters and only support chinese, english, numbers, '-_,.:[]', got %q", "image_copy_name", imageDestination.Name))
|
errs = append(errs, fmt.Errorf("expected %q to be 1-63 characters and only support chinese, english, numbers, '-_,.:[]', got %q", "image_copy_name", imageDestination.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,9 +120,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||||
&stepStopInstance{},
|
&stepStopInstance{},
|
||||||
&stepCreateImage{},
|
&stepCreateImage{},
|
||||||
&stepCopyUCloudImage{
|
&stepCopyUCloudImage{
|
||||||
ImageDestinations: b.config.ImageDestinations,
|
ImageDestinations: b.config.ImageDestinations,
|
||||||
RegionId: b.config.Region,
|
RegionId: b.config.Region,
|
||||||
ProjectId: b.config.ProjectId,
|
ProjectId: b.config.ProjectId,
|
||||||
|
WaitImageReadyTimeout: b.config.WaitImageReadyTimeout,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepCopyUCloudImage struct {
|
type stepCopyUCloudImage struct {
|
||||||
ImageDestinations []ucloudcommon.ImageDestination
|
ImageDestinations []ucloudcommon.ImageDestination
|
||||||
RegionId string
|
RegionId string
|
||||||
ProjectId string
|
ProjectId string
|
||||||
|
WaitImageReadyTimeout int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stepCopyUCloudImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *stepCopyUCloudImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
@ -63,9 +64,11 @@ func (s *stepCopyUCloudImage) Run(ctx context.Context, state multistep.StateBag)
|
||||||
ui.Message("Waiting for the copied images to become available...")
|
ui.Message("Waiting for the copied images to become available...")
|
||||||
|
|
||||||
err := retry.Config{
|
err := retry.Config{
|
||||||
Tries: 200,
|
StartTimeout: time.Duration(s.WaitImageReadyTimeout) * time.Second,
|
||||||
ShouldRetry: func(err error) bool { return ucloudcommon.IsNotCompleteError(err) },
|
ShouldRetry: func(err error) bool {
|
||||||
RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 12 * time.Second, Multiplier: 2}).Linear,
|
return ucloudcommon.IsNotCompleteError(err)
|
||||||
|
},
|
||||||
|
RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 12 * time.Second, Multiplier: 2}).Linear,
|
||||||
}.Run(ctx, func(ctx context.Context) error {
|
}.Run(ctx, func(ctx context.Context) error {
|
||||||
for _, v := range expectedImages.GetAll() {
|
for _, v := range expectedImages.GetAll() {
|
||||||
imageSet, err := client.DescribeImageByInfo(v.ProjectId, v.Region, v.ImageId)
|
imageSet, err := client.DescribeImageByInfo(v.ProjectId, v.Region, v.ImageId)
|
||||||
|
|
|
@ -38,7 +38,7 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul
|
||||||
ui.Message(fmt.Sprintf("Waiting for the created image %q to become available...", resp.ImageId))
|
ui.Message(fmt.Sprintf("Waiting for the created image %q to become available...", resp.ImageId))
|
||||||
|
|
||||||
err = retry.Config{
|
err = retry.Config{
|
||||||
Tries: 200,
|
StartTimeout: time.Duration(config.WaitImageReadyTimeout) * time.Second,
|
||||||
ShouldRetry: func(err error) bool {
|
ShouldRetry: func(err error) bool {
|
||||||
return ucloudcommon.IsExpectedStateError(err)
|
return ucloudcommon.IsExpectedStateError(err)
|
||||||
},
|
},
|
||||||
|
|
|
@ -243,7 +243,6 @@ func (s *stepCreateInstance) buildCreateInstanceRequest(state multistep.StateBag
|
||||||
req.MinimalCpuPlatform = ucloud.String("Intel/Auto")
|
req.MinimalCpuPlatform = ucloud.String("Intel/Auto")
|
||||||
if t.HostType == "o" {
|
if t.HostType == "o" {
|
||||||
req.MachineType = ucloud.String("O")
|
req.MachineType = ucloud.String("O")
|
||||||
req.MinimalCpuPlatform = ucloud.String("Intel/Cascadelake")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, ok := state.GetOk("security_group_id"); ok {
|
if v, ok := state.GetOk("security_group_id"); ok {
|
||||||
|
|
|
@ -175,7 +175,6 @@ var Provisioners = map[string]packer.Provisioner{
|
||||||
|
|
||||||
var PostProcessors = map[string]packer.PostProcessor{
|
var PostProcessors = map[string]packer.PostProcessor{
|
||||||
"alicloud-import": new(alicloudimportpostprocessor.PostProcessor),
|
"alicloud-import": new(alicloudimportpostprocessor.PostProcessor),
|
||||||
"ucloud-import": new(ucloudimportpostprocessor.PostProcessor),
|
|
||||||
"amazon-import": new(amazonimportpostprocessor.PostProcessor),
|
"amazon-import": new(amazonimportpostprocessor.PostProcessor),
|
||||||
"artifice": new(artificepostprocessor.PostProcessor),
|
"artifice": new(artificepostprocessor.PostProcessor),
|
||||||
"checksum": new(checksumpostprocessor.PostProcessor),
|
"checksum": new(checksumpostprocessor.PostProcessor),
|
||||||
|
@ -190,6 +189,7 @@ var PostProcessors = map[string]packer.PostProcessor{
|
||||||
"googlecompute-import": new(googlecomputeimportpostprocessor.PostProcessor),
|
"googlecompute-import": new(googlecomputeimportpostprocessor.PostProcessor),
|
||||||
"manifest": new(manifestpostprocessor.PostProcessor),
|
"manifest": new(manifestpostprocessor.PostProcessor),
|
||||||
"shell-local": new(shelllocalpostprocessor.PostProcessor),
|
"shell-local": new(shelllocalpostprocessor.PostProcessor),
|
||||||
|
"ucloud-import": new(ucloudimportpostprocessor.PostProcessor),
|
||||||
"vagrant": new(vagrantpostprocessor.PostProcessor),
|
"vagrant": new(vagrantpostprocessor.PostProcessor),
|
||||||
"vagrant-cloud": new(vagrantcloudpostprocessor.PostProcessor),
|
"vagrant-cloud": new(vagrantcloudpostprocessor.PostProcessor),
|
||||||
"vsphere": new(vspherepostprocessor.PostProcessor),
|
"vsphere": new(vspherepostprocessor.PostProcessor),
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
install
|
||||||
|
cdrom
|
||||||
|
lang en_US.UTF-8
|
||||||
|
keyboard us
|
||||||
|
network --bootproto=dhcp
|
||||||
|
rootpw ucloud_2019
|
||||||
|
firewall --disabled
|
||||||
|
selinux --permissive
|
||||||
|
timezone UTC
|
||||||
|
unsupported_hardware
|
||||||
|
bootloader --location=mbr
|
||||||
|
text
|
||||||
|
skipx
|
||||||
|
zerombr
|
||||||
|
clearpart --all
|
||||||
|
autopart
|
||||||
|
auth --enableshadow --passalgo=sha512
|
||||||
|
firstboot --disabled
|
||||||
|
reboot
|
||||||
|
|
||||||
|
%packages --nobase --ignoremissing
|
||||||
|
sudo
|
||||||
|
gcc
|
||||||
|
make
|
||||||
|
%end
|
|
@ -1,42 +0,0 @@
|
||||||
install
|
|
||||||
cdrom
|
|
||||||
lang en_US.UTF-8
|
|
||||||
keyboard us
|
|
||||||
network --bootproto=dhcp
|
|
||||||
rootpw vagrant
|
|
||||||
firewall --disabled
|
|
||||||
selinux --permissive
|
|
||||||
timezone UTC
|
|
||||||
unsupported_hardware
|
|
||||||
bootloader --location=mbr
|
|
||||||
text
|
|
||||||
skipx
|
|
||||||
zerombr
|
|
||||||
clearpart --all
|
|
||||||
autopart
|
|
||||||
auth --enableshadow --passalgo=sha512
|
|
||||||
firstboot --disabled
|
|
||||||
reboot
|
|
||||||
user --name=vagrant --password=vagrant
|
|
||||||
|
|
||||||
%packages --nobase --ignoremissing
|
|
||||||
# vagrant needs this to copy initial files via scp
|
|
||||||
openssh-clients
|
|
||||||
sudo
|
|
||||||
kernel-headers
|
|
||||||
kernel-devel
|
|
||||||
gcc
|
|
||||||
make
|
|
||||||
perl
|
|
||||||
wget
|
|
||||||
nfs-utils
|
|
||||||
-fprintd-pam
|
|
||||||
-intltool
|
|
||||||
%end
|
|
||||||
|
|
||||||
%post
|
|
||||||
# Force to set SELinux to a permissive mode
|
|
||||||
sed -i -e 's/\(^SELINUX=\).*$/\1permissive/' /etc/selinux/config
|
|
||||||
# sudo
|
|
||||||
echo "%vagrant ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/vagrant
|
|
||||||
%end
|
|
|
@ -1,22 +1,16 @@
|
||||||
{"variables": {
|
{"variables": {
|
||||||
"build_timestamp": "{{isotime \"20060102150405\"}}",
|
"ucloud_public_key": "{{env `UCLOUD_PUBLIC_KEY`}}",
|
||||||
"cpus": "1",
|
"ucloud_private_key": "{{env `UCLOUD_PRIVATE_KEY`}}",
|
||||||
|
"ucloud_project_id": "{{env `UCLOUD_PROJECT_ID`}}",
|
||||||
"disk_size": "4096",
|
"disk_size": "4096",
|
||||||
"git_revision": "__unknown_git_revision__",
|
|
||||||
"headless": "",
|
"headless": "",
|
||||||
"http_proxy": "{{env `http_proxy`}}",
|
"iso_checksum": "0da4a1206e7642906e33c0f155d2f835",
|
||||||
"https_proxy": "{{env `https_proxy`}}",
|
|
||||||
"iso_checksum": "7002b56184180591a8fa08c2fe0c7338",
|
|
||||||
"iso_checksum_type": "md5",
|
"iso_checksum_type": "md5",
|
||||||
"iso_name": "CentOS-7-x86_64-Minimal-1908.iso",
|
"iso_name": "CentOS-6.10-x86_64-minimal.iso",
|
||||||
"ks_path": "centos-6.8/ks.cfg",
|
"ks_path": "centos-6.10/ks.cfg",
|
||||||
"memory": "512",
|
"mirror": "http://mirrors.ustc.edu.cn/centos",
|
||||||
"metadata": "floppy/dummy_metadata.json",
|
"mirror_directory": "6.10/isos/x86_64",
|
||||||
"mirror": "https://mirrors.sjtug.sjtu.edu.cn/centos",
|
"template": "centos-6.10-x86_64"
|
||||||
"mirror_directory": "7.7.1908/isos/x86_64",
|
|
||||||
"no_proxy": "{{env `no_proxy`}}",
|
|
||||||
"template": "centos-7.7-x86_64",
|
|
||||||
"version": "2.1.TIMESTAMP"
|
|
||||||
},
|
},
|
||||||
"builders":[
|
"builders":[
|
||||||
{
|
{
|
||||||
|
@ -33,7 +27,7 @@
|
||||||
"iso_url": "{{user `mirror`}}/{{user `mirror_directory`}}/{{user `iso_name`}}",
|
"iso_url": "{{user `mirror`}}/{{user `mirror_directory`}}/{{user `iso_name`}}",
|
||||||
"output_directory": "packer-{{user `template`}}-qemu",
|
"output_directory": "packer-{{user `template`}}-qemu",
|
||||||
"shutdown_command": "echo 'vagrant'|sudo -S /sbin/halt -h -p",
|
"shutdown_command": "echo 'vagrant'|sudo -S /sbin/halt -h -p",
|
||||||
"ssh_password": "vagrant",
|
"ssh_password": "ucloud_2019",
|
||||||
"ssh_port": 22,
|
"ssh_port": 22,
|
||||||
"ssh_username": "root",
|
"ssh_username": "root",
|
||||||
"ssh_timeout": "10000s",
|
"ssh_timeout": "10000s",
|
||||||
|
@ -50,12 +44,15 @@
|
||||||
"post-processors":[
|
"post-processors":[
|
||||||
{
|
{
|
||||||
"type":"ucloud-import",
|
"type":"ucloud-import",
|
||||||
"ufile_bucket_name": "packer",
|
"public_key": "{{user `ucloud_public_key`}}",
|
||||||
"image_name": "packer_import",
|
"private_key": "{{user `ucloud_private_key`}}",
|
||||||
|
"project_id": "{{user `ucloud_project_id`}}",
|
||||||
|
"region":"cn-bj2",
|
||||||
|
"ufile_bucket_name": "packer-test",
|
||||||
|
"image_name": "packer_import_test",
|
||||||
"image_os_type": "CentOS",
|
"image_os_type": "CentOS",
|
||||||
"image_os_name": "CentOS 6.8 64位",
|
"image_os_name": "CentOS 6.10 64位",
|
||||||
"format": "raw",
|
"format": "raw"
|
||||||
"region":"cn-bj2"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -1,62 +0,0 @@
|
||||||
{"variables": {
|
|
||||||
"build_timestamp": "{{isotime \"20060102150405\"}}",
|
|
||||||
"cpus": "1",
|
|
||||||
"disk_size": "4096",
|
|
||||||
"git_revision": "__unknown_git_revision__",
|
|
||||||
"headless": "",
|
|
||||||
"http_proxy": "{{env `http_proxy`}}",
|
|
||||||
"https_proxy": "{{env `https_proxy`}}",
|
|
||||||
"iso_checksum": "7002b56184180591a8fa08c2fe0c7338",
|
|
||||||
"iso_checksum_type": "md5",
|
|
||||||
"iso_name": "CentOS-7-x86_64-Minimal-1908.iso",
|
|
||||||
"ks_path": "centos-6.8/ks.cfg",
|
|
||||||
"memory": "512",
|
|
||||||
"metadata": "floppy/dummy_metadata.json",
|
|
||||||
"mirror": "http://mirrors.aliyun.com/centos",
|
|
||||||
"mirror_directory": "7/isos/x86_64",
|
|
||||||
"no_proxy": "{{env `no_proxy`}}",
|
|
||||||
"template": "centos-7-x86_64",
|
|
||||||
"version": "2.1.TIMESTAMP"
|
|
||||||
},
|
|
||||||
"builders":[
|
|
||||||
{
|
|
||||||
"type": "qemu",
|
|
||||||
"boot_command": [
|
|
||||||
"<tab> text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/{{user `ks_path`}}<enter><wait>"
|
|
||||||
],
|
|
||||||
"boot_wait": "10s",
|
|
||||||
"disk_size": "{{user `disk_size`}}",
|
|
||||||
"headless": "{{ user `headless` }}",
|
|
||||||
"http_directory": "http",
|
|
||||||
"iso_checksum": "{{user `iso_checksum`}}",
|
|
||||||
"iso_checksum_type": "{{user `iso_checksum_type`}}",
|
|
||||||
"iso_url": "{{user `mirror`}}/{{user `mirror_directory`}}/{{user `iso_name`}}",
|
|
||||||
"output_directory": "packer-{{user `template`}}-qemu",
|
|
||||||
"shutdown_command": "echo 'vagrant'|sudo -S /sbin/halt -h -p",
|
|
||||||
"ssh_password": "vagrant",
|
|
||||||
"ssh_port": 22,
|
|
||||||
"ssh_username": "root",
|
|
||||||
"ssh_timeout": "10000s",
|
|
||||||
"vm_name": "{{ user `template` }}.raw",
|
|
||||||
"net_device": "virtio-net",
|
|
||||||
"disk_interface": "virtio",
|
|
||||||
"format": "raw",
|
|
||||||
"use_default_display": "false",
|
|
||||||
"qemuargs": [
|
|
||||||
["-display", "cocoa"]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"post-processors":[
|
|
||||||
{
|
|
||||||
"type":"ucloud-import",
|
|
||||||
"ufile_bucket_name": "packer",
|
|
||||||
"image_name": "packer_import",
|
|
||||||
"image_os_type": "CentOS",
|
|
||||||
"image_os_name": "CentOS 7.0 64位",
|
|
||||||
"format": "raw",
|
|
||||||
"region":"cn-bj2",
|
|
||||||
"project_id": "org-xp2ucn"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -24,6 +24,13 @@ const (
|
||||||
VHDFileFormat = "vhd"
|
VHDFileFormat = "vhd"
|
||||||
VMDKFileFormat = "vmdk"
|
VMDKFileFormat = "vmdk"
|
||||||
QCOW2FileFormat = "qcow2"
|
QCOW2FileFormat = "qcow2"
|
||||||
|
|
||||||
|
CentOSOsType = "CentOS"
|
||||||
|
UbuntuOsType = "Ubuntu"
|
||||||
|
WindowsOsType = "Windows"
|
||||||
|
RedHatOsType = "RedHat"
|
||||||
|
DebianOsType = "Debian"
|
||||||
|
OtherOsType = "Other"
|
||||||
)
|
)
|
||||||
|
|
||||||
var regionForFileMap = ucloudcommon.NewStringConverter(map[string]string{
|
var regionForFileMap = ucloudcommon.NewStringConverter(map[string]string{
|
||||||
|
@ -43,14 +50,15 @@ type Config struct {
|
||||||
ucloudcommon.AccessConfig `mapstructure:",squash"`
|
ucloudcommon.AccessConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
// Variables specific to this post processor
|
// Variables specific to this post processor
|
||||||
UFileBucket string `mapstructure:"ufile_bucket_name"`
|
UFileBucket string `mapstructure:"ufile_bucket_name"`
|
||||||
UFileKey string `mapstructure:"ufile_key_name"`
|
UFileKey string `mapstructure:"ufile_key_name"`
|
||||||
SkipClean bool `mapstructure:"skip_clean"`
|
SkipClean bool `mapstructure:"skip_clean"`
|
||||||
ImageName string `mapstructure:"image_name"`
|
ImageName string `mapstructure:"image_name"`
|
||||||
ImageDescription string `mapstructure:"image_description"`
|
ImageDescription string `mapstructure:"image_description"`
|
||||||
OSType string `mapstructure:"image_os_type"`
|
OSType string `mapstructure:"image_os_type"`
|
||||||
OSName string `mapstructure:"image_os_name"`
|
OSName string `mapstructure:"image_os_name"`
|
||||||
Format string `mapstructure:"format"`
|
Format string `mapstructure:"format"`
|
||||||
|
WaitImageReadyTimeout int `mapstructure:"wait_image_ready_timeout"`
|
||||||
|
|
||||||
ctx interpolate.Context
|
ctx interpolate.Context
|
||||||
}
|
}
|
||||||
|
@ -76,10 +84,13 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||||
|
|
||||||
// Set defaults
|
// Set defaults
|
||||||
if p.config.UFileKey == "" {
|
if p.config.UFileKey == "" {
|
||||||
//fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID()[:8])
|
|
||||||
p.config.UFileKey = "packer-import-{{timestamp}}." + p.config.Format
|
p.config.UFileKey = "packer-import-{{timestamp}}." + p.config.Format
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.config.WaitImageReadyTimeout <= 0 {
|
||||||
|
p.config.WaitImageReadyTimeout = ucloudcommon.DefaultCreateImageTimeOut
|
||||||
|
}
|
||||||
|
|
||||||
errs := new(packer.MultiError)
|
errs := new(packer.MultiError)
|
||||||
|
|
||||||
// Check and render ufile_key_name
|
// Check and render ufile_key_name
|
||||||
|
@ -95,8 +106,9 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||||
templates := map[string]*string{
|
templates := map[string]*string{
|
||||||
"ufile_bucket_name": &p.config.UFileBucket,
|
"ufile_bucket_name": &p.config.UFileBucket,
|
||||||
"image_name": &p.config.ImageName,
|
"image_name": &p.config.ImageName,
|
||||||
"image_os_type": &p.config.OSName,
|
"image_os_type": &p.config.OSType,
|
||||||
"image_os_name": &p.config.OSType,
|
"image_os_name": &p.config.OSName,
|
||||||
|
"format": &p.config.Format,
|
||||||
}
|
}
|
||||||
// Check out required params are defined
|
// Check out required params are defined
|
||||||
for key, ptr := range templates {
|
for key, ptr := range templates {
|
||||||
|
@ -106,11 +118,23 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
imageName := p.config.ImageName
|
||||||
|
if !ucloudcommon.ImageNamePattern.MatchString(imageName) {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("expected %q to be 1-63 characters and only support chinese, english, numbers, '-_,.:[]', got %q", "image_name", imageName))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch p.config.OSType {
|
||||||
|
case CentOSOsType, UbuntuOsType, WindowsOsType, RedHatOsType, DebianOsType, OtherOsType:
|
||||||
|
default:
|
||||||
|
errs = packer.MultiErrorAppend(
|
||||||
|
errs, fmt.Errorf("expected %q only be one of 'CentOS', 'Ubuntu', 'Windows', 'RedHat', 'Debian' or 'Other', got %q", "image_os_type", p.config.OSType))
|
||||||
|
}
|
||||||
|
|
||||||
switch p.config.Format {
|
switch p.config.Format {
|
||||||
case VHDFileFormat, RAWFileFormat, VMDKFileFormat, QCOW2FileFormat:
|
case VHDFileFormat, RAWFileFormat, VMDKFileFormat, QCOW2FileFormat:
|
||||||
default:
|
default:
|
||||||
errs = packer.MultiErrorAppend(
|
errs = packer.MultiErrorAppend(
|
||||||
errs, fmt.Errorf("invalid format '%s'. Only 'raw', 'vhd', 'vmdk', or 'qcow2' are allowed", p.config.Format))
|
errs, fmt.Errorf("expected %q only be one of 'raw', 'vhd', 'vmdk', or 'qcow2', got %q", "format", p.config.Format))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anything which flagged return back up the stack
|
// Anything which flagged return back up the stack
|
||||||
|
@ -128,7 +152,7 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact
|
||||||
|
|
||||||
client, err := p.config.Client()
|
client, err := p.config.Client()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, false, err
|
return nil, false, false, fmt.Errorf("Failed to connect ucloud client %s", err)
|
||||||
}
|
}
|
||||||
uhostconn := client.UHostConn
|
uhostconn := client.UHostConn
|
||||||
ufileconn := client.UFileConn
|
ufileconn := client.UFileConn
|
||||||
|
@ -139,9 +163,9 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact
|
||||||
return nil, false, false, fmt.Errorf("Error rendering ufile_key_name template: %s", err)
|
return nil, false, false, fmt.Errorf("Error rendering ufile_key_name template: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Rendered ufile_key_name as %s", p.config.UFileKey)
|
ui.Message(fmt.Sprintf("Rendered ufile_key_name as %s", p.config.UFileKey))
|
||||||
|
|
||||||
log.Println("Looking for image in artifact")
|
ui.Message("Looking for image in artifact")
|
||||||
// Locate the files output from the builder
|
// Locate the files output from the builder
|
||||||
var source string
|
var source string
|
||||||
for _, path := range artifact.Files() {
|
for _, path := range artifact.Files() {
|
||||||
|
@ -156,8 +180,7 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact
|
||||||
return nil, false, false, fmt.Errorf("No %s image file found in artifact from builder", p.config.Format)
|
return nil, false, false, fmt.Errorf("No %s image file found in artifact from builder", p.config.Format)
|
||||||
}
|
}
|
||||||
|
|
||||||
region := regionForFileMap.Convert(p.config.Region)
|
convertedRegion := regionForFileMap.Convert(p.config.Region)
|
||||||
projectId := p.config.ProjectId
|
|
||||||
keyName := p.config.UFileKey
|
keyName := p.config.UFileKey
|
||||||
bucketName := p.config.UFileBucket
|
bucketName := p.config.UFileBucket
|
||||||
|
|
||||||
|
@ -165,43 +188,42 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact
|
||||||
PublicKey: p.config.PublicKey,
|
PublicKey: p.config.PublicKey,
|
||||||
PrivateKey: p.config.PrivateKey,
|
PrivateKey: p.config.PrivateKey,
|
||||||
BucketName: bucketName,
|
BucketName: bucketName,
|
||||||
FileHost: fmt.Sprintf(region + ".ufileos.com"),
|
FileHost: fmt.Sprintf(convertedRegion + ".ufileos.com"),
|
||||||
BucketHost: "api.ucloud.cn",
|
BucketHost: "api.ucloud.cn",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// query or create bucket
|
||||||
|
if err := queryOrCreateBucket(ufileconn, config); err != nil {
|
||||||
|
return nil, false, false, fmt.Errorf("Failed to query or create bucket, %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bucketUrl := fmt.Sprintf("http://" + bucketName + "." + convertedRegion + ".ufileos.com")
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Waiting for uploading image file %s to UFile: %s/%s...", source, bucketUrl, p.config.UFileKey))
|
||||||
|
|
||||||
|
// upload file to bucket
|
||||||
|
ufileUrl, err := uploadFile(ufileconn, config, keyName, source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, false, fmt.Errorf("Load config error %s", err)
|
return nil, false, false, fmt.Errorf("Failed to Upload image file, %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := queryOrCreateBucket(ufileconn, config, region, projectId); err != nil {
|
ui.Say(fmt.Sprintf("Image file %s has been uploaded to UFile %s", source, ufileUrl))
|
||||||
return nil, false, false, fmt.Errorf("Query or create bucket error %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bucketUrl := fmt.Sprintf("http://" + bucketName + "." + region + ".ufileos.com")
|
importImageRequest := p.buildImportImageRequest(uhostconn, ufileUrl)
|
||||||
ui.Say(fmt.Sprintf("Waiting for uploading file %s to %s/%s...", source, bucketUrl, p.config.UFileKey))
|
|
||||||
|
|
||||||
privateUrl, err := uploadFile(config, keyName, source, projectId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, false, fmt.Errorf("Upload file error %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Image file %s has been uploaded to UFile %s", source, privateUrl))
|
|
||||||
|
|
||||||
importImageRequest := p.buildImportImageRequest(uhostconn, privateUrl)
|
|
||||||
importImageResponse, err := uhostconn.ImportCustomImage(importImageRequest)
|
importImageResponse, err := uhostconn.ImportCustomImage(importImageRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, false, fmt.Errorf("Failed to import from %s/%s: %s", bucketUrl, p.config.UFileKey, err)
|
return nil, false, false, fmt.Errorf("Failed to import image from %s/%s, %s", bucketUrl, p.config.UFileKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
imageId := importImageResponse.ImageId
|
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Waiting for importing %s/%s to ucloud...", bucketUrl, p.config.UFileKey))
|
ui.Say(fmt.Sprintf("Waiting for importing %s/%s to ucloud...", bucketUrl, p.config.UFileKey))
|
||||||
|
|
||||||
|
imageId := importImageResponse.ImageId
|
||||||
err = retry.Config{
|
err = retry.Config{
|
||||||
Tries: 30,
|
StartTimeout: time.Duration(p.config.WaitImageReadyTimeout) * time.Second,
|
||||||
ShouldRetry: func(err error) bool {
|
ShouldRetry: func(err error) bool {
|
||||||
return ucloudcommon.IsExpectedStateError(err)
|
return ucloudcommon.IsExpectedStateError(err)
|
||||||
},
|
},
|
||||||
RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 6 * time.Second, Multiplier: 2}).Linear,
|
RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 12 * time.Second, Multiplier: 2}).Linear,
|
||||||
}.Run(ctx, func(ctx context.Context) error {
|
}.Run(ctx, func(ctx context.Context) error {
|
||||||
image, err := client.DescribeImageById(imageId)
|
image, err := client.DescribeImageById(imageId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -209,7 +231,7 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact
|
||||||
}
|
}
|
||||||
|
|
||||||
if image.State == ucloudcommon.ImageStateUnavailable {
|
if image.State == ucloudcommon.ImageStateUnavailable {
|
||||||
return fmt.Errorf("Unavailable importing image %s", imageId)
|
return fmt.Errorf("Unavailable importing image %q", imageId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if image.State != ucloudcommon.ImageStateAvailable {
|
if image.State != ucloudcommon.ImageStateAvailable {
|
||||||
|
@ -225,7 +247,7 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the reported UCloud image ID to the artifact list
|
// Add the reported UCloud image ID to the artifact list
|
||||||
ui.Say(fmt.Sprintf("Importing created ucloud image ID %s in region %s Finished.", imageId, p.config.Region))
|
ui.Say(fmt.Sprintf("Importing created ucloud image %q in region %q Complete.", imageId, p.config.Region))
|
||||||
images := []ucloudcommon.ImageInfo{
|
images := []ucloudcommon.ImageInfo{
|
||||||
{
|
{
|
||||||
ImageId: imageId,
|
ImageId: imageId,
|
||||||
|
@ -241,9 +263,9 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact
|
||||||
}
|
}
|
||||||
|
|
||||||
if !p.config.SkipClean {
|
if !p.config.SkipClean {
|
||||||
ui.Message(fmt.Sprintf("Deleting import source %s/%s/%s", bucketUrl, p.config.UFileBucket, p.config.UFileKey))
|
ui.Message(fmt.Sprintf("Deleting import source UFile: %s/%s", p.config.UFileBucket, p.config.UFileKey))
|
||||||
if err = deleteFile(config, p.config.UFileKey); err != nil {
|
if err = deleteFile(config, p.config.UFileKey); err != nil {
|
||||||
return nil, false, false, fmt.Errorf("Failed to delete %s/%s/%s: %s", bucketUrl, p.config.UFileBucket, p.config.UFileKey, err)
|
return nil, false, false, fmt.Errorf("Failed to delete UFile: %s/%s, %s", p.config.UFileBucket, p.config.UFileKey, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,7 +284,7 @@ func (p *PostProcessor) buildImportImageRequest(conn *uhost.UHostClient, private
|
||||||
return req
|
return req
|
||||||
}
|
}
|
||||||
|
|
||||||
func queryOrCreateBucket(conn *ufile.UFileClient, config *ufsdk.Config, region, projectId string) error {
|
func queryOrCreateBucket(conn *ufile.UFileClient, config *ufsdk.Config) error {
|
||||||
var limit = 100
|
var limit = 100
|
||||||
var offset int
|
var offset int
|
||||||
var bucketList []ufile.UFileBucketSet
|
var bucketList []ufile.UFileBucketSet
|
||||||
|
@ -272,7 +294,7 @@ func queryOrCreateBucket(conn *ufile.UFileClient, config *ufsdk.Config, region,
|
||||||
req.Offset = ucloud.Int(offset)
|
req.Offset = ucloud.Int(offset)
|
||||||
resp, err := conn.DescribeBucket(req)
|
resp, err := conn.DescribeBucket(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error on reading bucket list, %s", err)
|
return fmt.Errorf("error on reading bucket list when create bucket, %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp == nil || len(resp.DataSet) < 1 {
|
if resp == nil || len(resp.DataSet) < 1 {
|
||||||
|
@ -307,32 +329,30 @@ func queryOrCreateBucket(conn *ufile.UFileClient, config *ufsdk.Config, region,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func uploadFile(config *ufsdk.Config, keyName, filePath, projectId string) (string, error) {
|
func uploadFile(conn *ufile.UFileClient, config *ufsdk.Config, keyName, source string) (string, error) {
|
||||||
reqFile, err := ufsdk.NewFileRequest(config, nil)
|
reqFile, err := ufsdk.NewFileRequest(config, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("NewFileErr:%s", err)
|
return "", fmt.Errorf("error on building upload file request, %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = reqFile.AsyncMPut(filePath, keyName, "")
|
// upload file in segments
|
||||||
|
err = reqFile.AsyncMPut(source, keyName, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("AsyncMPutErr:%s, Response:%s", err, reqFile.DumpResponse(true))
|
return "", fmt.Errorf("error on upload file, %s, details: %s", err, reqFile.DumpResponse(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
reqBucket, err := ufsdk.NewBucketRequest(config, nil)
|
reqBucket := conn.NewDescribeBucketRequest()
|
||||||
|
reqBucket.BucketName = ucloud.String(config.BucketName)
|
||||||
|
resp, err := conn.DescribeBucket(reqBucket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", fmt.Errorf("error on reading bucket list when upload file, %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bucketList, err := reqBucket.DescribeBucket(config.BucketName, 0, 1, projectId)
|
if resp.DataSet[0].Type == "private" {
|
||||||
if err != nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if bucketList.DataSet[0].Type == "private" {
|
|
||||||
return reqFile.GetPrivateURL(keyName, 24*60*60), nil
|
return reqFile.GetPrivateURL(keyName, 24*60*60), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return reqBucket.GetPublicURL(keyName), nil
|
return reqFile.GetPublicURL(keyName), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteFile(config *ufsdk.Config, keyName string) error {
|
func deleteFile(config *ufsdk.Config, keyName string) error {
|
||||||
|
|
|
@ -83,6 +83,8 @@ builder.
|
||||||
- `name` (string) - The copied image name. If not defined, builder will use `image_name` as default name.
|
- `name` (string) - The copied image name. If not defined, builder will use `image_name` as default name.
|
||||||
|
|
||||||
- `description` (number) - The copied image description.
|
- `description` (number) - The copied image description.
|
||||||
|
|
||||||
|
- `wait_image_ready_timeout` (int)Timeout of creating image or copying image. The default timeout is 3600 seconds if this option is not set or is set to 0.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
---
|
||||||
|
description: |
|
||||||
|
The Packer UCloud Import post-processor takes the RAW, VHD, VMDK, or qcow2 artifact from various builders and imports it to UCloud customized image list for UHost Instance.
|
||||||
|
layout: docs
|
||||||
|
page_title: 'UCloud Import Post-Processors'
|
||||||
|
sidebar_current: 'docs-post-processors-ucloud-import'
|
||||||
|
---
|
||||||
|
|
||||||
|
# UCloud Import Post-Processor
|
||||||
|
|
||||||
|
Type: `ucloud-import`
|
||||||
|
|
||||||
|
The Packer UCloud Import post-processor takes the RAW, VHD, VMDK, or qcow2 artifact from various builders and imports it to UCloud customized image list for UHost Instance.
|
||||||
|
|
||||||
|
## How Does it Work?
|
||||||
|
|
||||||
|
The import process operates by making a temporary copy of the RAW, VHD, VMDK, or qcow2 to an UFile bucket, and calling an import task in UHost on the RAW, VHD, VMDK, or qcow2 file. Once completed, an UCloud UHost Image is returned. The temporary RAW, VHD, VMDK, or qcow2 copy in UFile can be discarded after the import is complete.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
There are some configuration options available for the post-processor. There
|
||||||
|
are two categories: required and optional parameters.
|
||||||
|
|
||||||
|
### Required:
|
||||||
|
|
||||||
|
- `public_key` - (string) This is the UCloud public key. It must be provided, but it can also be sourced from the `UCLOUD_PUBLIC_KEY` environment variable.
|
||||||
|
|
||||||
|
- `private_key` - (string) This is the UCloud private key. It must be provided, but it can also be sourced from the `UCLOUD_PRIVATE_KEY` environment variable.
|
||||||
|
|
||||||
|
- `project_id` - (string) This is the UCloud project id. It must be provided, but it can also be sourced from the `UCLOUD_PROJECT_ID` environment variables.
|
||||||
|
|
||||||
|
- `region` - (string) This is the UCloud region. It must be provided, but it can also be sourced from the `UCLOUD_REGION` environment variables.
|
||||||
|
|
||||||
|
- `image_name` - (string) The name of the user-defined image, which contains 1-63 characters and only support Chinese, English, numbers, '-_,.:[]'.
|
||||||
|
|
||||||
|
- `ufile_bucket_name` (string) - The name of the ufile bucket where the RAW, VHD, VMDK, or qcow2 file will be copied to for import. If the Bucket isn't exist, post-process will create it for you.
|
||||||
|
|
||||||
|
- `image_os_type` (string) - Type of the OS. Possible values are: `CentOS`, `Ubuntu`, `Windows`, `RedHat`, `Debian`, `Other`.
|
||||||
|
|
||||||
|
- `image_os_name` (string) - The name of OS. Such as: `CentOS 7.2 64位`, set `Other` When `image_os_type` is `Other`.
|
||||||
|
|
||||||
|
- `format` (string) - The format of the import image , Possible values are: `RAW`, `VHD`, `VMDK`, or `qcow2`.
|
||||||
|
|
||||||
|
### Optional:
|
||||||
|
|
||||||
|
|
||||||
|
- `ufile_key_name` (string) - The name of the object key in `ufile_bucket_name` where the RAW, VHD, VMDK, or qcow2 file will be copied to for import.
|
||||||
|
|
||||||
|
- `skip_clean` (boolean) - Whether we should skip removing the RAW or VHD file uploaded to UFile after the import process has completed. Possible values are: `true` as leave it in the UFile bucket, `false` as clean it out.(Default: `false`).
|
||||||
|
|
||||||
|
- `image_description` (string) - The description of the image.
|
||||||
|
|
||||||
|
- `wait_image_ready_timeout`(number) - Timeout of importing image. The default timeout is 3600 seconds if this option is not set or is set
|
||||||
|
to 0.
|
||||||
|
|
||||||
|
## Basic Example
|
||||||
|
|
||||||
|
Here is a basic example. This assumes that the builder has produced a RAW artifact for us to work with. This will take the RAW image generated by a builder and upload it to UFile. Once uploaded, the import process will start, creating an UCloud UHost image to the region `cn-bj2`.
|
||||||
|
|
||||||
|
``` json
|
||||||
|
"post-processors":[
|
||||||
|
{
|
||||||
|
"type":"ucloud-import",
|
||||||
|
"public_key": "{{user `ucloud_public_key`}}",
|
||||||
|
"private_key": "{{user `ucloud_private_key`}}",
|
||||||
|
"project_id": "{{user `ucloud_project_id`}}",
|
||||||
|
"region":"cn-bj2",
|
||||||
|
"ufile_bucket_name": "packer-import",
|
||||||
|
"image_name": "packer_import",
|
||||||
|
"image_os_type": "CentOS",
|
||||||
|
"image_os_name": "CentOS 6.10 64位",
|
||||||
|
"format": "raw"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
Loading…
Reference in New Issue