Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement delete and force source_image option #137

Merged
merged 1 commit into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions builder/nutanix/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,13 @@ type ClusterConfig struct {
}

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"`
SourceImageURI string `mapstructure:"source_image_uri" json:"source_image_uri" required:"false"`
DiskSizeGB int64 `mapstructure:"disk_size_gb" json:"disk_size_gb" required:"false"`
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"`
SourceImageURI string `mapstructure:"source_image_uri" json:"source_image_uri" required:"false"`
SourceImageDelete bool `mapstructure:"source_image_delete" json:"source_image_delete" required:"false"`
SourceImageForce bool `mapstructure:"source_image_force" json:"source_image_force" required:"false"`
DiskSizeGB int64 `mapstructure:"disk_size_gb" json:"disk_size_gb" required:"false"`
}

type VmNIC struct {
Expand Down
24 changes: 14 additions & 10 deletions builder/nutanix/config.hcl2spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

135 changes: 110 additions & 25 deletions builder/nutanix/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"log"
"path"

"github.com/hashicorp/packer-plugin-sdk/multistep"
client "github.com/nutanix-cloud-native/prism-go-client/pkg/nutanix"
v3 "github.com/nutanix-cloud-native/prism-go-client/pkg/nutanix/v3"
)
Expand All @@ -24,13 +25,14 @@ const (
// A driver is able to talk to Nutanix PrismCentral and perform certain
// operations with it.
type Driver interface {
CreateRequest(VmConfig) (*v3.VMIntentInput, error)
CreateRequest(VmConfig, multistep.StateBag) (*v3.VMIntentInput, error)
Create(*v3.VMIntentInput) (*nutanixInstance, error)
Delete(string) error
GetVM(string) (*nutanixInstance, error)
GetHost(string) (*nutanixHost, error)
PowerOff(string) error
UploadImage(string, string, string, VmConfig) (*nutanixImage, error)
CreateImageURL(VmDisk, VmConfig) (*nutanixImage, error)
CreateImageFile(string, VmConfig) (*nutanixImage, error)
DeleteImage(string) error
GetImage(string) (*nutanixImage, error)
ExportImage(string) (io.ReadCloser, error)
Expand Down Expand Up @@ -257,7 +259,8 @@ func (d *NutanixDriver) WaitForShutdown(vmUUID string, cancelCh <-chan struct{})
return false
}
}
func (d *NutanixDriver) CreateRequest(vm VmConfig) (*v3.VMIntentInput, error) {

func (d *NutanixDriver) CreateRequest(vm VmConfig, state multistep.StateBag) (*v3.VMIntentInput, error) {

configCreds := client.Credentials{
URL: fmt.Sprintf("%s:%d", d.ClusterConfig.Endpoint, d.ClusterConfig.Port),
Expand All @@ -273,6 +276,8 @@ func (d *NutanixDriver) CreateRequest(vm VmConfig) (*v3.VMIntentInput, error) {
return nil, err
}

log.Printf("preparing vm %s...", d.Config.VMName)

// If UserData exists, create GuestCustomization
var guestCustomization *v3.GuestCustomization
if vm.UserData == "" {
Expand All @@ -298,14 +303,23 @@ func (d *NutanixDriver) CreateRequest(vm VmConfig) (*v3.VMIntentInput, error) {
DiskList := []*v3.VMDisk{}
SATAindex := 0
SCSIindex := 0

var imageToDelete []string

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)
image, err := d.CreateImageURL(disk, vm)
if err != nil {
return nil, fmt.Errorf("error while findImageByUUID, Error %s", err.Error())
}

if disk.SourceImageDelete {
log.Printf("mark this image to delete: %s", *image.image.Status.Name)
imageToDelete = append(imageToDelete, *image.image.Metadata.UUID)
}

disk.SourceImageUUID = *image.image.Metadata.UUID
}
if disk.SourceImageUUID != "" {
Expand Down Expand Up @@ -362,10 +376,16 @@ 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)
image, err := d.CreateImageURL(disk, vm)
if err != nil {
return nil, fmt.Errorf("error while findImageByUUID, Error %s", err.Error())
}

if disk.SourceImageDelete {
log.Printf("mark this image to delete %s:", *image.image.Status.Name)
imageToDelete = append(imageToDelete, *image.image.Metadata.UUID)
}

disk.SourceImageUUID = *image.image.Metadata.UUID
}
if disk.SourceImageUUID != "" {
Expand Down Expand Up @@ -397,6 +417,8 @@ func (d *NutanixDriver) CreateRequest(vm VmConfig) (*v3.VMIntentInput, error) {
}
}

state.Put("image_to_delete", imageToDelete)

NICList := []*v3.VMNic{}
for _, nic := range vm.VmNICs {
subnet := &v3.SubnetIntentResponse{}
Expand Down Expand Up @@ -563,8 +585,8 @@ func (d *NutanixDriver) Delete(vmUUID string) error {
return nil
}

// UploadImage (string, VmConfig) (*nutanixImage, error)
func (d *NutanixDriver) UploadImage(imagePath string, sourceType string, imageType string, vm VmConfig) (*nutanixImage, error) {
// CreateImageURL (VmDisk, VmConfig) (*nutanixImage, error)
func (d *NutanixDriver) CreateImageURL(disk VmDisk, vm VmConfig) (*nutanixImage, error) {
configCreds := client.Credentials{
URL: fmt.Sprintf("%s:%d", d.ClusterConfig.Endpoint, d.ClusterConfig.Port),
Endpoint: d.ClusterConfig.Endpoint,
Expand All @@ -579,7 +601,7 @@ func (d *NutanixDriver) UploadImage(imagePath string, sourceType string, imageTy
return nil, err
}

_, file := path.Split(imagePath)
_, file := path.Split(disk.SourceImageURI)

cluster := &v3.ClusterIntentResponse{}
if vm.ClusterUUID != "" {
Expand All @@ -600,7 +622,7 @@ func (d *NutanixDriver) UploadImage(imagePath string, sourceType string, imageTy
Spec: &v3.Image{
Name: &file,
Resources: &v3.ImageResources{
ImageType: &imageType,
ImageType: &disk.ImageType,
InitialPlacementRefList: InitialPlacementRef,
},
Description: StringPtr(defaultImageDLDescription),
Expand All @@ -609,15 +631,79 @@ func (d *NutanixDriver) UploadImage(imagePath string, sourceType string, imageTy
Kind: StringPtr("image"),
},
}
if sourceType == "URI" {
image, err := sourceImageExists(conn, file, imagePath)

image, err := sourceImageExists(conn, file, disk.SourceImageURI)
if err != nil {
return nil, fmt.Errorf("error while checking if image exists, %s", err.Error())
}
if image != nil && !disk.SourceImageForce {
log.Printf("reuse existing image: %s", *image.Status.Name)
return &nutanixImage{image: *image}, nil
} else if image != nil && disk.SourceImageForce {
log.Printf("delete existing image: %s", *image.Status.Name)
d.DeleteImage(*image.Metadata.UUID)
}
req.Spec.Resources.SourceURI = &disk.SourceImageURI

log.Printf("creating image: %s", file)
image, err = conn.V3.CreateImage(req)
if err != nil {
return nil, fmt.Errorf("error while create image: %s", err.Error())
}

err = checkTask(conn, image.Status.ExecutionContext.TaskUUID.(string))
if err != nil {
return nil, fmt.Errorf("error while create image: %s", err.Error())
}

return &nutanixImage{image: *image}, nil
}

// CreateImageFile (VmDisk, VmConfig) (*nutanixImage, error)
func (d *NutanixDriver) CreateImageFile(filePath string, vm VmConfig) (*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, err
}

_, file := path.Split(filePath)

cluster := &v3.ClusterIntentResponse{}
if vm.ClusterUUID != "" {
cluster, err = conn.V3.GetCluster(vm.ClusterUUID)
if err != nil {
return nil, fmt.Errorf("error while checking if image exists, %s", err.Error())
return nil, fmt.Errorf("error while GetCluster, %s", err.Error())
}
if image != nil {
return &nutanixImage{image: *image}, nil
} else if vm.ClusterName != "" {
cluster, err = findClusterByName(conn, vm.ClusterName)
if err != nil {
return nil, fmt.Errorf("error while findClusterByName, %s", err.Error())
}
req.Spec.Resources.SourceURI = &imagePath
}

refvalue := BuildReferenceValue(*cluster.Metadata.UUID, "cluster")
InitialPlacementRef := []*v3.ReferenceValues{refvalue}
req := &v3.ImageIntentInput{
Spec: &v3.Image{
Name: &file,
Resources: &v3.ImageResources{
ImageType: StringPtr("ISO_IMAGE"),
InitialPlacementRefList: InitialPlacementRef,
},
Description: StringPtr(defaultImageDLDescription),
},
Metadata: &v3.Metadata{
Kind: StringPtr("image"),
},
}

log.Printf("creating image: %s", file)
Expand All @@ -631,18 +717,17 @@ func (d *NutanixDriver) UploadImage(imagePath string, sourceType string, imageTy
return nil, fmt.Errorf("error while create image: %s", err.Error())
}

if sourceType == "PATH" {
log.Printf("uploading image: %s", imagePath)
err = conn.V3.UploadImage(*image.Metadata.UUID, imagePath)
if err != nil {
return nil, fmt.Errorf("error while upload image: %s", err.Error())
}
log.Printf("uploading image: %s", filePath)
err = conn.V3.UploadImage(*image.Metadata.UUID, filePath)
if err != nil {
return nil, fmt.Errorf("error while upload image: %s", err.Error())
}

running, err := conn.V3.GetImage(*image.Metadata.UUID)
if err != nil || *running.Status.State != "COMPLETE" {
return nil, fmt.Errorf("error while upload image: %s", err.Error())
}
running, err := conn.V3.GetImage(*image.Metadata.UUID)
if err != nil || *running.Status.State != "COMPLETE" {
return nil, fmt.Errorf("error while upload image: %s", err.Error())
}

return &nutanixImage{image: *image}, nil

}
Expand Down
11 changes: 9 additions & 2 deletions builder/nutanix/step_build_vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (s *stepBuildVM) Run(ctx context.Context, state multistep.StateBag) multist
ui.Say("Uploading CD disk...")
cdFilesPath := cdPathRaw.(string)
log.Println("CD disk found " + cdFilesPath)
cdfilesImage, err := d.UploadImage(cdFilesPath, "PATH", "ISO_IMAGE", config.VmConfig)
cdfilesImage, err := d.CreateImageFile(cdFilesPath, config.VmConfig)
if err != nil {
ui.Error("Error uploading CD disk:" + err.Error())
state.Put("error", err)
Expand All @@ -46,7 +46,7 @@ func (s *stepBuildVM) Run(ctx context.Context, state multistep.StateBag) multist
ui.Say("Creating Packer Builder virtual machine...")

// Create VM Spec
vmRequest, err := d.CreateRequest(config.VmConfig)
vmRequest, err := d.CreateRequest(config.VmConfig, state)
if err != nil {
ui.Error("Error creating virtual machine request: " + err.Error())
state.Put("error", err)
Expand Down Expand Up @@ -113,4 +113,11 @@ func (s *stepBuildVM) Cleanup(state multistep.StateBag) {
ui.Message("Virtual machine successfully deleted")
}

imageToDelete := state.Get("image_to_delete")

for _, image := range imageToDelete.([]string) {
log.Printf("delete marked image: %s", image)
d.DeleteImage(image)
}

}
4 changes: 4 additions & 0 deletions docs/builders/nutanix.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ Sample:
- `source_image_name` (string) - Name of the image used as disk source.
- `source_image_uuid` (string) - UUID of the image used as disk source.
- `source_image_uri` (string) - URI of the image used as disk source (if image is not already on the cluster, it will download and store it before launching output image creation process).
- `source_image_delete` (bool) - Delete source image once build process is completed (default is false).
- `source_image_force` (bool) - Always download and replace source image even if already exist (default is false).
- `disk_size_gb` (number) - size of the disk (in gigabytes).

Sample:
Expand All @@ -104,6 +106,8 @@ Sample:
- `image_type` (string) - "ISO_IMAGE".
- `source_image_name` (string) - Name of the ISO image to mount.
- `source_image_uuid` (string) - UUID of the ISO image to mount.
- `source_image_delete` (bool) - Delete source image once build process is completed (default is false).
- `source_image_force` (bool) - Always download and replace source image even if already exist (default is false).

Sample:
```hcl
Expand Down
Loading