From 788d149f36f074a05b45169bcdac8c78d9091e9a Mon Sep 17 00:00:00 2001 From: Wolfgang Huse Date: Sat, 24 Sep 2022 15:28:42 +0200 Subject: [PATCH] implement source image uri --- builder/nutanix/builder.go | 3 - builder/nutanix/config.go | 2 +- builder/nutanix/config.hcl2spec.go | 2 + builder/nutanix/driver.go | 104 ++++++++++++++++++++++---- builder/nutanix/step_build_vm.go | 2 +- builder/nutanix/step_prepare_image.go | 56 -------------- 6 files changed, 93 insertions(+), 76 deletions(-) delete mode 100644 builder/nutanix/step_prepare_image.go diff --git a/builder/nutanix/builder.go b/builder/nutanix/builder.go index 5d716d3..144e0c6 100644 --- a/builder/nutanix/builder.go +++ b/builder/nutanix/builder.go @@ -53,9 +53,6 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) state.Put("ui", ui) steps := []multistep.Step{ - &stepPrepareImage{ - Config: &b.config, - }, &commonsteps.StepCreateCD{ Files: b.config.CDConfig.CDFiles, Content: b.config.CDConfig.CDContent, diff --git a/builder/nutanix/config.go b/builder/nutanix/config.go index d57af9a..13a835f 100644 --- a/builder/nutanix/config.go +++ b/builder/nutanix/config.go @@ -52,7 +52,7 @@ type VmDisk struct { ImageType string `mapstructure:"image_type" json:"image_type" required:"false"` SourceImageName string `mapstructure:"source_image_name" json:"source_image_name" required:"false"` SourceImageUUID string `mapstructure:"source_image_uuid" json:"source_image_uuid" required:"false"` - SourceImageURL string `mapstructure:"source_image_url" json:"source_image_url" required:"false"` + SourceImageURI string `mapstructure:"source_image_uri" json:"source_image_uri" required:"false"` DiskSizeGB int64 `mapstructure:"disk_size_gb" json:"disk_size_gb" required:"false"` } diff --git a/builder/nutanix/config.hcl2spec.go b/builder/nutanix/config.hcl2spec.go index e73b29e..c60ad22 100644 --- a/builder/nutanix/config.hcl2spec.go +++ b/builder/nutanix/config.hcl2spec.go @@ -266,6 +266,7 @@ type FlatVmDisk struct { ImageType *string `mapstructure:"image_type" json:"image_type" required:"false" cty:"image_type" hcl:"image_type"` SourceImageName *string `mapstructure:"source_image_name" json:"source_image_name" required:"false" cty:"source_image_name" hcl:"source_image_name"` SourceImageUUID *string `mapstructure:"source_image_uuid" json:"source_image_uuid" required:"false" cty:"source_image_uuid" hcl:"source_image_uuid"` + SourceImageURI *string `mapstructure:"source_image_uri" json:"source_image_uri" required:"false" cty:"source_image_uri" hcl:"source_image_uri"` DiskSizeGB *int64 `mapstructure:"disk_size_gb" json:"disk_size_gb" required:"false" cty:"disk_size_gb" hcl:"disk_size_gb"` } @@ -284,6 +285,7 @@ func (*FlatVmDisk) HCL2Spec() map[string]hcldec.Spec { "image_type": &hcldec.AttrSpec{Name: "image_type", Type: cty.String, Required: false}, "source_image_name": &hcldec.AttrSpec{Name: "source_image_name", Type: cty.String, Required: false}, "source_image_uuid": &hcldec.AttrSpec{Name: "source_image_uuid", Type: cty.String, Required: false}, + "source_image_uri": &hcldec.AttrSpec{Name: "source_image_uri", Type: cty.String, Required: false}, "disk_size_gb": &hcldec.AttrSpec{Name: "disk_size_gb", Type: cty.Number, Required: false}, } return s diff --git a/builder/nutanix/driver.go b/builder/nutanix/driver.go index f2e4cb5..5d827ee 100644 --- a/builder/nutanix/driver.go +++ b/builder/nutanix/driver.go @@ -22,8 +22,9 @@ type Driver interface { //GetImage(string) (*nutanixImage, error) GetHost(string) (*nutanixHost, error) PowerOff(string) error - UploadImage(string, VmConfig) (*nutanixImage, error) + UploadImage(string, string, string, VmConfig) (*nutanixImage, error) DeleteImage(string) error + GetImage(string) (*nutanixImage, error) SaveVMDisk(string, string, bool) (*nutanixImage, error) WaitForShutdown(string, <-chan struct{}) bool } @@ -106,6 +107,31 @@ func findSubnetByName(conn *v3.Client, name string) (*v3.SubnetIntentResponse, e return found[0], nil } +func sourceImageExists(conn *v3.Client, name string, uri string) (*v3.ImageIntentResponse, error) { + filter := fmt.Sprintf("name==%s", name) + resp, err := conn.V3.ListAllImage(filter) + if err != nil { + return nil, err + } + + entities := resp.Entities + + found := make([]*v3.ImageIntentResponse, 0) + for _, v := range entities { + if (*v.Spec.Name == name) && (*v.Status.Resources.SourceURI == uri) { + found = append(found, v) + } + } + + if len(found) > 1 { + return nil, fmt.Errorf("your query returned more than one result with same Name/URI") + } + + if len(found) == 0 { + return nil, nil + } + return found[0], nil +} func findImageByUUID(conn *v3.Client, uuid string) (*v3.ImageIntentResponse, error) { return conn.V3.GetImage(uuid) @@ -210,6 +236,13 @@ func (d *NutanixDriver) CreateRequest(vm VmConfig) (*v3.VMIntentInput, error) { for _, disk := range vm.VmDisks { if disk.ImageType == "DISK_IMAGE" { image := &v3.ImageIntentResponse{} + if disk.SourceImageURI !="" { + image, err := d.UploadImage(disk.SourceImageURI, "URI", disk.ImageType,vm) + if err != nil { + return nil, fmt.Errorf("error while findImageByUUID, Error %s", err.Error()) + } + disk.SourceImageUUID= *image.image.Metadata.UUID + } if disk.SourceImageUUID != "" { image, err = findImageByUUID(conn, disk.SourceImageUUID) if err != nil { @@ -260,6 +293,13 @@ func (d *NutanixDriver) CreateRequest(vm VmConfig) (*v3.VMIntentInput, error) { } if disk.ImageType == "ISO_IMAGE" { image := &v3.ImageIntentResponse{} + if disk.SourceImageURI !="" { + image, err := d.UploadImage(disk.SourceImageURI, "URI", disk.ImageType,vm) + if err != nil { + return nil, fmt.Errorf("error while findImageByUUID, Error %s", err.Error()) + } + disk.SourceImageUUID= *image.image.Metadata.UUID + } if disk.SourceImageUUID != "" { image, err = findImageByUUID(conn, disk.SourceImageUUID) if err != nil { @@ -448,7 +488,7 @@ func (d *NutanixDriver) Delete(vmUUID string) error { } //UploadImage (string, VmConfig) (*nutanixImage, error) -func (d *NutanixDriver) UploadImage(imagePath string, vm VmConfig) (*nutanixImage, error) { +func (d *NutanixDriver) UploadImage(imagePath string, sourceType string, imageType string, vm VmConfig) (*nutanixImage, error) { configCreds := client.Credentials{ URL: fmt.Sprintf("%s:%d", d.ClusterConfig.Endpoint, d.ClusterConfig.Port), Endpoint: d.ClusterConfig.Endpoint, @@ -484,7 +524,7 @@ func (d *NutanixDriver) UploadImage(imagePath string, vm VmConfig) (*nutanixImag Spec: &v3.Image{ Name: &file, Resources: &v3.ImageResources{ - ImageType: StringPtr("ISO_IMAGE"), + ImageType: &imageType, InitialPlacementRefList: InitialPlacementRef, }, }, @@ -492,7 +532,16 @@ func (d *NutanixDriver) UploadImage(imagePath string, vm VmConfig) (*nutanixImag Kind: StringPtr("image"), }, } - + if sourceType=="URI" { + image, err:=sourceImageExists(conn, file, imagePath) + if err != nil { + return nil, fmt.Errorf("error while check if Image exists, %s", err.Error()) + } + if image !=nil { + return &nutanixImage{image: *image}, nil + } + req.Spec.Resources.SourceURI=&imagePath + } image, err := conn.V3.CreateImage(req) if err != nil { return nil, fmt.Errorf("error while Image Create, %s", err.Error()) @@ -510,24 +559,27 @@ func (d *NutanixDriver) UploadImage(imagePath string, vm VmConfig) (*nutanixImag time.Sleep(5 * time.Second) } - err = conn.V3.UploadImage(*image.Metadata.UUID, imagePath) - if err != nil { - return nil, fmt.Errorf("error while upload, %s", err.Error()) - } - for { - running, err := conn.V3.GetImage(*image.Metadata.UUID) + if sourceType=="PATH" { + err = conn.V3.UploadImage(*image.Metadata.UUID, imagePath) if err != nil { - return nil, fmt.Errorf("error while upload status, %s", err.Error()) + return nil, fmt.Errorf("error while upload, %s", err.Error()) } - if *running.Status.State == "COMPLETE" { - break - } - time.Sleep(5 * time.Second) + for { + running, err := conn.V3.GetImage(*image.Metadata.UUID) + if err != nil { + return nil, fmt.Errorf("error while upload status, %s", err.Error()) + } + if *running.Status.State == "COMPLETE" { + break + } + time.Sleep(5 * time.Second) + } } return &nutanixImage{image: *image}, nil } + func (d *NutanixDriver) DeleteImage(imageUUID string) error { configCreds := client.Credentials{ URL: fmt.Sprintf("%s:%d", d.ClusterConfig.Endpoint, d.ClusterConfig.Port), @@ -549,6 +601,28 @@ func (d *NutanixDriver) DeleteImage(imageUUID string) error { return nil } +func (d *NutanixDriver) GetImage(imagename string) (*nutanixImage, error) { + configCreds := client.Credentials{ + URL: fmt.Sprintf("%s:%d", d.ClusterConfig.Endpoint, d.ClusterConfig.Port), + Endpoint: d.ClusterConfig.Endpoint, + Username: d.ClusterConfig.Username, + Password: d.ClusterConfig.Password, + Port: string(d.ClusterConfig.Port), + Insecure: d.ClusterConfig.Insecure, + } + + conn, err := v3.NewV3Client(configCreds) + if err != nil { + return nil, fmt.Errorf("error while NewV3Client, %s", err.Error()) + } + + image, err := findImageByName(conn,imagename) + if err != nil { + return nil, fmt.Errorf("error while GetImage, %s", err.Error()) + } + return &nutanixImage{image: *image}, nil +} + func (d *NutanixDriver) GetVM(vmUUID string) (*nutanixInstance, error) { configCreds := client.Credentials{ diff --git a/builder/nutanix/step_build_vm.go b/builder/nutanix/step_build_vm.go index ed4c56b..d6cdd69 100644 --- a/builder/nutanix/step_build_vm.go +++ b/builder/nutanix/step_build_vm.go @@ -26,7 +26,7 @@ func (s *stepBuildVM) Run(ctx context.Context, state multistep.StateBag) multist if cdPathRaw, ok := state.GetOk("cd_path"); ok { cdFilesPath := cdPathRaw.(string) log.Println("temporary iso found, " + cdFilesPath) - cdfilesImage, err := d.UploadImage(cdFilesPath, config.VmConfig) + cdfilesImage, err := d.UploadImage(cdFilesPath, "PATH", "ISO_IMAGE",config.VmConfig) if err != nil { ui.Error("Error uploading temporary image:") ui.Error(err.Error()) diff --git a/builder/nutanix/step_prepare_image.go b/builder/nutanix/step_prepare_image.go deleted file mode 100644 index 61a7801..0000000 --- a/builder/nutanix/step_prepare_image.go +++ /dev/null @@ -1,56 +0,0 @@ -package nutanix - -import ( - "context" - "errors" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer-plugin-sdk/packer" -) - -type stepPrepareImage struct { - Config *Config -} - -// Will check if image with this name already exists and fetch from URL if necessary -func (s *stepPrepareImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packer.Ui) - vmUUID := state.Get("vmUUID").(string) - ui.Say("Retrieving Status for uuid: " + vmUUID) - d := state.Get("driver").(Driver) - - ui.Say("Check if Source Image exists") - image, err := d.getImage d.GetVM(vmUUID) - - ui.Message("Initiatiating save VM DISK task.") - // Choose disk to replicate - looking for first "DISK" - var diskToCopy string - - for i := range vm.nutanix.Spec.Resources.DiskList { - if *vm.nutanix.Spec.Resources.DiskList[i].DeviceProperties.DeviceType == "DISK" { - diskToCopy = *vm.nutanix.Spec.Resources.DiskList[i].UUID - ui.Message("Found DISK to copy: " + diskToCopy) - break - } - } - - if diskToCopy == "" { - err := errors.New("no DISK was found to save, halting build") - ui.Error(err.Error()) - state.Put("error", err) - return multistep.ActionHalt - } - - imageResponse, err := d.SaveVMDisk(diskToCopy, s.Config.VmConfig.ImageName) - if err != nil { - ui.Error("Unexpected Nutanix Task status: " + err.Error()) - state.Put("error", err) - return multistep.ActionHalt - } - ui.Message("Successfully created image: " + *imageResponse.image.Metadata.UUID) - state.Put("vm_disk_uuid", (*imageResponse.image.Metadata.UUID)) - return multistep.ActionContinue -} - -func (s *stepPrepareImage) Cleanup(state multistep.StateBag) { -}