diff --git a/.github/workflows/test-plugin-e2e.yaml b/.github/workflows/test-plugin-e2e.yaml index 951bcef..9792842 100644 --- a/.github/workflows/test-plugin-e2e.yaml +++ b/.github/workflows/test-plugin-e2e.yaml @@ -5,9 +5,9 @@ on: workflow_dispatch: inputs: logs: - description: 'Set 1 to activate full logs' + description: "Set 1 to activate full logs" required: false - default: '0' + default: "0" jobs: plugin-build: @@ -22,28 +22,27 @@ jobs: - name: Setup `golang` uses: actions/setup-go@v4 with: - go-version: '>=1.19.0' + go-version: ">=1.19.0" - name: Build packer plugin run: | cd $GITHUB_WORKSPACE make dev - + - uses: actions/upload-artifact@v3 with: name: packer-plugin-nutanix - path: ~/.packer.d/plugins/packer-plugin-nutanix + path: ~/.config/packer/plugins/packer-plugin-nutanix retention-days: 7 - name: build test list id: test-list run: echo "list=$(ls test/e2e | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT - e2e: name: E2E test needs: plugin-build - + strategy: matrix: test: ${{fromJSON(needs.plugin-build.outputs.test-list)}} @@ -52,7 +51,6 @@ jobs: defaults: run: working-directory: test/e2e/${{ matrix.test}} - steps: - name: Checkout Repository @@ -74,9 +72,9 @@ jobs: - name: Install plugin run: | - mkdir -p ~/.packer.d/plugins/ - cp /tmp/packer-plugin-nutanix ~/.packer.d/plugins/packer-plugin-nutanix - chmod 755 ~/.packer.d/plugins/packer-plugin-nutanix + mkdir -p ~/.config/packer/plugins/ + cp /tmp/packer-plugin-nutanix ~/.config/packer/plugins/packer-plugin-nutanix + chmod 755 ~/.config/packer/plugins/packer-plugin-nutanix - name: Run `packer init` id: init @@ -108,4 +106,3 @@ jobs: else exit 1 fi - diff --git a/Makefile b/Makefile index 426cd8b..84e28d7 100644 --- a/Makefile +++ b/Makefile @@ -11,8 +11,8 @@ build: @go build -o ${BINARY} dev: build - @mkdir -p ~/.packer.d/plugins/ - @mv ${BINARY} ~/.packer.d/plugins/${BINARY} + @mkdir -p ~/.config/packer/plugins + @mv ${BINARY} ~/.config/packer/plugins/${BINARY} test: @go test -race -count $(COUNT) $(TEST) -timeout=3m diff --git a/builder/nutanix/config.go b/builder/nutanix/config.go index 5ef4df7..5a1495e 100644 --- a/builder/nutanix/config.go +++ b/builder/nutanix/config.go @@ -82,6 +82,7 @@ type VmConfig struct { MemoryMB int64 `mapstructure:"memory_mb" json:"memory_mb" required:"false"` UserData string `mapstructure:"user_data" json:"user_data" required:"false"` VMCategories []Category `mapstructure:"vm_categories" required:"false"` + Project string `mapstructure:"project" required:"false"` } func (c *Config) Prepare(raws ...interface{}) ([]string, error) { diff --git a/builder/nutanix/config.hcl2spec.go b/builder/nutanix/config.hcl2spec.go index fcf0ae4..71efded 100644 --- a/builder/nutanix/config.hcl2spec.go +++ b/builder/nutanix/config.hcl2spec.go @@ -145,6 +145,7 @@ type FlatConfig struct { MemoryMB *int64 `mapstructure:"memory_mb" json:"memory_mb" required:"false" cty:"memory_mb" hcl:"memory_mb"` UserData *string `mapstructure:"user_data" json:"user_data" required:"false" cty:"user_data" hcl:"user_data"` VMCategories []FlatCategory `mapstructure:"vm_categories" required:"false" cty:"vm_categories" hcl:"vm_categories"` + Project *string `mapstructure:"project" required:"false" cty:"project" hcl:"project"` ForceDeregister *bool `mapstructure:"force_deregister" json:"force_deregister" required:"false" cty:"force_deregister" hcl:"force_deregister"` ImageDescription *string `mapstructure:"image_description" json:"image_description" required:"false" cty:"image_description" hcl:"image_description"` ImageCategories []FlatCategory `mapstructure:"image_categories" required:"false" cty:"image_categories" hcl:"image_categories"` @@ -244,6 +245,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "memory_mb": &hcldec.AttrSpec{Name: "memory_mb", Type: cty.Number, Required: false}, "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, "vm_categories": &hcldec.BlockListSpec{TypeName: "vm_categories", Nested: hcldec.ObjectSpec((*FlatCategory)(nil).HCL2Spec())}, + "project": &hcldec.AttrSpec{Name: "project", Type: cty.String, Required: false}, "force_deregister": &hcldec.AttrSpec{Name: "force_deregister", Type: cty.Bool, Required: false}, "image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false}, "image_categories": &hcldec.BlockListSpec{TypeName: "image_categories", Nested: hcldec.ObjectSpec((*FlatCategory)(nil).HCL2Spec())}, @@ -269,6 +271,7 @@ type FlatVmConfig struct { MemoryMB *int64 `mapstructure:"memory_mb" json:"memory_mb" required:"false" cty:"memory_mb" hcl:"memory_mb"` UserData *string `mapstructure:"user_data" json:"user_data" required:"false" cty:"user_data" hcl:"user_data"` VMCategories []FlatCategory `mapstructure:"vm_categories" required:"false" cty:"vm_categories" hcl:"vm_categories"` + Project *string `mapstructure:"project" required:"false" cty:"project" hcl:"project"` } // FlatMapstructure returns a new FlatVmConfig. @@ -295,6 +298,7 @@ func (*FlatVmConfig) HCL2Spec() map[string]hcldec.Spec { "memory_mb": &hcldec.AttrSpec{Name: "memory_mb", Type: cty.Number, Required: false}, "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, "vm_categories": &hcldec.BlockListSpec{TypeName: "vm_categories", Nested: hcldec.ObjectSpec((*FlatCategory)(nil).HCL2Spec())}, + "project": &hcldec.AttrSpec{Name: "project", Type: cty.String, Required: false}, } return s } diff --git a/builder/nutanix/driver.go b/builder/nutanix/driver.go index 9589c50..06283e7 100644 --- a/builder/nutanix/driver.go +++ b/builder/nutanix/driver.go @@ -56,6 +56,37 @@ type nutanixImage struct { image v3.ImageIntentResponse } +func findProjectByName(conn *v3.Client, name string) (*v3.Project, error) { + filter := fmt.Sprintf("name==%s", name) + resp, err := conn.V3.ListAllProject(filter) + if err != nil { + return nil, err + } + entities := resp.Entities + + found := make([]*v3.Project, 0) + for _, v := range entities { + if v.Status.Name == name { + found = append(found, &v3.Project{ + Status: v.Status, + Spec: v.Spec, + Metadata: v.Metadata, + APIVersion: v.APIVersion, + }) + } + } + + if len(found) > 1 { + return nil, fmt.Errorf("your query returned more than one result") + } + + if len(found) == 0 { + return nil, fmt.Errorf("did not find project with name %s", name) + } + + return found[0], nil +} + func findClusterByName(conn *v3.Client, name string) (*v3.ClusterIntentResponse, error) { filter := fmt.Sprintf("name==%s", name) resp, err := conn.V3.ListAllCluster(filter) @@ -117,6 +148,7 @@ 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) @@ -437,6 +469,18 @@ func (d *NutanixDriver) CreateRequest(vm VmConfig) (*v3.VMIntentInput, error) { req.Metadata.Categories = c } + if vm.Project != "" { + project, err := findProjectByName(conn, vm.Project) + if err != nil { + return nil, fmt.Errorf("error while findProjectByName, %s", err.Error()) + } + + req.Metadata.ProjectReference = &v3.Reference{ + Kind: StringPtr("project"), + UUID: project.Metadata.UUID, + } + } + return req, nil } diff --git a/builder/nutanix/step_build_vm.go b/builder/nutanix/step_build_vm.go index c45d5ba..1ea7562 100644 --- a/builder/nutanix/step_build_vm.go +++ b/builder/nutanix/step_build_vm.go @@ -45,12 +45,15 @@ 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) if err != nil { ui.Error("Error creating virtual machine request: " + err.Error()) state.Put("error", err) return multistep.ActionHalt } + + // Create VM vmInstance, err := d.Create(vmRequest) if err != nil { diff --git a/docs/builders/nutanix.mdx b/docs/builders/nutanix.mdx index 8fd9cdb..4ebae15 100644 --- a/docs/builders/nutanix.mdx +++ b/docs/builders/nutanix.mdx @@ -32,6 +32,7 @@ These parameters allow to define information about platform and temporary VM use - `boot_type` (string) - Type of boot used on the temporary VM ("legacy" or "uefi"). - `ip_wait_timeout` (duration string | ex: "0h42m0s") - Amount of time to wait for VM's IP, similar to 'ssh_timeout'. Defaults to 15m (15 minutes). See the Golang [ParseDuration](https://golang.org/pkg/time/#ParseDuration) documentation for full details. - `vm_categories` ([]Category) - Assign Categories to the vm. + - `project` (string) - Assign Project to the vm. ## Output configuration diff --git a/example/source.nutanix.pkr.hcl b/example/source.nutanix.pkr.hcl index 7db7fb8..9c50490 100644 --- a/example/source.nutanix.pkr.hcl +++ b/example/source.nutanix.pkr.hcl @@ -27,6 +27,8 @@ source "nutanix" "centos" { value = "Dev" } +// project = "myproject" + image_name = "centos-packer-image" image_export = false force_deregister = true