├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml ├── release.yaml └── workflows │ ├── codeql-analysis.yml │ ├── ensure-docs-compiled.yaml │ ├── integration.yml │ ├── notify-integration-release-via-manual.yaml │ ├── notify-integration-release-via-tag.yaml │ ├── release.yml │ ├── synopsys-schedule.yaml │ ├── synopsys.yaml │ ├── test-plugin-e2e.yaml │ ├── test-plugin-example.yml │ └── trivy-scan.yaml ├── .gitignore ├── .goreleaser.yml ├── .web-docs ├── README.md ├── components │ └── builder │ │ └── nutanix │ │ └── README.md ├── metadata.hcl └── scripts │ └── compile-to-webdocs.sh ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── builder └── nutanix │ ├── artifact.go │ ├── builder.go │ ├── config.go │ ├── config.hcl2spec.go │ ├── driver.go │ ├── pointers.go │ ├── ssh.go │ ├── step_build_vm.go │ ├── step_copy_image.go │ ├── step_export_image.go │ ├── step_shutdown_vm.go │ └── utils.go ├── docs ├── README.md └── builders │ └── nutanix.mdx ├── example ├── README.md ├── build.nutanix.pkr.hcl ├── init │ └── plugin.pkr.hcl ├── scripts │ ├── cleanup.ps1 │ ├── cloud-init │ │ ├── cloud-config-centos.yaml │ │ └── cloud-config-ubuntu.yaml │ ├── gui │ │ └── autounattend.xml │ ├── ks.cfg │ └── win-update.ps1 ├── settings.auto.pkrvars.hcl ├── source.nutanix.pkr.hcl └── variables.pkr.hcl ├── go.mod ├── go.sum ├── main.go ├── test └── e2e │ ├── centos-img │ ├── build.pkr.hcl │ ├── source.pkr.hcl │ └── variables.pkr.hcl │ └── centos-iso │ ├── build.pkr.hcl │ ├── scripts │ └── ks.cfg │ ├── source.pkr.hcl │ └── variables.pkr.hcl └── version └── version.go /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Tell us about a problem you are experiencing 4 | 5 | --- 6 | 7 | /kind bug 8 | 9 | **What steps did you take and what happened:** 10 | 11 | _A clear and concise description of what the bug is and how has this been tested. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration_ 12 | _Paste a full trace by launching packer with `PACKER_LOG=1 packer []`_ 13 | 14 | **What did you expect to happen:** 15 | 16 | 17 | **Anything else you would like to add:** 18 | 19 | _Miscellaneous information that will assist in solving the issue._ 20 | 21 | 22 | **Environment:** 23 | 24 | - packer-plugin-nutanix version (use `packer plugins installed`): 25 | - Packer version (use `packer version`): 26 | - OS (e.g. from `/etc/os-release`): -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature enhancement request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | /kind feature 8 | 9 | **Describe the solution you'd like** 10 | [A clear and concise description of what you want to happen.] 11 | 12 | 13 | **Anything else you would like to add:** 14 | [Miscellaneous information that will assist in solving the issue.] 15 | 16 | 17 | **Environment:** 18 | 19 | - packer-plugin-nutanix version (use `packer plugins installed`): 20 | - Packer version (use `packer version`): 21 | - OS (e.g. from `/etc/os-release`): -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | **What this PR does / why we need it**: 10 | 11 | **Which issue(s) this PR fixes** *(optional, in `fixes #(, fixes #, ...)` format, will close the issue(s) when PR gets merged)*: 12 | Fixes # 13 | 14 | **How Has This Been Tested?**: 15 | 16 | _Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration and test output_ 17 | 18 | 19 | **Special notes for your reviewer**: 20 | 21 | _Please confirm that if this PR changes any image versions, then that's the sole change this PR makes._ 22 | 23 | **Release note**: 24 | 28 | ```release-note 29 | 30 | ``` 31 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | # Group all patch and minor Go module updates into a single PR for ease of management. This assumes 9 | # that Go modules respect semver, which is generally true but not guaranteed. 10 | # Update weekly to avoid excessive PRs. 11 | - package-ecosystem: "gomod" 12 | directory: "/" 13 | schedule: 14 | interval: "weekly" 15 | groups: 16 | all-go-mod-patch-and-minor: 17 | patterns: [ "*" ] 18 | update-types: [ "patch", "minor" ] 19 | 20 | # Enable version updates for GitHub Actions 21 | - package-ecosystem: "github-actions" 22 | directory: "/" 23 | schedule: 24 | interval: "daily" 25 | -------------------------------------------------------------------------------- /.github/release.yaml: -------------------------------------------------------------------------------- 1 | # .github/release.yml 2 | 3 | changelog: 4 | exclude: 5 | labels: 6 | - ignore-for-release 7 | categories: 8 | - title: Breaking Changes 🛠 9 | labels: 10 | - Semver-Major 11 | - breaking-change 12 | - title: Exciting New Features 🎉 13 | labels: 14 | - Semver-Minor 15 | - enhancement 16 | - title: Bug Fixes 🐛 17 | labels: 18 | - bug 19 | - title: Documentation 📖 20 | labels: 21 | - documentation 22 | - title: Other Changes 23 | labels: 24 | - "*" -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | 2 | name: "Code Scanning - Action" 3 | 4 | on: 5 | push: 6 | branches: [main] 7 | pull_request: 8 | branches: [main] 9 | schedule: 10 | - cron: '30 1 * * 0' 11 | 12 | jobs: 13 | CodeQL-Build: 14 | runs-on: ubuntu-latest 15 | 16 | permissions: 17 | # required for all workflows 18 | security-events: write 19 | 20 | # only required for workflows in private repositories 21 | actions: read 22 | contents: read 23 | 24 | steps: 25 | - name: Checkout repository 26 | uses: actions/checkout@v4 27 | 28 | # Initializes the CodeQL tools for scanning. 29 | - name: Initialize CodeQL 30 | uses: github/codeql-action/init@v3 31 | # Override language selection by uncommenting this and choosing your languages 32 | # with: 33 | # languages: go, javascript, csharp, python, cpp, java 34 | 35 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 36 | # If this step fails, then you should remove it and run the build manually (see below). 37 | - name: Autobuild 38 | uses: github/codeql-action/autobuild@v3 39 | 40 | # ℹ️ Command-line programs to run using the OS shell. 41 | # 📚 https://git.io/JvXDl 42 | 43 | # ✏️ If the Autobuild fails above, remove it and uncomment the following 44 | # three lines and modify them (or add more) to build your code if your 45 | # project uses a compiled language 46 | 47 | #- run: | 48 | # make bootstrap 49 | # make release 50 | 51 | - name: Perform CodeQL Analysis 52 | uses: github/codeql-action/analyze@v3 53 | -------------------------------------------------------------------------------- /.github/workflows/ensure-docs-compiled.yaml: -------------------------------------------------------------------------------- 1 | name: Ensure Docs are Compiled 2 | on: 3 | push: 4 | jobs: 5 | ensure-docs-compiled: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout 🛎 9 | uses: actions/checkout@v4 10 | - uses: actions/setup-go@v5 11 | - shell: bash 12 | run: make generate 13 | - shell: bash 14 | run: | 15 | if [[ -z "$(git status -s)" ]]; then 16 | echo "OK" 17 | else 18 | echo "Docs have been updated, but the compiled docs have not been committed." 19 | echo "Run 'make generate', and commit the result to resolve this error." 20 | exit 1 21 | fi 22 | 23 | -------------------------------------------------------------------------------- /.github/workflows/integration.yml: -------------------------------------------------------------------------------- 1 | name: Integration 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | check: 9 | name: Check 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout Code 13 | uses: actions/checkout@v4 14 | 15 | - name: Setup Go 16 | uses: actions/setup-go@v5 17 | with: 18 | go-version-file: go.mod 19 | 20 | - name: Static Check 21 | uses: dominikh/staticcheck-action@v1.3.1 22 | with: 23 | version: "latest" 24 | install-go: false 25 | 26 | vet: 27 | name: Vet 28 | runs-on: ubuntu-latest 29 | steps: 30 | - name: Checkout Code 31 | uses: actions/checkout@v4 32 | 33 | - name: Setup Go 34 | uses: actions/setup-go@v5 35 | with: 36 | go-version-file: go.mod 37 | 38 | - name: Vet Go code 39 | run: go vet ./... 40 | 41 | build: 42 | name: Build 43 | runs-on: ubuntu-latest 44 | steps: 45 | - name: Check out code 46 | uses: actions/checkout@v4 47 | 48 | - name: Set up Go 49 | uses: actions/setup-go@v5 50 | with: 51 | go-version-file: go.mod 52 | 53 | - name: Compile 54 | run: make build 55 | 56 | - name: Trivy scan 57 | uses: aquasecurity/trivy-action@0.30.0 58 | env: 59 | TRIVY_DB_REPOSITORY: "ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db" 60 | with: 61 | scan-type: "fs" 62 | ignore-unfixed: true 63 | format: "table" 64 | exit-code: "1" 65 | vuln-type: "os,library" 66 | severity: "CRITICAL,HIGH" 67 | -------------------------------------------------------------------------------- /.github/workflows/notify-integration-release-via-manual.yaml: -------------------------------------------------------------------------------- 1 | # Manual release workflow is used for deploying documentation updates 2 | # on the specified branch without making an official plugin release. 3 | name: Notify Integration Release (Manual) 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | version: 8 | description: "The release version (semver)" 9 | default: 1.0.0 10 | required: false 11 | branch: 12 | description: "A branch or SHA" 13 | default: 'main' 14 | required: false 15 | jobs: 16 | notify-release: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout this repo 20 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 21 | with: 22 | ref: ${{ github.event.inputs.branch }} 23 | # Ensure that Docs are Compiled 24 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0 25 | - shell: bash 26 | run: make generate 27 | - shell: bash 28 | run: | 29 | if [[ -z "$(git status -s)" ]]; then 30 | echo "OK" 31 | else 32 | echo "Docs have been updated, but the compiled docs have not been committed." 33 | echo "Run 'make generate', and commit the result to resolve this error." 34 | exit 1 35 | fi 36 | # Perform the Release 37 | - name: Checkout integration-release-action 38 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 39 | with: 40 | repository: hashicorp/integration-release-action 41 | path: ./integration-release-action 42 | - name: Notify Release 43 | uses: ./integration-release-action 44 | with: 45 | # The integration identifier will be used by the Packer team to register the integration 46 | # the expected format is packer// 47 | integration_identifier: "packer/nutanix-cloud-native/nutanix" 48 | release_version: ${{ github.event.inputs.version }} 49 | release_sha: ${{ github.event.inputs.branch }} 50 | github_token: ${{ secrets.GITHUB_TOKEN }} 51 | -------------------------------------------------------------------------------- /.github/workflows/notify-integration-release-via-tag.yaml: -------------------------------------------------------------------------------- 1 | name: Notify Integration Release (Tag) 2 | on: 3 | push: 4 | tags: 5 | - '*.*.*' # Proper releases 6 | jobs: 7 | strip-version: 8 | runs-on: ubuntu-latest 9 | outputs: 10 | packer-version: ${{ steps.strip.outputs.packer-version }} 11 | steps: 12 | - name: Strip leading v from version tag 13 | id: strip 14 | env: 15 | REF: ${{ github.ref_name }} 16 | run: | 17 | echo "packer-version=$(echo "$REF" | sed -E 's/v?([0-9]+\.[0-9]+\.[0-9]+)/\1/')" >> "$GITHUB_OUTPUT" 18 | notify-release: 19 | needs: 20 | - strip-version 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout this repo 24 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 25 | with: 26 | ref: ${{ github.ref }} 27 | # Ensure that Docs are Compiled 28 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0 29 | - shell: bash 30 | run: make generate 31 | - shell: bash 32 | run: | 33 | if [[ -z "$(git status -s)" ]]; then 34 | echo "OK" 35 | else 36 | echo "Docs have been updated, but the compiled docs have not been committed." 37 | echo "Run 'make generate', and commit the result to resolve this error." 38 | exit 1 39 | fi 40 | # Perform the Release 41 | - name: Checkout integration-release-action 42 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 43 | with: 44 | repository: hashicorp/integration-release-action 45 | path: ./integration-release-action 46 | - name: Notify Release 47 | uses: ./integration-release-action 48 | with: 49 | # The integration identifier will be used by the Packer team to register the integration 50 | # the expected format is packer// 51 | integration_identifier: "packer/nutanix-cloud-native/nutanix" 52 | release_version: ${{ needs.strip-version.outputs.packer-version }} 53 | release_sha: ${{ github.ref }} 54 | github_token: ${{ secrets.GITHUB_TOKEN }} 55 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # This GitHub action can publish assets for release when a tag is created. 2 | # Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0). 3 | # 4 | # This uses an action (hashicorp/ghaction-import-gpg) that assumes you set your 5 | # private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `GPG_PASSPHRASE` 6 | # secret. If you would rather own your own GPG handling, please fork this action 7 | # or use an alternative one for key handling. 8 | # 9 | # You will need to pass the `--batch` flag to `gpg` in your signing step 10 | # in `goreleaser` to indicate this is being used in a non-interactive mode. 11 | # 12 | name: Release 13 | on: 14 | push: 15 | tags: 16 | - "v*" 17 | 18 | jobs: 19 | goreleaser: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v4 24 | with: 25 | fetch-depth: 0 26 | 27 | - name: Set up Go 28 | uses: actions/setup-go@v5 29 | with: 30 | go-version-file: go.mod 31 | 32 | - name: Describe plugin 33 | id: plugin_describe 34 | run: echo "::set-output name=api_version::$(go run . describe | jq -r '.api_version')" 35 | 36 | - name: Import GPG key 37 | id: import_gpg 38 | uses: crazy-max/ghaction-import-gpg@v6 39 | with: 40 | gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} 41 | passphrase: ${{ secrets.GPG_PASSPHRASE }} 42 | 43 | - name: Run GoReleaser 44 | uses: goreleaser/goreleaser-action@v6 45 | with: 46 | version: '~> v2' 47 | args: release --clean 48 | env: 49 | GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} 50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 51 | API_VERSION: ${{ steps.plugin_describe.outputs.api_version }} 52 | -------------------------------------------------------------------------------- /.github/workflows/synopsys-schedule.yaml: -------------------------------------------------------------------------------- 1 | name: Black Duck Daily Policy Check 2 | on: 3 | schedule: 4 | - cron: "0 0 * * *" 5 | 6 | jobs: 7 | security: 8 | if: github.repository == 'nutanix-cloud-native/packer-plugin-nutanix' 9 | runs-on: ubuntu-latest 10 | permissions: 11 | actions: read 12 | contents: read 13 | security-events: write 14 | checks: write 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | 19 | - name: Setup Go 20 | uses: actions/setup-go@v5 21 | with: 22 | go-version-file: go.mod 23 | 24 | - name: Build Project 25 | run: make build 26 | 27 | - name: Black Duck Full Scan 28 | uses: blackduck-inc/black-duck-security-scan@v2.1.1 29 | with: 30 | blackducksca_url : ${{ secrets.BLACKDUCK_URL }} 31 | blackducksca_token: ${{ secrets.BLACKDUCK_API_TOKEN }} 32 | github_token: ${{ secrets.GITHUB_TOKEN }} 33 | blackducksca_scan_full : true 34 | blackducksca_scan_failure_severities: 'BLOCKER,CRITICAL' -------------------------------------------------------------------------------- /.github/workflows/synopsys.yaml: -------------------------------------------------------------------------------- 1 | name: Black Duck Policy Check 2 | on: 3 | pull_request_target: 4 | branches: 5 | - main 6 | push: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | security: 12 | if: github.repository == 'nutanix-cloud-native/packer-plugin-nutanix' 13 | runs-on: ubuntu-latest 14 | permissions: 15 | actions: read 16 | contents: read 17 | security-events: write 18 | checks: write 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | 23 | - name: Setup Go 24 | uses: actions/setup-go@v5 25 | with: 26 | go-version-file: go.mod 27 | 28 | - name: Build Project 29 | run: make build 30 | 31 | - name: Black Duck Full Scan 32 | if: ${{ github.event_name != 'pull_request' }} 33 | uses: blackduck-inc/black-duck-security-scan@v2.1.1 34 | with: 35 | blackducksca_url: ${{ secrets.BLACKDUCK_URL }} 36 | blackducksca_token: ${{ secrets.BLACKDUCK_API_TOKEN }} 37 | github_token: ${{ secrets.GITHUB_TOKEN }} 38 | blackducksca_scan_full: true 39 | blackducksca_scan_failure_severities: 'BLOCKER,CRITICAL' 40 | 41 | - name: Black Duck PR Scan 42 | if: ${{ github.event_name == 'pull_request' }} 43 | uses: blackduck-inc/black-duck-security-scan@v2.1.1 44 | env: 45 | DETECT_PROJECT_VERSION_NAME: ${{ github.base_ref }} 46 | with: 47 | blackducksca_url: ${{ secrets.BLACKDUCK_URL }} 48 | blackducksca_token: ${{ secrets.BLACKDUCK_API_TOKEN }} 49 | github_token: ${{ secrets.GITHUB_TOKEN }} 50 | blackducksca_scan_full: false 51 | blackducksca_prComment_enabled: true -------------------------------------------------------------------------------- /.github/workflows/test-plugin-e2e.yaml: -------------------------------------------------------------------------------- 1 | name: E2E tests 2 | 3 | on: 4 | pull_request: 5 | workflow_dispatch: 6 | inputs: 7 | logs: 8 | description: "Set 1 to activate full logs" 9 | required: false 10 | default: "0" 11 | 12 | jobs: 13 | plugin-build: 14 | runs-on: [self-hosted, nxlab, packer] 15 | outputs: 16 | test-list: ${{ steps.test-list.outputs.list}} 17 | 18 | steps: 19 | - name: Checkout Repository 20 | uses: actions/checkout@v4 21 | 22 | - name: Setup `golang` 23 | uses: actions/setup-go@v5 24 | with: 25 | go-version-file: go.mod 26 | 27 | - name: Build packer plugin 28 | run: | 29 | cd $GITHUB_WORKSPACE 30 | make build 31 | 32 | - uses: actions/upload-artifact@v4 33 | with: 34 | name: packer-plugin-nutanix 35 | path: packer-plugin-nutanix 36 | retention-days: 7 37 | 38 | - name: build test list 39 | id: test-list 40 | run: echo "list=$(ls test/e2e | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT 41 | 42 | e2e: 43 | name: E2E test 44 | needs: plugin-build 45 | 46 | strategy: 47 | matrix: 48 | test: ${{fromJSON(needs.plugin-build.outputs.test-list)}} 49 | 50 | runs-on: [self-hosted, nxlab, packer] 51 | defaults: 52 | run: 53 | working-directory: test/e2e/${{ matrix.test}} 54 | 55 | steps: 56 | - name: Checkout Repository 57 | uses: actions/checkout@v4 58 | 59 | - name: Setup `packer` 60 | uses: hashicorp/setup-packer@main 61 | id: setup-packer 62 | 63 | - name: Setup `xorriso` 64 | run: | 65 | sudo apt update -y 66 | sudo apt install -y xorriso 67 | 68 | - uses: actions/download-artifact@v4 69 | with: 70 | name: packer-plugin-nutanix 71 | path: /tmp 72 | 73 | - name: Fix plugin permissions 74 | run: | 75 | chmod +x /tmp/packer-plugin-nutanix 76 | 77 | - name: Install plugin 78 | run: | 79 | packer plugins install --path /tmp/packer-plugin-nutanix "github.com/nutanix-cloud-native/nutanix" 80 | 81 | - name: Run `packer init` 82 | id: init 83 | run: | 84 | packer init . 85 | 86 | - name: Run `packer validate` 87 | id: validate 88 | run: packer validate -var "test=${{ matrix.test}}" . 89 | env: 90 | PACKER_LOG: ${{ github.event.inputs.logs }} 91 | 92 | - name: Run `packer build` 93 | id: build 94 | run: packer build -var "test=${{ matrix.test}}" . 95 | env: 96 | PACKER_LOG: ${{ github.event.inputs.logs }} 97 | 98 | results: 99 | if: ${{ always() }} 100 | runs-on: [self-hosted, nxlab, packer] 101 | name: Final E2E results 102 | needs: [e2e] 103 | steps: 104 | - run: | 105 | result="${{ needs.e2e.result }}" 106 | if [[ $result == "success" || $result == "skipped" ]]; then 107 | exit 0 108 | else 109 | exit 1 110 | fi 111 | -------------------------------------------------------------------------------- /.github/workflows/test-plugin-example.yml: -------------------------------------------------------------------------------- 1 | # This is a manually triggered action workflow. 2 | # It uses Packer at latest version to init, validate and build 3 | # an example configuration in a folder. 4 | # This action is compatible with Packer v1.7.0 or later. 5 | name: test plugin example 6 | 7 | on: 8 | workflow_dispatch: 9 | inputs: 10 | logs: 11 | description: 'Set 1 to activate full logs' 12 | required: false 13 | default: '0' 14 | folder: 15 | description: 'Example folder' 16 | required: false 17 | default: './example' 18 | 19 | jobs: 20 | build: 21 | runs-on: ubuntu-latest 22 | name: init and build example 23 | steps: 24 | - name: Checkout Repository 25 | uses: actions/checkout@v4 26 | 27 | - name: Init 28 | uses: hashicorp/packer-github-actions@master 29 | with: 30 | working_directory: ${{ github.event.inputs.folder }} 31 | command: init 32 | 33 | - name: Validate 34 | uses: hashicorp/packer-github-actions@master 35 | with: 36 | working_directory: ${{ github.event.inputs.folder }} 37 | command: validate 38 | env: 39 | PACKER_LOG: ${{ github.event.inputs.logs }} 40 | 41 | - name: Build 42 | uses: hashicorp/packer-github-actions@master 43 | with: 44 | working_directory: ${{ github.event.inputs.folder }} 45 | command: build 46 | env: 47 | PACKER_LOG: ${{ github.event.inputs.logs }} -------------------------------------------------------------------------------- /.github/workflows/trivy-scan.yaml: -------------------------------------------------------------------------------- 1 | name: Trivy Scan 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "17 17 * * *" 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | build: 13 | permissions: 14 | contents: read 15 | security-events: write 16 | name: Scan 17 | runs-on: "ubuntu-latest" 18 | steps: 19 | - name: Checkout Code 20 | uses: actions/checkout@v4 21 | 22 | - name: Run Trivy vulnerability scanner 23 | uses: aquasecurity/trivy-action@0.30.0 24 | env: 25 | TRIVY_DB_REPOSITORY: "ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db" 26 | with: 27 | scan-type: "fs" 28 | format: "sarif" 29 | output: "trivy-results.sarif" 30 | severity: "CRITICAL,HIGH" 31 | 32 | - name: Upload Trivy scan results to GitHub Security tab 33 | uses: github/codeql-action/upload-sarif@v3 34 | with: 35 | sarif_file: "trivy-results.sarif" 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | main 2 | example/secret.nutanix.auto.pkrvars.hcl 3 | dist/* 4 | packer-plugin-nutanix 5 | .docs/* 6 | 7 | ### Go ### 8 | # Binaries for programs and plugins 9 | *.exe 10 | *.exe~ 11 | *.dll 12 | *.so 13 | *.dylib 14 | 15 | # Test binary, built with `go test -c` 16 | *.test 17 | 18 | # Output of the go coverage tool, specifically when used with LiteIDE 19 | *.out 20 | 21 | # Dependency directories (remove the comment below to include it) 22 | # vendor/ 23 | 24 | # Go workspace file 25 | go.work 26 | 27 | ### Go Patch ### 28 | /vendor/ 29 | /Godeps/ 30 | 31 | ### macOS ### 32 | # General 33 | .DS_Store 34 | .AppleDouble 35 | .LSOverride 36 | 37 | # Icon must end with two \r 38 | Icon 39 | 40 | 41 | # Thumbnails 42 | ._* 43 | 44 | # Files that might appear in the root of a volume 45 | .DocumentRevisions-V100 46 | .fseventsd 47 | .Spotlight-V100 48 | .TemporaryItems 49 | .Trashes 50 | .VolumeIcon.icns 51 | .com.apple.timemachine.donotpresent 52 | 53 | # Directories potentially created on remote AFP share 54 | .AppleDB 55 | .AppleDesktop 56 | Network Trash Folder 57 | Temporary Items 58 | .apdisk 59 | 60 | ### VisualStudioCode ### 61 | .vscode/* 62 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | # Make sure to check the documentation at http://goreleaser.com 3 | env: 4 | - CGO_ENABLED=0 5 | before: 6 | hooks: 7 | # We strongly recommend running tests to catch any regression before release. 8 | # Even though, this an optional step. 9 | - go test ./... 10 | # As part of the release doc files are included as a separate deliverable for 11 | # consumption by Packer.io. To include a separate docs.zip uncomment the following command. 12 | # - make ci-release-docs 13 | # Check plugin compatibility with required version of the Packer SDK 14 | - make plugin-check 15 | builds: 16 | # A separated build to run the packer-plugins-check only once for a linux_amd64 binary 17 | - id: plugin-check 18 | mod_timestamp: "{{ .CommitTimestamp }}" 19 | flags: 20 | - -trimpath #removes all file system paths from the compiled executable 21 | ldflags: 22 | - "-s -w -X {{ .ModulePath }}/version.Version={{.Version}} -X {{ .ModulePath }}/version.VersionPrerelease= " 23 | goos: 24 | - linux 25 | goarch: 26 | - amd64 27 | binary: "{{ .ProjectName }}_v{{ .Version }}_{{ .Env.API_VERSION }}_{{ .Os }}_{{ .Arch }}" 28 | - mod_timestamp: "{{ .CommitTimestamp }}" 29 | flags: 30 | - -trimpath #removes all file system paths from the compiled executable 31 | ldflags: 32 | - "-s -w -X {{ .ModulePath }}/version.Version={{.Version}} -X {{ .ModulePath }}/version.VersionPrerelease= " 33 | goos: 34 | - netbsd 35 | - solaris 36 | - openbsd 37 | - freebsd 38 | - windows 39 | - linux 40 | - darwin 41 | goarch: 42 | - amd64 43 | - "386" 44 | - arm 45 | - arm64 46 | ignore: 47 | - goos: openbsd 48 | goarch: arm64 49 | - goos: darwin 50 | goarch: "386" 51 | - goos: linux 52 | goarch: amd64 53 | binary: "{{ .ProjectName }}_v{{ .Version }}_{{ .Env.API_VERSION }}_{{ .Os }}_{{ .Arch }}" 54 | archives: 55 | - formats: ["zip"] 56 | files: 57 | - none* 58 | name_template: "{{ .ProjectName }}_v{{ .Version }}_{{ .Env.API_VERSION }}_{{ .Os }}_{{ .Arch }}" 59 | checksum: 60 | name_template: "{{ .ProjectName }}_v{{ .Version }}_SHA256SUMS" 61 | algorithm: sha256 62 | signs: 63 | - artifacts: checksum 64 | args: 65 | # if you are using this is in a GitHub action or some other automated pipeline, you 66 | # need to pass the batch flag to indicate its not interactive. 67 | - "--batch" 68 | - "--local-user" 69 | - "{{ .Env.GPG_FINGERPRINT }}" 70 | - "--output" 71 | - "${signature}" 72 | - "--detach-sign" 73 | - "${artifact}" 74 | release: 75 | # If you want to manually examine the release before its live, uncomment this line: 76 | # draft: true 77 | # As part of the release doc files are included as a separate deliverable for consumption by Packer.io. 78 | # To include a separate docs.zip uncomment the extra_files config and the docs.zip command hook above. 79 | # extra_files: 80 | # - glob: ./docs.zip 81 | 82 | changelog: 83 | disable: true 84 | -------------------------------------------------------------------------------- /.web-docs/README.md: -------------------------------------------------------------------------------- 1 | 2 | The `Nutanix` multi-component plugin can be used with HashiCorp [Packer](https://www.packer.io) 3 | to create custom images. 4 | 5 | ### Installation 6 | 7 | To install this plugin, copy and paste this code into your Packer configuration, then run [`packer init`](https://www.packer.io/docs/commands/init). 8 | 9 | ``` 10 | packer { 11 | required_plugins { 12 | nutanix = { 13 | version = ">= 0.12.2" 14 | source = "github.com/nutanix-cloud-native/nutanix" 15 | } 16 | } 17 | } 18 | ``` 19 | 20 | Alternatively, you can use `packer plugins install` to manage installation of this plugin. 21 | 22 | ```sh 23 | $ packer plugins install github.com/nutanix-cloud-native/nutanix 24 | ``` 25 | 26 | ### Components 27 | 28 | #### Builders 29 | 30 | - [nutanix](/packer/integrations/nutanix-cloud-native/nutanix/latest/components/builder/nutanix) - The Nutanix builder will create a temporary VM as foundation of your Packer image, apply all providers you define to customize your image, then clone the VM disk image as your final Packer image. 31 | 32 | ### Limitations 33 | #### Building temporary ISOs on MacOS 34 | If you want to use the cd_files Option to create an additional iso-image for kickstart-files or similar be aware that MacOS won´t create a suitable file. 35 | Please install xorriso for support on MacOS. 36 | ``` 37 | brew install xorriso 38 | ``` 39 | 40 | ### Contributing 41 | See the [contributing docs](https://github.com/nutanix-cloud-native/packer-plugin-nutanix/blob/main/CONTRIBUTING.md). 42 | 43 | ### Support 44 | #### Community Plus 45 | 46 | This code is developed in the open with input from the community through issues and PRs. A Nutanix engineering team serves as the maintainer. Documentation is available in the project repository. 47 | 48 | Issues and enhancement requests can be submitted in the [Issues tab of this repository](https://github.com/nutanix-cloud-native/packer-plugin-nutanix/issues). Please search for and review the existing open issues before submitting a new issue. 49 | 50 | ### License 51 | The project is released under version 2.0 of the [Apache license](http://www.apache.org/licenses/LICENSE-2.0). 52 | 53 | -------------------------------------------------------------------------------- /.web-docs/components/builder/nutanix/README.md: -------------------------------------------------------------------------------- 1 | This document is going to detail all Nutanix plugin parameters. 2 | 3 | ## Principle 4 | The Nutanix plugin will create a temporary VM as foundation of your Packer image, apply all providers you define to customize your image, then clone the VM disk image as your final Packer image. 5 | 6 | ## Environment configuration 7 | These parameters allow to define information about platform and temporary VM used to create the image. 8 | 9 | ### Required 10 | - `nutanix_username` (string) - User used for Prism Central login. 11 | - `nutanix_password` (string) - Password of this user for Prism Central login. 12 | - `nutanix_endpoint` (string) - Prism Central FQDN or IP. 13 | - `cluster_name` or `cluster_uuid` (string) - Nutanix cluster name or uuid used to create and store image. 14 | - `os_type` (string) - OS Type ("Linux" or "Windows"). 15 | 16 | ### Optional 17 | - `nutanix_port` (number) - Port used for connection to Prism Central. 18 | - `nutanix_insecure` (bool) - Authorize connection to Prism Central without valid certificate. 19 | - `vm_name` (string) - Name of the temporary VM to create. If not specified a random `packer-*` name will be used. 20 | - `cpu` (number) - Number of vCPU for temporary VM. 21 | - `memory_mb` (number) - Size of vRAM for temporary VM (in megabytes). 22 | - `cd_files` (array of strings) - A list of files to place onto a CD that is attached when the VM is booted. This can include either files or directories; any directories will be copied onto the CD recursively, preserving directory structure hierarchy. 23 | - `cd_label` (string) - Label of this CD Drive. 24 | - `boot_type` (string) - Type of boot used on the temporary VM ("legacy" or "uefi", default is "legacy"). 25 | - `boot_priority` (string) - Priority of boot device ("cdrom" or "disk", default is "cdrom". UEFI support need AHV 8.0.12+, 9.1.1.2+, 9.1.3+, 9.2+ or 10.0+). 26 | - `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. 27 | - `vm_categories` ([]Category) - Assign Categories to the vm. 28 | - `project` (string) - Assign Project to the vm. 29 | - `gpu` ([] GPU) - GPU in cluster name to be attached on temporary VM. 30 | - `serialport` (bool) - Add a serial port to the temporary VM. This is required for some Linux Cloud Images that will have a kernel panic if a serial port is not present on first boot. 31 | 32 | ## Output configuration 33 | These parameters allow to configure everything around image creation, from the temporary VM connection to the final image definition. 34 | 35 | ### All OS 36 | - `image_name` (string) - Name of the output image. 37 | - `image_description` (string) - Description for output image. 38 | - `image_categories` ([]Category) - Assign Categories to the image. 39 | - `force_deregister` (bool) - Allow output image override if already exists. 40 | - `image_delete` (bool) - Delete image once build process is completed (default is false). 41 | - `image_export` (bool) - Export raw image in the current folder (default is false). 42 | - `shutdown_command` (string) - Command line to shutdown your temporary VM. 43 | - `shutdown_timeout` (string) - Timeout for VM shutdown (format : 2m). 44 | - `vm_force_delete` (bool) - Delete vm even if build is not succesful (default is false). 45 | - `communicator` (string) - Protocol used for Packer connection (ex "winrm" or "ssh"). Default is : "ssh". 46 | 47 | ### Dedicated to Linux 48 | - `user_data` (string) - cloud-init content base64 encoded. 49 | - `ssh_username` (string) - user for ssh connection initiated by Packer. 50 | - `ssh_password` (string) - password for the ssh user. 51 | 52 | ### Dedicated to Windows 53 | - `winrm_port` (number) - Port for WinRM communication (default is 5986). 54 | - `winrm_insecure` (bool) - Allow insecure connection to WinRM. 55 | - `winrm_use_ssl` (bool) - Request SSL connection with WinRM. 56 | - `winrm_timeout` (string) - Timeout for WinRM (format 45m). 57 | - `winrm_username` (string) - User login for WinRM connection. 58 | - `winrm_password` (string) - Password this User. 59 | 60 | ## Disk configuration 61 | Use `vm_disks{}` entry to configure disk to your VM image. If you want to configure several disks, use this entry multiple times. 62 | 63 | All parameters of this `vm_disks` section are described below. 64 | 65 | 3 types of disk configurations can be used: 66 | - disk (create an empty disk) 67 | - disk image (create disk from Nutanix image library) 68 | - ISO image (create disk from ISO image) 69 | 70 | ### Disk 71 | - `image_type` (string) - "DISK". 72 | - `disk_size_gb` (number) - size of th disk (in gigabytes). 73 | 74 | Sample: 75 | ```hcl 76 | vm_disks { 77 | image_type = "DISK" 78 | disk_size_gb = 30 79 | } 80 | ``` 81 | 82 | ### Disk image 83 | - `image_type` (string) - "DISK_IMAGE" (you must use one of the three following parameters to source the image). 84 | - `source_image_name` (string) - Name of the image used as disk source. 85 | - `source_image_uuid` (string) - UUID of the image used as disk source. 86 | - `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). 87 | - `source_image_checksum` (string) - Checksum of the image used as disk source (work only with `source_image_uri` and if image is not already present in the library). 88 | - `source_image_checksum_type` (string) - Type of checksum used for `source_image_checksum` (`sha256` or `sha1` ). 89 | - `source_image_delete` (bool) - Delete source image once build process is completed (default is false). 90 | - `source_image_force` (bool) - Always download and replace source image even if already exist (default is false). 91 | - `disk_size_gb` (number) - size of the disk (in gigabytes). 92 | 93 | Sample: 94 | ```hcl 95 | vm_disks { 96 | image_type = "DISK_IMAGE" 97 | source_image_name = "" 98 | disk_size_gb = 40 99 | } 100 | ``` 101 | ### ISO Image 102 | - `image_type` (string) - "ISO_IMAGE". 103 | - `source_image_name` (string) - Name of the ISO image to mount. 104 | - `source_image_uuid` (string) - UUID of the ISO image to mount. 105 | - `source_image_delete` (bool) - Delete source image once build process is completed (default is false). 106 | - `source_image_force` (bool) - Always download and replace source image even if already exist (default is false). 107 | 108 | Sample: 109 | ```hcl 110 | vm_disks { 111 | image_type = "ISO_IMAGE" 112 | source_image_name = "" 113 | } 114 | ``` 115 | 116 | ## Network Configuration 117 | Use `vm_nics{}` entry to configure NICs in your image 118 | 119 | In this section, you have to define network you will to connect with one of this keyword : 120 | 121 | - `subnet_name` (string) - Name of the cluster subnet to use. 122 | - `subnet_uuid` (string) - UUID of the cluster subnet to use. 123 | 124 | Sample 125 | ```hcl 126 | vm_nics { 127 | subnet_name = "" 128 | } 129 | ``` 130 | 131 | ## Categories Configuration 132 | 133 | Use `image_categories{}` and `vm_categories{}` to assign category to your image or vm. If you want to assign multiple categories , use the entry multiple times. 134 | 135 | In this section, you have to define category you will to assign with the following parameters: 136 | 137 | - `key` (string) - Name of the category to assign. 138 | - `value` (string) - Value of the category to assign. 139 | 140 | Sample 141 | ```hcl 142 | image_categories { 143 | key = "OSType" 144 | value = "ubuntu-22.04" 145 | } 146 | ``` 147 | 148 | Note: Categories must already be present in Prism Central. 149 | 150 | ## GPU Configuration 151 | 152 | Use `GPU` to assign a GPU that is present on `cluster-name` on the temporary vm. Add the name of the GPU you wish to attach. 153 | 154 | Sample 155 | 156 | ```hcl 157 | gpu { 158 | name = "Ampere 40" 159 | } 160 | ``` 161 | 162 | ## Samples 163 | 164 | You can find samples [here](https://github.com/nutanix-cloud-native/packer-plugin-nutanix/tree/main/example) for these instructions usage. 165 | -------------------------------------------------------------------------------- /.web-docs/metadata.hcl: -------------------------------------------------------------------------------- 1 | # For full specification on the configuration of this file visit: 2 | # https://github.com/hashicorp/integration-template#metadata-configuration 3 | integration { 4 | name = "Nutanix" 5 | description = "A multi-component plugin can be used with Packer to create custom images." 6 | identifier = "packer/nutanix-cloud-native/nutanix" 7 | component { 8 | type = "builder" 9 | name = "Nutanix plugin" 10 | slug = "nutanix" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.web-docs/scripts/compile-to-webdocs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Converts the folder name that the component documentation file 4 | # is stored in into the integration slug of the component. 5 | componentTypeFromFolderName() { 6 | if [[ "$1" = "builders" ]]; then 7 | echo "builder" 8 | elif [[ "$1" = "provisioners" ]]; then 9 | echo "provisioner" 10 | elif [[ "$1" = "post-processors" ]]; then 11 | echo "post-processor" 12 | elif [[ "$1" = "datasources" ]]; then 13 | echo "data-source" 14 | else 15 | echo "" 16 | fi 17 | } 18 | 19 | # $1: The content to adjust links 20 | # $2: The organization of the integration 21 | rewriteLinks() { 22 | local result="$1" 23 | local organization="$2" 24 | 25 | urlSegment="([^/]+)" 26 | urlAnchor="(#[^/]+)" 27 | 28 | # Rewrite Component Index Page links to the Integration root page. 29 | # 30 | # (\1) (\2) (\3) 31 | # /packer/plugins/datasources/amazon#anchor-tag--> 32 | # /packer/integrations/hashicorp/amazon#anchor-tag 33 | local find="\(\/packer\/plugins\/$urlSegment\/$urlSegment$urlAnchor?\)" 34 | local replace="\(\/packer\/integrations\/$organization\/\2\3\)" 35 | result="$(echo "$result" | sed -E "s/$find/$replace/g")" 36 | 37 | 38 | # Rewrite Component links to the Integration component page 39 | # 40 | # (\1) (\2) (\3) (\4) 41 | # /packer/plugins/datasources/amazon/parameterstore#anchor-tag --> 42 | # /packer/integrations/{organization}/amazon/latest/components/datasources/parameterstore 43 | local find="\(\/packer\/plugins\/$urlSegment\/$urlSegment\/$urlSegment$urlAnchor?\)" 44 | local replace="\(\/packer\/integrations\/$organization\/\2\/latest\/components\/\1\/\3\4\)" 45 | result="$(echo "$result" | sed -E "s/$find/$replace/g")" 46 | 47 | # Rewrite the Component URL segment from the Packer Plugin format 48 | # to the Integrations format 49 | result="$(echo "$result" \ 50 | | sed "s/\/datasources\//\/data-source\//g" \ 51 | | sed "s/\/builders\//\/builder\//g" \ 52 | | sed "s/\/post-processors\//\/post-processor\//g" \ 53 | | sed "s/\/provisioners\//\/provisioner\//g" \ 54 | )" 55 | 56 | echo "$result" 57 | } 58 | 59 | # $1: Docs Dir 60 | # $2: Web Docs Dir 61 | # $3: Component File 62 | # $4: The org of the integration 63 | processComponentFile() { 64 | local docsDir="$1" 65 | local webDocsDir="$2" 66 | local componentFile="$3" 67 | 68 | local escapedDocsDir="$(echo "$docsDir" | sed 's/\//\\\//g' | sed 's/\./\\\./g')" 69 | local componentTypeAndSlug="$(echo "$componentFile" | sed "s/$escapedDocsDir\///g" | sed 's/\.mdx//g')" 70 | 71 | # Parse out the Component Slug & Component Type 72 | local componentSlug="$(echo "$componentTypeAndSlug" | cut -d'/' -f 2)" 73 | local componentType="$(componentTypeFromFolderName "$(echo "$componentTypeAndSlug" | cut -d'/' -f 1)")" 74 | if [[ "$componentType" = "" ]]; then 75 | echo "Failed to process '$componentFile', unexpected folder name." 76 | echo "Documentation for components must be stored in one of:" 77 | echo "builders, provisioners, post-processors, datasources" 78 | exit 1 79 | fi 80 | 81 | 82 | # Calculate the location of where this file will ultimately go 83 | local webDocsFolder="$webDocsDir/components/$componentType/$componentSlug" 84 | mkdir -p "$webDocsFolder" 85 | local webDocsFile="$webDocsFolder/README.md" 86 | local webDocsFileTmp="$webDocsFolder/README.md.tmp" 87 | 88 | # Copy over the file to its webDocsFile location 89 | cp "$componentFile" "$webDocsFile" 90 | 91 | # Remove the Header 92 | local lastMetadataLine="$(grep -n -m 2 '^\-\-\-' "$componentFile" | tail -n1 | cut -d':' -f1)" 93 | cat "$webDocsFile" | tail -n +"$(($lastMetadataLine+2))" > "$webDocsFileTmp" 94 | mv "$webDocsFileTmp" "$webDocsFile" 95 | 96 | # Remove the top H1, as this will be added automatically on the web 97 | cat "$webDocsFile" | tail -n +3 > "$webDocsFileTmp" 98 | mv "$webDocsFileTmp" "$webDocsFile" 99 | 100 | # Rewrite Links 101 | rewriteLinks "$(cat "$webDocsFile")" "$4" > "$webDocsFileTmp" 102 | mv "$webDocsFileTmp" "$webDocsFile" 103 | } 104 | 105 | # Compiles the Packer SDC compiled docs folder down 106 | # to a integrations-compliant folder (web docs) 107 | # 108 | # $1: The directory of the plugin 109 | # $2: The directory of the SDC compiled docs files 110 | # $3: The output directory to place the web-docs files 111 | # $4: The org of the integration 112 | compileWebDocs() { 113 | local docsDir="$1/$2" 114 | local webDocsDir="$1/$3" 115 | 116 | echo "Compiling MDX docs in '$2' to Markdown in '$3'..." 117 | # Create the web-docs directory if it hasn't already been created 118 | mkdir -p "$webDocsDir" 119 | 120 | # Copy the README over 121 | cp "$docsDir/README.md" "$webDocsDir/README.md" 122 | 123 | # Process all MDX component files (exclude index files, which are unsupported) 124 | for file in $(find "$docsDir" | grep "$docsDir/.*/.*\.mdx" | grep --invert-match "index.mdx"); do 125 | processComponentFile "$docsDir" "$webDocsDir" "$file" "$4" 126 | done 127 | } 128 | 129 | compileWebDocs "$1" "$2" "$3" "$4" 130 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are always welcome. Before contributing please search the [issue tracker](../../issues). Your issue may have already been discussed or fixed in `main`. To contribute, [fork](https://help.github.com/articles/fork-a-repo/) this repository, commit your changes, and [send a Pull Request](https://help.github.com/articles/using-pull-requests/). 4 | 5 | ## Feature requests 6 | 7 | Feature requests should be submitted in the [issue tracker](../../issues), with a description of the expected behavior & use case, where they'll remain closed until sufficient interest, [e.g. :+1: reactions](https://help.github.com/articles/about-discussions-in-issues-and-pull-requests/), has been [shown by the community](../../issues?q=label%3A%22votes+needed%22+sort%3Areactions-%2B1-desc). 8 | 9 | Before submitting an issue, please search for similar ones in the [closed issues](../../issues?q=is%3Aissue+is%3Aclosed+label%3Aenhancement). 10 | 11 | ## Pull requests 12 | 13 | ### Approval and release process 14 | 15 | Pull requests approvals go through the following steps: 16 | 17 | 1. A GitHub action may be triggered to lint and test. 18 | 2. A maintainer reviews the changes. Any change requires at least one review. 19 | 3. The pull request can be merged when at least one maintainer approves it. 20 | 21 | ## Contributor License Agreement 22 | 23 | Before you submit your pull request, you'll need to sign the [Nutanix Contributor License Agreement (CLA)](https://www.nutanix.dev/cla/). The CLA must be agreed to by all contributors who are not Nutanix Full-time Employees (FTE) or interns prior to the contribution being merged into the project codebase. The CLA is substantially similar to the Apache Contributor License Agreement, which is the industry standard CLA. 24 | 25 | For more information about CLAs, please check out Alex Russell's excellent post, 26 | ["Why Do I Need to Sign This?"](https://infrequently.org/2008/06/why-do-i-need-to-sign-this/). -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME=nutanix 2 | BINARY=packer-plugin-${NAME} 3 | PLUGIN_FQN="$(shell grep -E '^module' 0 { 293 | return warnings, errs 294 | } 295 | 296 | return warnings, nil 297 | } 298 | -------------------------------------------------------------------------------- /builder/nutanix/config.hcl2spec.go: -------------------------------------------------------------------------------- 1 | // Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT. 2 | 3 | package nutanix 4 | 5 | import ( 6 | "github.com/hashicorp/hcl/v2/hcldec" 7 | "github.com/zclconf/go-cty/cty" 8 | ) 9 | 10 | // FlatCategory is an auto-generated flat version of Category. 11 | // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. 12 | type FlatCategory struct { 13 | Key *string `mapstructure:"key" json:"key" required:"false" cty:"key" hcl:"key"` 14 | Value *string `mapstructure:"value" json:"value" required:"false" cty:"value" hcl:"value"` 15 | } 16 | 17 | // FlatMapstructure returns a new FlatCategory. 18 | // FlatCategory is an auto-generated flat version of Category. 19 | // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. 20 | func (*Category) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { 21 | return new(FlatCategory) 22 | } 23 | 24 | // HCL2Spec returns the hcl spec of a Category. 25 | // This spec is used by HCL to read the fields of Category. 26 | // The decoded values from this spec will then be applied to a FlatCategory. 27 | func (*FlatCategory) HCL2Spec() map[string]hcldec.Spec { 28 | s := map[string]hcldec.Spec{ 29 | "key": &hcldec.AttrSpec{Name: "key", Type: cty.String, Required: false}, 30 | "value": &hcldec.AttrSpec{Name: "value", Type: cty.String, Required: false}, 31 | } 32 | return s 33 | } 34 | 35 | // FlatClusterConfig is an auto-generated flat version of ClusterConfig. 36 | // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. 37 | type FlatClusterConfig struct { 38 | Username *string `mapstructure:"nutanix_username" required:"false" cty:"nutanix_username" hcl:"nutanix_username"` 39 | Password *string `mapstructure:"nutanix_password" required:"false" cty:"nutanix_password" hcl:"nutanix_password"` 40 | Insecure *bool `mapstructure:"nutanix_insecure" required:"false" cty:"nutanix_insecure" hcl:"nutanix_insecure"` 41 | Endpoint *string `mapstructure:"nutanix_endpoint" required:"true" cty:"nutanix_endpoint" hcl:"nutanix_endpoint"` 42 | Port *int32 `mapstructure:"nutanix_port" required:"false" cty:"nutanix_port" hcl:"nutanix_port"` 43 | } 44 | 45 | // FlatMapstructure returns a new FlatClusterConfig. 46 | // FlatClusterConfig is an auto-generated flat version of ClusterConfig. 47 | // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. 48 | func (*ClusterConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { 49 | return new(FlatClusterConfig) 50 | } 51 | 52 | // HCL2Spec returns the hcl spec of a ClusterConfig. 53 | // This spec is used by HCL to read the fields of ClusterConfig. 54 | // The decoded values from this spec will then be applied to a FlatClusterConfig. 55 | func (*FlatClusterConfig) HCL2Spec() map[string]hcldec.Spec { 56 | s := map[string]hcldec.Spec{ 57 | "nutanix_username": &hcldec.AttrSpec{Name: "nutanix_username", Type: cty.String, Required: false}, 58 | "nutanix_password": &hcldec.AttrSpec{Name: "nutanix_password", Type: cty.String, Required: false}, 59 | "nutanix_insecure": &hcldec.AttrSpec{Name: "nutanix_insecure", Type: cty.Bool, Required: false}, 60 | "nutanix_endpoint": &hcldec.AttrSpec{Name: "nutanix_endpoint", Type: cty.String, Required: false}, 61 | "nutanix_port": &hcldec.AttrSpec{Name: "nutanix_port", Type: cty.Number, Required: false}, 62 | } 63 | return s 64 | } 65 | 66 | // FlatConfig is an auto-generated flat version of Config. 67 | // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. 68 | type FlatConfig struct { 69 | PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` 70 | PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` 71 | PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` 72 | PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` 73 | PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` 74 | PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` 75 | PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` 76 | PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` 77 | Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"` 78 | PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"` 79 | SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"` 80 | SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"` 81 | SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"` 82 | SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"` 83 | SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"` 84 | SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"` 85 | SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"` 86 | SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"` 87 | SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"` 88 | SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"` 89 | SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"` 90 | SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"` 91 | SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"` 92 | SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"` 93 | SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"` 94 | SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"` 95 | SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"` 96 | SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"` 97 | SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"` 98 | SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"` 99 | SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"` 100 | SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"` 101 | SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"` 102 | SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"` 103 | SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"` 104 | SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"` 105 | SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"` 106 | SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"` 107 | SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"` 108 | SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"` 109 | SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"` 110 | SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"` 111 | SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"` 112 | SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"` 113 | SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"` 114 | SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"` 115 | SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"` 116 | SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"` 117 | WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"` 118 | WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"` 119 | WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"` 120 | WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"` 121 | WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"` 122 | WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"` 123 | WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"` 124 | WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` 125 | WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` 126 | CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"` 127 | CDContent map[string]string `mapstructure:"cd_content" cty:"cd_content" hcl:"cd_content"` 128 | CDLabel *string `mapstructure:"cd_label" cty:"cd_label" hcl:"cd_label"` 129 | ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command" hcl:"shutdown_command"` 130 | ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout" hcl:"shutdown_timeout"` 131 | Username *string `mapstructure:"nutanix_username" required:"false" cty:"nutanix_username" hcl:"nutanix_username"` 132 | Password *string `mapstructure:"nutanix_password" required:"false" cty:"nutanix_password" hcl:"nutanix_password"` 133 | Insecure *bool `mapstructure:"nutanix_insecure" required:"false" cty:"nutanix_insecure" hcl:"nutanix_insecure"` 134 | Endpoint *string `mapstructure:"nutanix_endpoint" required:"true" cty:"nutanix_endpoint" hcl:"nutanix_endpoint"` 135 | Port *int32 `mapstructure:"nutanix_port" required:"false" cty:"nutanix_port" hcl:"nutanix_port"` 136 | VMName *string `mapstructure:"vm_name" json:"vm_name" required:"false" cty:"vm_name" hcl:"vm_name"` 137 | OSType *string `mapstructure:"os_type" json:"os_type" required:"true" cty:"os_type" hcl:"os_type"` 138 | BootType *string `mapstructure:"boot_type" json:"boot_type" required:"false" cty:"boot_type" hcl:"boot_type"` 139 | BootPriority *string `mapstructure:"boot_priority" json:"boot_priority" required:"false" cty:"boot_priority" hcl:"boot_priority"` 140 | VmDisks []FlatVmDisk `mapstructure:"vm_disks" cty:"vm_disks" hcl:"vm_disks"` 141 | VmNICs []FlatVmNIC `mapstructure:"vm_nics" cty:"vm_nics" hcl:"vm_nics"` 142 | ImageName *string `mapstructure:"image_name" json:"image_name" required:"false" cty:"image_name" hcl:"image_name"` 143 | ClusterUUID *string `mapstructure:"cluster_uuid" json:"cluster_uuid" required:"false" cty:"cluster_uuid" hcl:"cluster_uuid"` 144 | ClusterName *string `mapstructure:"cluster_name" json:"cluster_name" required:"false" cty:"cluster_name" hcl:"cluster_name"` 145 | CPU *int64 `mapstructure:"cpu" json:"cpu" required:"false" cty:"cpu" hcl:"cpu"` 146 | MemoryMB *int64 `mapstructure:"memory_mb" json:"memory_mb" required:"false" cty:"memory_mb" hcl:"memory_mb"` 147 | UserData *string `mapstructure:"user_data" json:"user_data" required:"false" cty:"user_data" hcl:"user_data"` 148 | VMCategories []FlatCategory `mapstructure:"vm_categories" required:"false" cty:"vm_categories" hcl:"vm_categories"` 149 | Project *string `mapstructure:"project" required:"false" cty:"project" hcl:"project"` 150 | GPU []FlatGPU `mapstructure:"gpu" required:"false" cty:"gpu" hcl:"gpu"` 151 | SerialPort *bool `mapstructure:"serialport" json:"serialport" required:"false" cty:"serialport" hcl:"serialport"` 152 | ForceDeregister *bool `mapstructure:"force_deregister" json:"force_deregister" required:"false" cty:"force_deregister" hcl:"force_deregister"` 153 | ImageDescription *string `mapstructure:"image_description" json:"image_description" required:"false" cty:"image_description" hcl:"image_description"` 154 | ImageCategories []FlatCategory `mapstructure:"image_categories" required:"false" cty:"image_categories" hcl:"image_categories"` 155 | ImageDelete *bool `mapstructure:"image_delete" json:"image_delete" required:"false" cty:"image_delete" hcl:"image_delete"` 156 | ImageExport *bool `mapstructure:"image_export" json:"image_export" required:"false" cty:"image_export" hcl:"image_export"` 157 | WaitTimeout *string `mapstructure:"ip_wait_timeout" json:"ip_wait_timeout" required:"false" cty:"ip_wait_timeout" hcl:"ip_wait_timeout"` 158 | VmForceDelete *bool `mapstructure:"vm_force_delete" json:"vm_force_delete" required:"false" cty:"vm_force_delete" hcl:"vm_force_delete"` 159 | } 160 | 161 | // FlatMapstructure returns a new FlatConfig. 162 | // FlatConfig is an auto-generated flat version of Config. 163 | // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. 164 | func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { 165 | return new(FlatConfig) 166 | } 167 | 168 | // HCL2Spec returns the hcl spec of a Config. 169 | // This spec is used by HCL to read the fields of Config. 170 | // The decoded values from this spec will then be applied to a FlatConfig. 171 | func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { 172 | s := map[string]hcldec.Spec{ 173 | "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, 174 | "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, 175 | "packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false}, 176 | "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, 177 | "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, 178 | "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, 179 | "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, 180 | "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, 181 | "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, 182 | "pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false}, 183 | "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, 184 | "ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false}, 185 | "ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false}, 186 | "ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false}, 187 | "ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false}, 188 | "temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false}, 189 | "temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false}, 190 | "temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false}, 191 | "ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false}, 192 | "ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false}, 193 | "ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false}, 194 | "ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false}, 195 | "ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false}, 196 | "ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false}, 197 | "ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false}, 198 | "ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false}, 199 | "ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false}, 200 | "ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false}, 201 | "ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false}, 202 | "ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false}, 203 | "ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false}, 204 | "ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false}, 205 | "ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false}, 206 | "ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false}, 207 | "ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false}, 208 | "ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false}, 209 | "ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false}, 210 | "ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false}, 211 | "ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false}, 212 | "ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false}, 213 | "ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false}, 214 | "ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false}, 215 | "ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false}, 216 | "ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false}, 217 | "ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false}, 218 | "ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false}, 219 | "ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false}, 220 | "ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false}, 221 | "winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false}, 222 | "winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false}, 223 | "winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false}, 224 | "winrm_no_proxy": &hcldec.AttrSpec{Name: "winrm_no_proxy", Type: cty.Bool, Required: false}, 225 | "winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false}, 226 | "winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false}, 227 | "winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false}, 228 | "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, 229 | "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, 230 | "cd_files": &hcldec.AttrSpec{Name: "cd_files", Type: cty.List(cty.String), Required: false}, 231 | "cd_content": &hcldec.AttrSpec{Name: "cd_content", Type: cty.Map(cty.String), Required: false}, 232 | "cd_label": &hcldec.AttrSpec{Name: "cd_label", Type: cty.String, Required: false}, 233 | "shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false}, 234 | "shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false}, 235 | "nutanix_username": &hcldec.AttrSpec{Name: "nutanix_username", Type: cty.String, Required: false}, 236 | "nutanix_password": &hcldec.AttrSpec{Name: "nutanix_password", Type: cty.String, Required: false}, 237 | "nutanix_insecure": &hcldec.AttrSpec{Name: "nutanix_insecure", Type: cty.Bool, Required: false}, 238 | "nutanix_endpoint": &hcldec.AttrSpec{Name: "nutanix_endpoint", Type: cty.String, Required: false}, 239 | "nutanix_port": &hcldec.AttrSpec{Name: "nutanix_port", Type: cty.Number, Required: false}, 240 | "vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false}, 241 | "os_type": &hcldec.AttrSpec{Name: "os_type", Type: cty.String, Required: false}, 242 | "boot_type": &hcldec.AttrSpec{Name: "boot_type", Type: cty.String, Required: false}, 243 | "boot_priority": &hcldec.AttrSpec{Name: "boot_priority", Type: cty.String, Required: false}, 244 | "vm_disks": &hcldec.BlockListSpec{TypeName: "vm_disks", Nested: hcldec.ObjectSpec((*FlatVmDisk)(nil).HCL2Spec())}, 245 | "vm_nics": &hcldec.BlockListSpec{TypeName: "vm_nics", Nested: hcldec.ObjectSpec((*FlatVmNIC)(nil).HCL2Spec())}, 246 | "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, 247 | "cluster_uuid": &hcldec.AttrSpec{Name: "cluster_uuid", Type: cty.String, Required: false}, 248 | "cluster_name": &hcldec.AttrSpec{Name: "cluster_name", Type: cty.String, Required: false}, 249 | "cpu": &hcldec.AttrSpec{Name: "cpu", Type: cty.Number, Required: false}, 250 | "memory_mb": &hcldec.AttrSpec{Name: "memory_mb", Type: cty.Number, Required: false}, 251 | "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, 252 | "vm_categories": &hcldec.BlockListSpec{TypeName: "vm_categories", Nested: hcldec.ObjectSpec((*FlatCategory)(nil).HCL2Spec())}, 253 | "project": &hcldec.AttrSpec{Name: "project", Type: cty.String, Required: false}, 254 | "gpu": &hcldec.BlockListSpec{TypeName: "gpu", Nested: hcldec.ObjectSpec((*FlatGPU)(nil).HCL2Spec())}, 255 | "serialport": &hcldec.AttrSpec{Name: "serialport", Type: cty.Bool, Required: false}, 256 | "force_deregister": &hcldec.AttrSpec{Name: "force_deregister", Type: cty.Bool, Required: false}, 257 | "image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false}, 258 | "image_categories": &hcldec.BlockListSpec{TypeName: "image_categories", Nested: hcldec.ObjectSpec((*FlatCategory)(nil).HCL2Spec())}, 259 | "image_delete": &hcldec.AttrSpec{Name: "image_delete", Type: cty.Bool, Required: false}, 260 | "image_export": &hcldec.AttrSpec{Name: "image_export", Type: cty.Bool, Required: false}, 261 | "ip_wait_timeout": &hcldec.AttrSpec{Name: "ip_wait_timeout", Type: cty.String, Required: false}, 262 | "vm_force_delete": &hcldec.AttrSpec{Name: "vm_force_delete", Type: cty.Bool, Required: false}, 263 | } 264 | return s 265 | } 266 | 267 | // FlatGPU is an auto-generated flat version of GPU. 268 | // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. 269 | type FlatGPU struct { 270 | Name *string `mapstructure:"name" json:"name" required:"false" cty:"name" hcl:"name"` 271 | } 272 | 273 | // FlatMapstructure returns a new FlatGPU. 274 | // FlatGPU is an auto-generated flat version of GPU. 275 | // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. 276 | func (*GPU) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { 277 | return new(FlatGPU) 278 | } 279 | 280 | // HCL2Spec returns the hcl spec of a GPU. 281 | // This spec is used by HCL to read the fields of GPU. 282 | // The decoded values from this spec will then be applied to a FlatGPU. 283 | func (*FlatGPU) HCL2Spec() map[string]hcldec.Spec { 284 | s := map[string]hcldec.Spec{ 285 | "name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false}, 286 | } 287 | return s 288 | } 289 | 290 | // FlatVmConfig is an auto-generated flat version of VmConfig. 291 | // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. 292 | type FlatVmConfig struct { 293 | VMName *string `mapstructure:"vm_name" json:"vm_name" required:"false" cty:"vm_name" hcl:"vm_name"` 294 | OSType *string `mapstructure:"os_type" json:"os_type" required:"true" cty:"os_type" hcl:"os_type"` 295 | BootType *string `mapstructure:"boot_type" json:"boot_type" required:"false" cty:"boot_type" hcl:"boot_type"` 296 | BootPriority *string `mapstructure:"boot_priority" json:"boot_priority" required:"false" cty:"boot_priority" hcl:"boot_priority"` 297 | VmDisks []FlatVmDisk `mapstructure:"vm_disks" cty:"vm_disks" hcl:"vm_disks"` 298 | VmNICs []FlatVmNIC `mapstructure:"vm_nics" cty:"vm_nics" hcl:"vm_nics"` 299 | ImageName *string `mapstructure:"image_name" json:"image_name" required:"false" cty:"image_name" hcl:"image_name"` 300 | ClusterUUID *string `mapstructure:"cluster_uuid" json:"cluster_uuid" required:"false" cty:"cluster_uuid" hcl:"cluster_uuid"` 301 | ClusterName *string `mapstructure:"cluster_name" json:"cluster_name" required:"false" cty:"cluster_name" hcl:"cluster_name"` 302 | CPU *int64 `mapstructure:"cpu" json:"cpu" required:"false" cty:"cpu" hcl:"cpu"` 303 | MemoryMB *int64 `mapstructure:"memory_mb" json:"memory_mb" required:"false" cty:"memory_mb" hcl:"memory_mb"` 304 | UserData *string `mapstructure:"user_data" json:"user_data" required:"false" cty:"user_data" hcl:"user_data"` 305 | VMCategories []FlatCategory `mapstructure:"vm_categories" required:"false" cty:"vm_categories" hcl:"vm_categories"` 306 | Project *string `mapstructure:"project" required:"false" cty:"project" hcl:"project"` 307 | GPU []FlatGPU `mapstructure:"gpu" required:"false" cty:"gpu" hcl:"gpu"` 308 | SerialPort *bool `mapstructure:"serialport" json:"serialport" required:"false" cty:"serialport" hcl:"serialport"` 309 | } 310 | 311 | // FlatMapstructure returns a new FlatVmConfig. 312 | // FlatVmConfig is an auto-generated flat version of VmConfig. 313 | // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. 314 | func (*VmConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { 315 | return new(FlatVmConfig) 316 | } 317 | 318 | // HCL2Spec returns the hcl spec of a VmConfig. 319 | // This spec is used by HCL to read the fields of VmConfig. 320 | // The decoded values from this spec will then be applied to a FlatVmConfig. 321 | func (*FlatVmConfig) HCL2Spec() map[string]hcldec.Spec { 322 | s := map[string]hcldec.Spec{ 323 | "vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false}, 324 | "os_type": &hcldec.AttrSpec{Name: "os_type", Type: cty.String, Required: false}, 325 | "boot_type": &hcldec.AttrSpec{Name: "boot_type", Type: cty.String, Required: false}, 326 | "boot_priority": &hcldec.AttrSpec{Name: "boot_priority", Type: cty.String, Required: false}, 327 | "vm_disks": &hcldec.BlockListSpec{TypeName: "vm_disks", Nested: hcldec.ObjectSpec((*FlatVmDisk)(nil).HCL2Spec())}, 328 | "vm_nics": &hcldec.BlockListSpec{TypeName: "vm_nics", Nested: hcldec.ObjectSpec((*FlatVmNIC)(nil).HCL2Spec())}, 329 | "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, 330 | "cluster_uuid": &hcldec.AttrSpec{Name: "cluster_uuid", Type: cty.String, Required: false}, 331 | "cluster_name": &hcldec.AttrSpec{Name: "cluster_name", Type: cty.String, Required: false}, 332 | "cpu": &hcldec.AttrSpec{Name: "cpu", Type: cty.Number, Required: false}, 333 | "memory_mb": &hcldec.AttrSpec{Name: "memory_mb", Type: cty.Number, Required: false}, 334 | "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, 335 | "vm_categories": &hcldec.BlockListSpec{TypeName: "vm_categories", Nested: hcldec.ObjectSpec((*FlatCategory)(nil).HCL2Spec())}, 336 | "project": &hcldec.AttrSpec{Name: "project", Type: cty.String, Required: false}, 337 | "gpu": &hcldec.BlockListSpec{TypeName: "gpu", Nested: hcldec.ObjectSpec((*FlatGPU)(nil).HCL2Spec())}, 338 | "serialport": &hcldec.AttrSpec{Name: "serialport", Type: cty.Bool, Required: false}, 339 | } 340 | return s 341 | } 342 | 343 | // FlatVmDisk is an auto-generated flat version of VmDisk. 344 | // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. 345 | type FlatVmDisk struct { 346 | ImageType *string `mapstructure:"image_type" json:"image_type" required:"false" cty:"image_type" hcl:"image_type"` 347 | SourceImageName *string `mapstructure:"source_image_name" json:"source_image_name" required:"false" cty:"source_image_name" hcl:"source_image_name"` 348 | SourceImageUUID *string `mapstructure:"source_image_uuid" json:"source_image_uuid" required:"false" cty:"source_image_uuid" hcl:"source_image_uuid"` 349 | SourceImageURI *string `mapstructure:"source_image_uri" json:"source_image_uri" required:"false" cty:"source_image_uri" hcl:"source_image_uri"` 350 | SourceImageChecksum *string `mapstructure:"source_image_checksum" json:"source_image_checksum" required:"false" cty:"source_image_checksum" hcl:"source_image_checksum"` 351 | SourceImageChecksumType *string `mapstructure:"source_image_checksum_type" json:"source_image_checksum_type" required:"false" cty:"source_image_checksum_type" hcl:"source_image_checksum_type"` 352 | SourceImageDelete *bool `mapstructure:"source_image_delete" json:"source_image_delete" required:"false" cty:"source_image_delete" hcl:"source_image_delete"` 353 | SourceImageForce *bool `mapstructure:"source_image_force" json:"source_image_force" required:"false" cty:"source_image_force" hcl:"source_image_force"` 354 | DiskSizeGB *int64 `mapstructure:"disk_size_gb" json:"disk_size_gb" required:"false" cty:"disk_size_gb" hcl:"disk_size_gb"` 355 | } 356 | 357 | // FlatMapstructure returns a new FlatVmDisk. 358 | // FlatVmDisk is an auto-generated flat version of VmDisk. 359 | // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. 360 | func (*VmDisk) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { 361 | return new(FlatVmDisk) 362 | } 363 | 364 | // HCL2Spec returns the hcl spec of a VmDisk. 365 | // This spec is used by HCL to read the fields of VmDisk. 366 | // The decoded values from this spec will then be applied to a FlatVmDisk. 367 | func (*FlatVmDisk) HCL2Spec() map[string]hcldec.Spec { 368 | s := map[string]hcldec.Spec{ 369 | "image_type": &hcldec.AttrSpec{Name: "image_type", Type: cty.String, Required: false}, 370 | "source_image_name": &hcldec.AttrSpec{Name: "source_image_name", Type: cty.String, Required: false}, 371 | "source_image_uuid": &hcldec.AttrSpec{Name: "source_image_uuid", Type: cty.String, Required: false}, 372 | "source_image_uri": &hcldec.AttrSpec{Name: "source_image_uri", Type: cty.String, Required: false}, 373 | "source_image_checksum": &hcldec.AttrSpec{Name: "source_image_checksum", Type: cty.String, Required: false}, 374 | "source_image_checksum_type": &hcldec.AttrSpec{Name: "source_image_checksum_type", Type: cty.String, Required: false}, 375 | "source_image_delete": &hcldec.AttrSpec{Name: "source_image_delete", Type: cty.Bool, Required: false}, 376 | "source_image_force": &hcldec.AttrSpec{Name: "source_image_force", Type: cty.Bool, Required: false}, 377 | "disk_size_gb": &hcldec.AttrSpec{Name: "disk_size_gb", Type: cty.Number, Required: false}, 378 | } 379 | return s 380 | } 381 | 382 | // FlatVmNIC is an auto-generated flat version of VmNIC. 383 | // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. 384 | type FlatVmNIC struct { 385 | SubnetName *string `mapstructure:"subnet_name" json:"subnet_name" required:"false" cty:"subnet_name" hcl:"subnet_name"` 386 | SubnetUUID *string `mapstructure:"subnet_uuid" json:"subnet_uuid" required:"false" cty:"subnet_uuid" hcl:"subnet_uuid"` 387 | } 388 | 389 | // FlatMapstructure returns a new FlatVmNIC. 390 | // FlatVmNIC is an auto-generated flat version of VmNIC. 391 | // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. 392 | func (*VmNIC) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { 393 | return new(FlatVmNIC) 394 | } 395 | 396 | // HCL2Spec returns the hcl spec of a VmNIC. 397 | // This spec is used by HCL to read the fields of VmNIC. 398 | // The decoded values from this spec will then be applied to a FlatVmNIC. 399 | func (*FlatVmNIC) HCL2Spec() map[string]hcldec.Spec { 400 | s := map[string]hcldec.Spec{ 401 | "subnet_name": &hcldec.AttrSpec{Name: "subnet_name", Type: cty.String, Required: false}, 402 | "subnet_uuid": &hcldec.AttrSpec{Name: "subnet_uuid", Type: cty.String, Required: false}, 403 | } 404 | return s 405 | } 406 | -------------------------------------------------------------------------------- /builder/nutanix/pointers.go: -------------------------------------------------------------------------------- 1 | package nutanix 2 | 3 | import "time" 4 | 5 | // StringPtr returns a pointer to the string value passed in. 6 | func StringPtr(v string) *string { 7 | return &v 8 | } 9 | 10 | // StringValue returns the value of the string pointer passed in or 11 | // "" if the pointer is nil. 12 | func StringValue(v *string) string { 13 | if v != nil { 14 | return *v 15 | } 16 | 17 | return "" 18 | } 19 | 20 | // StringSlice converts a slice of string values into a slice of 21 | // string pointers 22 | func StringSlice(src []string) []*string { 23 | dst := make([]*string, len(src)) 24 | for i := 0; i < len(src); i++ { 25 | dst[i] = &(src[i]) 26 | } 27 | 28 | return dst 29 | } 30 | 31 | // StringValueSlice converts a slice of string pointers into a slice of 32 | // string values 33 | func StringValueSlice(src []*string) []string { 34 | dst := make([]string, len(src)) 35 | 36 | for i := 0; i < len(src); i++ { 37 | if src[i] != nil { 38 | dst[i] = *(src[i]) 39 | } 40 | } 41 | 42 | return dst 43 | } 44 | 45 | // StringMap converts a string map of string values into a string 46 | // map of string pointers 47 | func StringMap(src map[string]string) map[string]*string { 48 | dst := make(map[string]*string) 49 | 50 | for k, val := range src { 51 | v := val 52 | dst[k] = &v 53 | } 54 | 55 | return dst 56 | } 57 | 58 | // StringValueMap converts a string map of string pointers into a string 59 | // map of string values 60 | func StringValueMap(src map[string]*string) map[string]string { 61 | dst := make(map[string]string) 62 | 63 | for k, val := range src { 64 | if val != nil { 65 | dst[k] = *val 66 | } 67 | } 68 | 69 | return dst 70 | } 71 | 72 | // BoolPtr returns a pointer to the bool value passed in. 73 | func BoolPtr(v bool) *bool { 74 | return &v 75 | } 76 | 77 | // BoolValue returns the value of the bool pointer passed in or 78 | // false if the pointer is nil. 79 | func BoolValue(v *bool) bool { 80 | if v != nil { 81 | return *v 82 | } 83 | 84 | return false 85 | } 86 | 87 | // BoolSlice converts a slice of bool values into a slice of 88 | // bool pointers 89 | func BoolSlice(src []bool) []*bool { 90 | dst := make([]*bool, len(src)) 91 | 92 | for i := 0; i < len(src); i++ { 93 | dst[i] = &(src[i]) 94 | } 95 | 96 | return dst 97 | } 98 | 99 | // BoolValueSlice converts a slice of bool pointers into a slice of 100 | // bool values 101 | func BoolValueSlice(src []*bool) []bool { 102 | dst := make([]bool, len(src)) 103 | 104 | for i := 0; i < len(src); i++ { 105 | if src[i] != nil { 106 | dst[i] = *(src[i]) 107 | } 108 | } 109 | 110 | return dst 111 | } 112 | 113 | // BoolMap converts a string map of bool values into a string 114 | // map of bool pointers 115 | func BoolMap(src map[string]bool) map[string]*bool { 116 | dst := make(map[string]*bool) 117 | 118 | for k, val := range src { 119 | v := val 120 | dst[k] = &v 121 | } 122 | 123 | return dst 124 | } 125 | 126 | // BoolValueMap converts a string map of bool pointers into a string 127 | // map of bool values 128 | func BoolValueMap(src map[string]*bool) map[string]bool { 129 | dst := make(map[string]bool) 130 | 131 | for k, val := range src { 132 | if val != nil { 133 | dst[k] = *val 134 | } 135 | } 136 | 137 | return dst 138 | } 139 | 140 | // IntPtr returns a pointer to the int value passed in. 141 | func IntPtr(v int) *int { 142 | return &v 143 | } 144 | 145 | // IntValue returns the value of the int pointer passed in or 146 | // 0 if the pointer is nil. 147 | func IntValue(v *int) int { 148 | if v != nil { 149 | return *v 150 | } 151 | 152 | return 0 153 | } 154 | 155 | // IntSlice converts a slice of int values into a slice of 156 | // int pointers 157 | func IntSlice(src []int) []*int { 158 | dst := make([]*int, len(src)) 159 | 160 | for i := 0; i < len(src); i++ { 161 | dst[i] = &(src[i]) 162 | } 163 | 164 | return dst 165 | } 166 | 167 | // IntValueSlice converts a slice of int pointers into a slice of 168 | // int values 169 | func IntValueSlice(src []*int) []int { 170 | dst := make([]int, len(src)) 171 | 172 | for i := 0; i < len(src); i++ { 173 | if src[i] != nil { 174 | dst[i] = *(src[i]) 175 | } 176 | } 177 | 178 | return dst 179 | } 180 | 181 | // IntMap converts a string map of int values into a string 182 | // map of int pointers 183 | func IntMap(src map[string]int) map[string]*int { 184 | dst := make(map[string]*int) 185 | 186 | for k, val := range src { 187 | v := val 188 | dst[k] = &v 189 | } 190 | 191 | return dst 192 | } 193 | 194 | // IntValueMap converts a string map of int pointers into a string 195 | // map of int values 196 | func IntValueMap(src map[string]*int) map[string]int { 197 | dst := make(map[string]int) 198 | 199 | for k, val := range src { 200 | if val != nil { 201 | dst[k] = *val 202 | } 203 | } 204 | 205 | return dst 206 | } 207 | 208 | // Int64Ptr returns a pointer to the int64 value passed in. 209 | func Int64Ptr(v int64) *int64 { 210 | return &v 211 | } 212 | 213 | // Int64Value returns the value of the int64 pointer passed in or 214 | // 0 if the pointer is nil. 215 | func Int64Value(v *int64) int64 { 216 | if v != nil { 217 | return *v 218 | } 219 | 220 | return 0 221 | } 222 | 223 | // Int64Slice converts a slice of int64 values into a slice of 224 | // int64 pointers 225 | func Int64Slice(src []int64) []*int64 { 226 | dst := make([]*int64, len(src)) 227 | 228 | for i := 0; i < len(src); i++ { 229 | dst[i] = &(src[i]) 230 | } 231 | 232 | return dst 233 | } 234 | 235 | // Int64ValueSlice converts a slice of int64 pointers into a slice of 236 | // int64 values 237 | func Int64ValueSlice(src []*int64) []int64 { 238 | dst := make([]int64, len(src)) 239 | 240 | for i := 0; i < len(src); i++ { 241 | if src[i] != nil { 242 | dst[i] = *(src[i]) 243 | } 244 | } 245 | 246 | return dst 247 | } 248 | 249 | // Int64Map converts a string map of int64 values into a string 250 | // map of int64 pointers 251 | func Int64Map(src map[string]int64) map[string]*int64 { 252 | dst := make(map[string]*int64) 253 | 254 | for k, val := range src { 255 | v := val 256 | dst[k] = &v 257 | } 258 | 259 | return dst 260 | } 261 | 262 | // Int64ValueMap converts a string map of int64 pointers into a string 263 | // map of int64 values 264 | func Int64ValueMap(src map[string]*int64) map[string]int64 { 265 | dst := make(map[string]int64) 266 | 267 | for k, val := range src { 268 | if val != nil { 269 | dst[k] = *val 270 | } 271 | } 272 | 273 | return dst 274 | } 275 | 276 | // Float64Ptr returns a pointer to the float64 value passed in. 277 | func Float64Ptr(v float64) *float64 { 278 | return &v 279 | } 280 | 281 | // Float64Value returns the value of the float64 pointer passed in or 282 | // 0 if the pointer is nil. 283 | func Float64Value(v *float64) float64 { 284 | if v != nil { 285 | return *v 286 | } 287 | 288 | return 0 289 | } 290 | 291 | // Float64Slice converts a slice of float64 values into a slice of 292 | // float64 pointers 293 | func Float64Slice(src []float64) []*float64 { 294 | dst := make([]*float64, len(src)) 295 | 296 | for i := 0; i < len(src); i++ { 297 | dst[i] = &(src[i]) 298 | } 299 | 300 | return dst 301 | } 302 | 303 | // Float64ValueSlice converts a slice of float64 pointers into a slice of 304 | // float64 values 305 | func Float64ValueSlice(src []*float64) []float64 { 306 | dst := make([]float64, len(src)) 307 | 308 | for i := 0; i < len(src); i++ { 309 | if src[i] != nil { 310 | dst[i] = *(src[i]) 311 | } 312 | } 313 | 314 | return dst 315 | } 316 | 317 | // Float64Map converts a string map of float64 values into a string 318 | // map of float64 pointers 319 | func Float64Map(src map[string]float64) map[string]*float64 { 320 | dst := make(map[string]*float64) 321 | 322 | for k, val := range src { 323 | v := val 324 | dst[k] = &v 325 | } 326 | 327 | return dst 328 | } 329 | 330 | // Float64ValueMap converts a string map of float64 pointers into a string 331 | // map of float64 values 332 | func Float64ValueMap(src map[string]*float64) map[string]float64 { 333 | dst := make(map[string]float64) 334 | 335 | for k, val := range src { 336 | if val != nil { 337 | dst[k] = *val 338 | } 339 | } 340 | 341 | return dst 342 | } 343 | 344 | // Time returns a pointer to the time.Time value passed in. 345 | func Time(v time.Time) *time.Time { 346 | return &v 347 | } 348 | 349 | // TimeValue returns the value of the time.Time pointer passed in or 350 | // time.Time{} if the pointer is nil. 351 | func TimeValue(v *time.Time) time.Time { 352 | if v != nil { 353 | return *v 354 | } 355 | 356 | return time.Time{} 357 | } 358 | 359 | // SecondsTimeValue converts an int64 pointer to a time.Time value 360 | // representing seconds since Epoch or time.Time{} if the pointer is nil. 361 | func SecondsTimeValue(v *int64) time.Time { 362 | if v != nil { 363 | return time.Unix((*v / 1000), 0) 364 | } 365 | 366 | return time.Time{} 367 | } 368 | 369 | // MillisecondsTimeValue converts an int64 pointer to a time.Time value 370 | // representing milliseconds sinch Epoch or time.Time{} if the pointer is nil. 371 | func MillisecondsTimeValue(v *int64) time.Time { 372 | if v != nil { 373 | return time.Unix(0, (*v * 1000000)) 374 | } 375 | 376 | return time.Time{} 377 | } 378 | 379 | // TimeUnixMilli returns a Unix timestamp in milliseconds from "January 1, 1970 UTC". 380 | // The result is undefined if the Unix time cannot be represented by an int64. 381 | // Which includes calling TimeUnixMilli on a zero Time is undefined. 382 | // 383 | // This utility is useful for service API's such as CloudWatch Logs which require 384 | // their unix time values to be in milliseconds. 385 | // 386 | // See Go stdlib https://golang.org/pkg/time/#Time.UnixNano for more information. 387 | func TimeUnixMilli(t time.Time) int64 { 388 | return t.UnixNano() / int64(time.Millisecond/time.Nanosecond) 389 | } 390 | 391 | // TimeSlice converts a slice of time.Time values into a slice of 392 | // time.Time pointers 393 | func TimeSlice(src []time.Time) []*time.Time { 394 | dst := make([]*time.Time, len(src)) 395 | 396 | for i := 0; i < len(src); i++ { 397 | dst[i] = &(src[i]) 398 | } 399 | 400 | return dst 401 | } 402 | 403 | // TimeValueSlice converts a slice of time.Time pointers into a slice of 404 | // time.Time values 405 | func TimeValueSlice(src []*time.Time) []time.Time { 406 | dst := make([]time.Time, len(src)) 407 | 408 | for i := 0; i < len(src); i++ { 409 | if src[i] != nil { 410 | dst[i] = *(src[i]) 411 | } 412 | } 413 | 414 | return dst 415 | } 416 | 417 | // TimeMap converts a string map of time.Time values into a string 418 | // map of time.Time pointers 419 | func TimeMap(src map[string]time.Time) map[string]*time.Time { 420 | dst := make(map[string]*time.Time) 421 | 422 | for k, val := range src { 423 | v := val 424 | dst[k] = &v 425 | } 426 | 427 | return dst 428 | } 429 | 430 | // TimeValueMap converts a string map of time.Time pointers into a string 431 | // map of time.Time values 432 | func TimeValueMap(src map[string]*time.Time) map[string]time.Time { 433 | dst := make(map[string]time.Time) 434 | 435 | for k, val := range src { 436 | if val != nil { 437 | dst[k] = *val 438 | } 439 | } 440 | 441 | return dst 442 | } 443 | -------------------------------------------------------------------------------- /builder/nutanix/ssh.go: -------------------------------------------------------------------------------- 1 | package nutanix 2 | 3 | import ( 4 | "github.com/hashicorp/packer-plugin-sdk/multistep" 5 | ) 6 | 7 | func commHost() func(multistep.StateBag) (string, error) { 8 | return func(state multistep.StateBag) (string, error) { 9 | if guestAddress, ok := state.Get("ip").(string); ok { 10 | return guestAddress, nil 11 | } 12 | 13 | return "127.0.0.1", nil 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /builder/nutanix/step_build_vm.go: -------------------------------------------------------------------------------- 1 | package nutanix 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | 8 | "github.com/hashicorp/packer-plugin-sdk/multistep" 9 | "github.com/hashicorp/packer-plugin-sdk/packer" 10 | ) 11 | 12 | // stepBuildVM is the default struct which contains the step's information 13 | type stepBuildVM struct { 14 | } 15 | 16 | // Run is the primary function to build the image 17 | func (s *stepBuildVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { 18 | //Update UI 19 | ui := state.Get("ui").(packer.Ui) 20 | d := state.Get("driver").(Driver) 21 | config := state.Get("config").(*Config) 22 | 23 | // Determine if we even have a cd_files disk to attach 24 | log.Println("check for CD disk to attach") 25 | if cdPathRaw, ok := state.GetOk("cd_path"); ok { 26 | ui.Say("Uploading CD disk...") 27 | cdFilesPath := cdPathRaw.(string) 28 | log.Println("CD disk found " + cdFilesPath) 29 | cdfilesImage, err := d.CreateImageFile(ctx, cdFilesPath, config.VmConfig) 30 | if err != nil { 31 | ui.Error("Error uploading CD disk:" + err.Error()) 32 | state.Put("error", err) 33 | return multistep.ActionHalt 34 | } 35 | ui.Message("CD disk uploaded " + *cdfilesImage.image.Spec.Name) 36 | state.Put("cd_uuid", *cdfilesImage.image.Metadata.UUID) 37 | temp_cd := VmDisk{ 38 | ImageType: "ISO_IMAGE", 39 | SourceImageUUID: *cdfilesImage.image.Metadata.UUID, 40 | } 41 | config.VmConfig.VmDisks = append(config.VmConfig.VmDisks, temp_cd) 42 | } else { 43 | log.Println("no CD disk, not attaching.") 44 | } 45 | 46 | ui.Say("Creating Packer Builder virtual machine...") 47 | 48 | // Create VM Spec 49 | vmRequest, err := d.CreateRequest(ctx, config.VmConfig, state) 50 | if err != nil { 51 | ui.Error("Error creating virtual machine request: " + err.Error()) 52 | state.Put("error", err) 53 | return multistep.ActionHalt 54 | } 55 | 56 | // Create VM 57 | vmInstance, err := d.Create(ctx, vmRequest) 58 | 59 | if err != nil { 60 | ui.Error("Unable to create virtual machine: " + err.Error()) 61 | state.Put("error", err) 62 | return multistep.ActionHalt 63 | } 64 | 65 | ui.Message(fmt.Sprintf("Virtual machine %s created", config.VMName)) 66 | log.Printf("Nutanix VM UUID: %s", *vmInstance.nutanix.Metadata.UUID) 67 | state.Put("vm_uuid", *vmInstance.nutanix.Metadata.UUID) 68 | state.Put("ip", vmInstance.Addresses()[0]) 69 | state.Put("destroy_vm", true) 70 | ui.Message("Found IP for virtual machine: " + vmInstance.Addresses()[0]) 71 | 72 | return multistep.ActionContinue 73 | } 74 | 75 | // Cleanup will tear down the VM once the build is complete 76 | func (s *stepBuildVM) Cleanup(state multistep.StateBag) { 77 | vmUUID := state.Get("vm_uuid") 78 | if vmUUID == nil { 79 | return 80 | } 81 | 82 | ui := state.Get("ui").(packer.Ui) 83 | d := state.Get("driver").(Driver) 84 | config := state.Get("config").(*Config) 85 | ctx, ok := state.Get("ctx").(context.Context) 86 | if !ok { 87 | ctx = context.Background() 88 | } 89 | 90 | if cdUUID, ok := state.GetOk("cd_uuid"); ok { 91 | ui.Say("Deleting temporary CD disk...") 92 | err := d.DeleteImage(ctx, cdUUID.(string)) 93 | if err != nil { 94 | ui.Error("An error occurred while deleting CD disk") 95 | log.Println(err) 96 | } 97 | ui.Message("Temporary CD disk successfully deleted") 98 | } 99 | 100 | imageToDelete := state.Get("image_to_delete") 101 | 102 | for _, image := range imageToDelete.([]string) { 103 | ui.Say(fmt.Sprintf("Deleting marked source_image: %s...", image)) 104 | err := d.DeleteImage(ctx, image) 105 | if err != nil { 106 | ui.Error(fmt.Sprintf("An error occurred while deleting image %s", image)) 107 | log.Println(err) 108 | } 109 | ui.Message("Image successfully deleted") 110 | } 111 | 112 | _, cancelled := state.GetOk(multistep.StateCancelled) 113 | _, halted := state.GetOk(multistep.StateHalted) 114 | 115 | if cancelled || halted && !config.VmForceDelete { 116 | ui.Say("Task cancelled, virtual machine is not deleted") 117 | return 118 | } else if config.VmForceDelete && cancelled || halted { 119 | ui.Say("Force deleting virtual machine...") 120 | } else { 121 | ui.Say("Deleting virtual machine...") 122 | } 123 | 124 | err := d.Delete(ctx, vmUUID.(string)) 125 | if err != nil { 126 | ui.Error("An error occurred while deleting the Virtual machine") 127 | log.Println(err) 128 | } else { 129 | ui.Message("Virtual machine successfully deleted") 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /builder/nutanix/step_copy_image.go: -------------------------------------------------------------------------------- 1 | package nutanix 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | 8 | "github.com/hashicorp/packer-plugin-sdk/multistep" 9 | "github.com/hashicorp/packer-plugin-sdk/packer" 10 | ) 11 | 12 | type imageArtefact struct { 13 | uuid string 14 | size int64 15 | } 16 | 17 | type diskArtefact struct { 18 | uuid string 19 | size int64 20 | } 21 | 22 | type stepCopyImage struct { 23 | Config *Config 24 | } 25 | 26 | func (s *stepCopyImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { 27 | ui := state.Get("ui").(packer.Ui) 28 | vmUUID := state.Get("vm_uuid").(string) 29 | d := state.Get("driver").(Driver) 30 | vm, _ := d.GetVM(ctx, vmUUID) 31 | 32 | ui.Say(fmt.Sprintf("Creating image(s) from virtual machine %s...", s.Config.VMName)) 33 | 34 | // Choose disk to replicate - looking for first "DISK" 35 | var disksToCopy []diskArtefact 36 | 37 | for i := range vm.nutanix.Spec.Resources.DiskList { 38 | if *vm.nutanix.Spec.Resources.DiskList[i].DeviceProperties.DeviceType == "DISK" { 39 | disksToCopy = append(disksToCopy, diskArtefact{ 40 | uuid: *vm.nutanix.Spec.Resources.DiskList[i].UUID, 41 | size: *vm.nutanix.Spec.Resources.DiskList[i].DiskSizeBytes, 42 | }) 43 | diskID := fmt.Sprintf("%s:%d", *vm.nutanix.Spec.Resources.DiskList[i].DeviceProperties.DiskAddress.AdapterType, *vm.nutanix.Spec.Resources.DiskList[i].DeviceProperties.DiskAddress.DeviceIndex) 44 | ui.Message("Found disk to copy: " + diskID) 45 | } 46 | } 47 | 48 | if len(disksToCopy) == 0 { 49 | err := errors.New("no DISK was found to save, halting build") 50 | ui.Error(err.Error()) 51 | state.Put("error", err) 52 | return multistep.ActionHalt 53 | } 54 | 55 | var imageList []imageArtefact 56 | 57 | for i, diskToCopy := range disksToCopy { 58 | 59 | imageResponse, err := d.SaveVMDisk(ctx, diskToCopy.uuid, i, s.Config.ImageCategories) 60 | if err != nil { 61 | ui.Error("Image creation failed: " + err.Error()) 62 | state.Put("error", err) 63 | return multistep.ActionHalt 64 | } 65 | 66 | imageList = append(imageList, imageArtefact{ 67 | uuid: *imageResponse.image.Metadata.UUID, 68 | size: diskToCopy.size, 69 | }) 70 | 71 | ui.Message(fmt.Sprintf("Image successfully created: %s (%s)", *imageResponse.image.Spec.Name, *imageResponse.image.Metadata.UUID)) 72 | } 73 | 74 | state.Put("image_uuid", imageList) 75 | return multistep.ActionContinue 76 | } 77 | 78 | func (s *stepCopyImage) Cleanup(state multistep.StateBag) { 79 | ui := state.Get("ui").(packer.Ui) 80 | d := state.Get("driver").(Driver) 81 | ctx, ok := state.Get("ctx").(context.Context) 82 | if !ok { 83 | ctx = context.Background() 84 | } 85 | 86 | if !s.Config.ImageDelete { 87 | return 88 | } 89 | 90 | if imgUUID, ok := state.GetOk("image_uuid"); ok { 91 | ui.Say(fmt.Sprintf("Deleting image(s) %s...", s.Config.ImageName)) 92 | 93 | for _, image := range imgUUID.([]imageArtefact) { 94 | 95 | err := d.DeleteImage(ctx, image.uuid) 96 | if err != nil { 97 | ui.Error("An error occurred while deleting image") 98 | return 99 | } else { 100 | ui.Message(fmt.Sprintf("Image successfully deleted (%s)", image.uuid)) 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /builder/nutanix/step_export_image.go: -------------------------------------------------------------------------------- 1 | package nutanix 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "os" 8 | "os/signal" 9 | "syscall" 10 | 11 | "github.com/hashicorp/packer-plugin-sdk/multistep" 12 | "github.com/hashicorp/packer-plugin-sdk/packer" 13 | ) 14 | 15 | type stepExportImage struct { 16 | VMName string 17 | ImageName string 18 | } 19 | 20 | func (s *stepExportImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { 21 | ui := state.Get("ui").(packer.Ui) 22 | imageList := state.Get("image_uuid").([]imageArtefact) 23 | d := state.Get("driver").(Driver) 24 | // vm, _ := d.GetVM(vmUUID) 25 | 26 | ui.Say(fmt.Sprintf("Exporting image(s) from virtual machine %s...", s.VMName)) 27 | 28 | // Create a channel to receive signals 29 | sigChan := make(chan os.Signal, 1) 30 | signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) 31 | 32 | for index, imageToExport := range imageList { 33 | 34 | name := s.ImageName 35 | if index > 0 { 36 | name = fmt.Sprintf("%s-disk%d", name, index+1) 37 | } 38 | 39 | file, err := d.ExportImage(ctx, imageToExport.uuid) 40 | if err != nil { 41 | ui.Error("Image export failed: " + err.Error()) 42 | state.Put("error", err) 43 | return multistep.ActionHalt 44 | } 45 | defer file.Close() 46 | 47 | toRead := ui.TrackProgress(name, 0, imageToExport.size, file) 48 | 49 | tempDestinationPath := name + ".tmp" 50 | 51 | f, err := os.OpenFile(tempDestinationPath, os.O_CREATE|os.O_WRONLY, 0644) 52 | if err != nil { 53 | return multistep.ActionHalt 54 | } 55 | 56 | // Use a goroutine to copy the data, so that we can 57 | // interrupt it if necessary 58 | copyDone := make(chan bool) 59 | go func() { 60 | io.Copy(f, toRead) 61 | copyDone <- true 62 | }() 63 | 64 | select { 65 | case <-copyDone: 66 | toRead.Close() 67 | 68 | // Check if size is OK 69 | fi, err := f.Stat() 70 | if err != nil { 71 | ui.Error("Image stat failed: " + err.Error()) 72 | state.Put("error", err) 73 | return multistep.ActionHalt 74 | } 75 | 76 | if fi.Size() != imageToExport.size { 77 | os.Remove(tempDestinationPath) 78 | ui.Error("image size mistmatch") 79 | state.Put("error", fmt.Errorf("image size mistmatch")) 80 | return multistep.ActionHalt 81 | } 82 | 83 | name = name + ".img" 84 | os.Rename(tempDestinationPath, name) 85 | 86 | ui.Message(fmt.Sprintf("image %s exported", name)) 87 | 88 | case <-sigChan: 89 | // We received a signal, cancel the copy operation 90 | toRead.Close() 91 | f.Close() 92 | os.Remove(tempDestinationPath) 93 | ui.Message("image export cancelled") 94 | return multistep.ActionHalt 95 | } 96 | 97 | } 98 | return multistep.ActionContinue 99 | } 100 | 101 | func (s *stepExportImage) Cleanup(state multistep.StateBag) {} 102 | -------------------------------------------------------------------------------- /builder/nutanix/step_shutdown_vm.go: -------------------------------------------------------------------------------- 1 | package nutanix 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "log" 8 | "time" 9 | 10 | "github.com/hashicorp/packer-plugin-sdk/multistep" 11 | packersdk "github.com/hashicorp/packer-plugin-sdk/packer" 12 | ) 13 | 14 | // This step shuts down the machine. It first attempts to do so gracefully, 15 | // but ultimately forcefully shuts it down if that fails. 16 | // 17 | // Uses: 18 | // 19 | // communicator packersdk.Communicator 20 | // driver Driver 21 | // ui packersdk.Ui 22 | // vmName string 23 | // 24 | // Produces: 25 | // 26 | // 27 | type StepShutdown struct { 28 | Command string 29 | Timeout time.Duration 30 | } 31 | 32 | func (s *StepShutdown) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { 33 | comm := state.Get("communicator").(packersdk.Communicator) 34 | driver := state.Get("driver").(Driver) 35 | ui := state.Get("ui").(packersdk.Ui) 36 | config := state.Get("config").(*Config) 37 | vmUUID := state.Get("vm_uuid").(string) 38 | 39 | if config.CommConfig.Type == "none" { 40 | ui.Say("No Communicator configured, halting the virtual machine...") 41 | if err := driver.PowerOff(ctx, vmUUID); err != nil { 42 | err := fmt.Errorf("error stopping VM: %s", err) 43 | state.Put("error", err) 44 | ui.Error(err.Error()) 45 | return multistep.ActionHalt 46 | } 47 | 48 | } else if s.Command != "" { 49 | ui.Say("Gracefully halting virtual machine...") 50 | log.Printf("executing shutdown command: %s", s.Command) 51 | cmd := &packersdk.RemoteCmd{Command: s.Command} 52 | if err := cmd.RunWithUi(ctx, comm, ui); err != nil { 53 | err := fmt.Errorf("failed to send shutdown command: %s", err) 54 | state.Put("error", err) 55 | ui.Error(err.Error()) 56 | return multistep.ActionHalt 57 | } 58 | 59 | } else { 60 | ui.Say("Halting the virtual machine...") 61 | if err := driver.PowerOff(ctx, vmUUID); err != nil { 62 | err := fmt.Errorf("error stopping VM: %s", err) 63 | state.Put("error", err) 64 | ui.Error(err.Error()) 65 | return multistep.ActionHalt 66 | } 67 | } 68 | 69 | // Wait for the machine to actually shut down 70 | log.Printf("waiting max %s for shutdown to complete", s.Timeout) 71 | shutdownTimer := time.After(s.Timeout) 72 | for { 73 | running, _ := driver.GetVM(ctx, vmUUID) 74 | if running.PowerState() == "OFF" { 75 | log.Printf("VM powered off") 76 | break 77 | } 78 | 79 | select { 80 | case <-shutdownTimer: 81 | err := errors.New("timeout while waiting for machine to shutdown") 82 | state.Put("error", err) 83 | ui.Error(err.Error()) 84 | return multistep.ActionHalt 85 | default: 86 | time.Sleep(15 * time.Second) 87 | } 88 | } 89 | 90 | log.Println("VM shut down.") 91 | return multistep.ActionContinue 92 | } 93 | 94 | func (s *StepShutdown) Cleanup(state multistep.StateBag) {} 95 | -------------------------------------------------------------------------------- /builder/nutanix/utils.go: -------------------------------------------------------------------------------- 1 | package nutanix 2 | 3 | import ( 4 | v3 "github.com/nutanix-cloud-native/prism-go-client/v3" 5 | ) 6 | 7 | // BuildReference create reference from defined object 8 | func BuildReference(uuid, kind string) *v3.Reference { 9 | return &v3.Reference{ 10 | Kind: StringPtr(kind), 11 | UUID: StringPtr(uuid), 12 | } 13 | } 14 | 15 | // BuildReferenceValue create referencevalue from defined object 16 | func BuildReferenceValue(uuid, kind string) *v3.ReferenceValues { 17 | return &v3.ReferenceValues{ 18 | Kind: kind, 19 | UUID: uuid, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 2 | The `Nutanix` multi-component plugin can be used with HashiCorp [Packer](https://www.packer.io) 3 | to create custom images. 4 | 5 | ### Installation 6 | 7 | To install this plugin, copy and paste this code into your Packer configuration, then run [`packer init`](https://www.packer.io/docs/commands/init). 8 | 9 | ``` 10 | packer { 11 | required_plugins { 12 | nutanix = { 13 | version = ">= 0.12.2" 14 | source = "github.com/nutanix-cloud-native/nutanix" 15 | } 16 | } 17 | } 18 | ``` 19 | 20 | Alternatively, you can use `packer plugins install` to manage installation of this plugin. 21 | 22 | ```sh 23 | $ packer plugins install github.com/nutanix-cloud-native/nutanix 24 | ``` 25 | 26 | ### Components 27 | 28 | #### Builders 29 | 30 | - [nutanix](/packer/integrations/nutanix-cloud-native/nutanix/latest/components/builder/nutanix) - The Nutanix builder will create a temporary VM as foundation of your Packer image, apply all providers you define to customize your image, then clone the VM disk image as your final Packer image. 31 | 32 | ### Limitations 33 | #### Building temporary ISOs on MacOS 34 | If you want to use the cd_files Option to create an additional iso-image for kickstart-files or similar be aware that MacOS won´t create a suitable file. 35 | Please install xorriso for support on MacOS. 36 | ``` 37 | brew install xorriso 38 | ``` 39 | 40 | ### Contributing 41 | See the [contributing docs](https://github.com/nutanix-cloud-native/packer-plugin-nutanix/blob/main/CONTRIBUTING.md). 42 | 43 | ### Support 44 | #### Community Plus 45 | 46 | This code is developed in the open with input from the community through issues and PRs. A Nutanix engineering team serves as the maintainer. Documentation is available in the project repository. 47 | 48 | Issues and enhancement requests can be submitted in the [Issues tab of this repository](https://github.com/nutanix-cloud-native/packer-plugin-nutanix/issues). Please search for and review the existing open issues before submitting a new issue. 49 | 50 | ### License 51 | The project is released under version 2.0 of the [Apache license](http://www.apache.org/licenses/LICENSE-2.0). 52 | 53 | -------------------------------------------------------------------------------- /docs/builders/nutanix.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: > 3 | The Nutanix packer plugin allow to use Packer on Nutanix AHV platform. 4 | page_title: Nutanix plugin 5 | nav_title: Nutanix 6 | --- 7 | 8 | # Nutanix Builder 9 | 10 | This document is going to detail all Nutanix plugin parameters. 11 | 12 | ## Principle 13 | The Nutanix plugin will create a temporary VM as foundation of your Packer image, apply all providers you define to customize your image, then clone the VM disk image as your final Packer image. 14 | 15 | ## Environment configuration 16 | These parameters allow to define information about platform and temporary VM used to create the image. 17 | 18 | ### Required 19 | - `nutanix_username` (string) - User used for Prism Central login. 20 | - `nutanix_password` (string) - Password of this user for Prism Central login. 21 | - `nutanix_endpoint` (string) - Prism Central FQDN or IP. 22 | - `cluster_name` or `cluster_uuid` (string) - Nutanix cluster name or uuid used to create and store image. 23 | - `os_type` (string) - OS Type ("Linux" or "Windows"). 24 | 25 | ### Optional 26 | - `nutanix_port` (number) - Port used for connection to Prism Central. 27 | - `nutanix_insecure` (bool) - Authorize connection to Prism Central without valid certificate. 28 | - `vm_name` (string) - Name of the temporary VM to create. If not specified a random `packer-*` name will be used. 29 | - `cpu` (number) - Number of vCPU for temporary VM. 30 | - `memory_mb` (number) - Size of vRAM for temporary VM (in megabytes). 31 | - `cd_files` (array of strings) - A list of files to place onto a CD that is attached when the VM is booted. This can include either files or directories; any directories will be copied onto the CD recursively, preserving directory structure hierarchy. 32 | - `cd_label` (string) - Label of this CD Drive. 33 | - `boot_type` (string) - Type of boot used on the temporary VM ("legacy" or "uefi", default is "legacy"). 34 | - `boot_priority` (string) - Priority of boot device ("cdrom" or "disk", default is "cdrom". UEFI support need AHV 8.0.12+, 9.1.1.2+, 9.1.3+, 9.2+ or 10.0+). 35 | - `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. 36 | - `vm_categories` ([]Category) - Assign Categories to the vm. 37 | - `project` (string) - Assign Project to the vm. 38 | - `gpu` ([] GPU) - GPU in cluster name to be attached on temporary VM. 39 | - `serialport` (bool) - Add a serial port to the temporary VM. This is required for some Linux Cloud Images that will have a kernel panic if a serial port is not present on first boot. 40 | 41 | ## Output configuration 42 | These parameters allow to configure everything around image creation, from the temporary VM connection to the final image definition. 43 | 44 | ### All OS 45 | - `image_name` (string) - Name of the output image. 46 | - `image_description` (string) - Description for output image. 47 | - `image_categories` ([]Category) - Assign Categories to the image. 48 | - `force_deregister` (bool) - Allow output image override if already exists. 49 | - `image_delete` (bool) - Delete image once build process is completed (default is false). 50 | - `image_export` (bool) - Export raw image in the current folder (default is false). 51 | - `shutdown_command` (string) - Command line to shutdown your temporary VM. 52 | - `shutdown_timeout` (string) - Timeout for VM shutdown (format : 2m). 53 | - `vm_force_delete` (bool) - Delete vm even if build is not succesful (default is false). 54 | - `communicator` (string) - Protocol used for Packer connection (ex "winrm" or "ssh"). Default is : "ssh". 55 | 56 | ### Dedicated to Linux 57 | - `user_data` (string) - cloud-init content base64 encoded. 58 | - `ssh_username` (string) - user for ssh connection initiated by Packer. 59 | - `ssh_password` (string) - password for the ssh user. 60 | 61 | ### Dedicated to Windows 62 | - `winrm_port` (number) - Port for WinRM communication (default is 5986). 63 | - `winrm_insecure` (bool) - Allow insecure connection to WinRM. 64 | - `winrm_use_ssl` (bool) - Request SSL connection with WinRM. 65 | - `winrm_timeout` (string) - Timeout for WinRM (format 45m). 66 | - `winrm_username` (string) - User login for WinRM connection. 67 | - `winrm_password` (string) - Password this User. 68 | 69 | ## Disk configuration 70 | Use `vm_disks{}` entry to configure disk to your VM image. If you want to configure several disks, use this entry multiple times. 71 | 72 | All parameters of this `vm_disks` section are described below. 73 | 74 | 3 types of disk configurations can be used: 75 | - disk (create an empty disk) 76 | - disk image (create disk from Nutanix image library) 77 | - ISO image (create disk from ISO image) 78 | 79 | ### Disk 80 | - `image_type` (string) - "DISK". 81 | - `disk_size_gb` (number) - size of th disk (in gigabytes). 82 | 83 | Sample: 84 | ```hcl 85 | vm_disks { 86 | image_type = "DISK" 87 | disk_size_gb = 30 88 | } 89 | ``` 90 | 91 | ### Disk image 92 | - `image_type` (string) - "DISK_IMAGE" (you must use one of the three following parameters to source the image). 93 | - `source_image_name` (string) - Name of the image used as disk source. 94 | - `source_image_uuid` (string) - UUID of the image used as disk source. 95 | - `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). 96 | - `source_image_checksum` (string) - Checksum of the image used as disk source (work only with `source_image_uri` and if image is not already present in the library). 97 | - `source_image_checksum_type` (string) - Type of checksum used for `source_image_checksum` (`sha256` or `sha1` ). 98 | - `source_image_delete` (bool) - Delete source image once build process is completed (default is false). 99 | - `source_image_force` (bool) - Always download and replace source image even if already exist (default is false). 100 | - `disk_size_gb` (number) - size of the disk (in gigabytes). 101 | 102 | Sample: 103 | ```hcl 104 | vm_disks { 105 | image_type = "DISK_IMAGE" 106 | source_image_name = "" 107 | disk_size_gb = 40 108 | } 109 | ``` 110 | ### ISO Image 111 | - `image_type` (string) - "ISO_IMAGE". 112 | - `source_image_name` (string) - Name of the ISO image to mount. 113 | - `source_image_uuid` (string) - UUID of the ISO image to mount. 114 | - `source_image_delete` (bool) - Delete source image once build process is completed (default is false). 115 | - `source_image_force` (bool) - Always download and replace source image even if already exist (default is false). 116 | 117 | Sample: 118 | ```hcl 119 | vm_disks { 120 | image_type = "ISO_IMAGE" 121 | source_image_name = "" 122 | } 123 | ``` 124 | 125 | ## Network Configuration 126 | Use `vm_nics{}` entry to configure NICs in your image 127 | 128 | In this section, you have to define network you will to connect with one of this keyword : 129 | 130 | - `subnet_name` (string) - Name of the cluster subnet to use. 131 | - `subnet_uuid` (string) - UUID of the cluster subnet to use. 132 | 133 | Sample 134 | ```hcl 135 | vm_nics { 136 | subnet_name = "" 137 | } 138 | ``` 139 | 140 | ## Categories Configuration 141 | 142 | Use `image_categories{}` and `vm_categories{}` to assign category to your image or vm. If you want to assign multiple categories , use the entry multiple times. 143 | 144 | In this section, you have to define category you will to assign with the following parameters: 145 | 146 | - `key` (string) - Name of the category to assign. 147 | - `value` (string) - Value of the category to assign. 148 | 149 | Sample 150 | ```hcl 151 | image_categories { 152 | key = "OSType" 153 | value = "ubuntu-22.04" 154 | } 155 | ``` 156 | 157 | Note: Categories must already be present in Prism Central. 158 | 159 | ## GPU Configuration 160 | 161 | Use `GPU` to assign a GPU that is present on `cluster-name` on the temporary vm. Add the name of the GPU you wish to attach. 162 | 163 | Sample 164 | 165 | ```hcl 166 | gpu { 167 | name = "Ampere 40" 168 | } 169 | ``` 170 | 171 | ## Samples 172 | 173 | You can find samples [here](https://github.com/nutanix-cloud-native/packer-plugin-nutanix/tree/main/example) for these instructions usage. 174 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | ## Examples 2 | Validate Manifests: 3 | `packer validate .` 4 | 5 | Creating CentOS from local Image and running Provisioner: 6 | `packer build -only nutanix.centos .` 7 | 8 | Creating Ubuntu from Upstream Image and running Provisioner: 9 | `packer build -only nutanix.ubuntu .` 10 | 11 | Creating from ISO with Kickstart-File: 12 | `packer build -only nutanix.centos-kickstart .` 13 | 14 | Windows Image (ISO Boot, VirtIO Drivers, cd_files) 15 | `packer build -only nutanix.windows .` 16 | 17 | -------------------------------------------------------------------------------- /example/build.nutanix.pkr.hcl: -------------------------------------------------------------------------------- 1 | build { 2 | sources = [ 3 | "source.nutanix.centos" 4 | ] 5 | 6 | source "nutanix.ubuntu" { 7 | name = "ubuntu" 8 | } 9 | 10 | source "nutanix.centos-kickstart" { 11 | name = "centos-kickstart" 12 | } 13 | 14 | source "nutanix.windows" { 15 | name = "windows" 16 | } 17 | 18 | provisioner "shell" { 19 | only = ["nutanix.centos", "nutanix.centos-kickstart" ,"nutanix.ubuntu"] 20 | environment_vars = [ 21 | "FOO=hello world", 22 | ] 23 | inline = [ 24 | "echo \"FOO is $FOO\" > example.txt", 25 | ] 26 | } 27 | 28 | provisioner "powershell" { 29 | only = ["nutanix.windows"] 30 | scripts = ["scripts/win-update.ps1"] 31 | pause_before = "2m" 32 | } 33 | provisioner "windows-restart" { 34 | only = ["nutanix.windows"] 35 | restart_timeout = "30m" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /example/init/plugin.pkr.hcl: -------------------------------------------------------------------------------- 1 | packer { 2 | required_plugins { 3 | nutanix = { 4 | version = ">= 0.12.2" 5 | source = "github.com/nutanix-cloud-native/nutanix" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /example/scripts/cleanup.ps1: -------------------------------------------------------------------------------- 1 | Function Cleanup { 2 | 3 | Clear-Host 4 | 5 | ## Stops the windows update service. 6 | Get-Service -Name wuauserv | Stop-Service -Force -Verbose -ErrorAction SilentlyContinue 7 | 8 | ## Deletes the contents of windows software distribution. 9 | Get-ChildItem "C:\Windows\SoftwareDistribution\*" -Recurse -Force -Verbose -ErrorAction SilentlyContinue | Remove-Item -Force -Verbose -recurse -ErrorAction SilentlyContinue 10 | 11 | ## Deletes the contents of the Windows Temp folder. 12 | Get-ChildItem "C:\Windows\Temp\*" -Recurse -Force -Verbose -ErrorAction SilentlyContinue | Remove-Item -Force -Verbose -recurse -ErrorAction SilentlyContinue 13 | 14 | ## Delets all files and folders in user's Temp folder. 15 | Get-ChildItem "C:\users\*\AppData\Local\Temp\*" -Recurse -Force -ErrorAction SilentlyContinue | Remove-Item -Force -Verbose -recurse -ErrorAction SilentlyContinue 16 | 17 | ## Remove all files and folders in user's Temporary Internet Files. 18 | Get-ChildItem "C:\users\*\AppData\Local\Microsoft\Windows\Temporary Internet Files\*" -Recurse -Force -Verbose -ErrorAction SilentlyContinue | Remove-Item -Force -recurse -ErrorAction SilentlyContinue 19 | } 20 | 21 | Cleanup -------------------------------------------------------------------------------- /example/scripts/cloud-init/cloud-config-centos.yaml: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | users: 3 | - name: centos 4 | sudo: ['ALL=(ALL) NOPASSWD:ALL'] 5 | chpasswd: 6 | list: | 7 | centos:packer 8 | expire: False 9 | ssh_pwauth: True 10 | -------------------------------------------------------------------------------- /example/scripts/cloud-init/cloud-config-ubuntu.yaml: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | users: 3 | - name: builder 4 | sudo: ['ALL=(ALL) NOPASSWD:ALL'] 5 | chpasswd: 6 | list: | 7 | builder:packer 8 | expire: False 9 | ssh_pwauth: True 10 | -------------------------------------------------------------------------------- /example/scripts/gui/autounattend.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | en-US 8 | 9 | en-US 10 | en-US 11 | en-US 12 | en-US 13 | en-US 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Primary 22 | 1 23 | 499 24 | 25 | 26 | 2 27 | Primary 28 | true 29 | 30 | 31 | 32 | 33 | true 34 | NTFS 35 | 36 | 1 37 | 1 38 | 39 | 40 | NTFS 41 | 42 | C 43 | 2 44 | 2 45 | 46 | 47 | 0 48 | true 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | /IMAGE/INDEX 58 | 2 59 | 60 | 61 | 62 | 0 63 | 2 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | OnError 74 | 75 | true 76 | Packer 77 | Packer 78 | 79 | 80 | 81 | 82 | 83 | e:\Windows Server 2016\amd64 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | de-DE 92 | en-US 93 | en-US 94 | en-US 95 | de-DE 96 | 97 | 98 | 99 | packer-win2016 100 | Romance Standard Time 101 | 102 | 103 | 104 | true 105 | 106 | 107 | 108 | false 109 | true 110 | 111 | 112 | 113 | true 114 | 115 | 116 | 117 | true 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | packer 126 | true</PlainText> 127 | </Password> 128 | <Enabled>true</Enabled> 129 | <Username>Administrator</Username> 130 | </AutoLogon> 131 | <FirstLogonCommands> 132 | <SynchronousCommand wcm:action="add"> 133 | <CommandLine>%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command New-SelfSignedCertificate -CertstoreLocation Cert:\LocalMachine\My -DnsName "WinRMCertificate"</CommandLine> 134 | <Description>Certificate for WinRM</Description> 135 | <Order>1</Order> 136 | <RequiresUserInput>true</RequiresUserInput> 137 | </SynchronousCommand> 138 | <SynchronousCommand wcm:action="add"> 139 | <CommandLine>%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command Enable-PSRemoting -SkipNetworkProfileCheck -Force</CommandLine> 140 | <Description>Enable WinRM</Description> 141 | <Order>2</Order> 142 | <RequiresUserInput>true</RequiresUserInput> 143 | </SynchronousCommand> 144 | <SynchronousCommand wcm:action="add"> 145 | <CommandLine>%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command ($cert = gci Cert:\LocalMachine\My\) -and (New-Item -Path WSMan:\LocalHost\Listener -Transport HTTPS -Address * -CertificateThumbPrint $cert.Thumbprint –Force)</CommandLine> 146 | <Description>Add HTTPS WinRM listener with previously generated certificate</Description> 147 | <Order>3</Order> 148 | <RequiresUserInput>true</RequiresUserInput> 149 | </SynchronousCommand> 150 | <SynchronousCommand wcm:action="add"> 151 | <CommandLine>%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command New-NetFirewallRule -DisplayName 'Windows Remote Management (HTTPS-In)' -Name 'Windows Remote Management (HTTPS-In)' -Profile Any -LocalPort 5986 -Protocol TCP</CommandLine> 152 | <Description>Add firewall exception to TCP port 5986 for WinRM over HTTPS</Description> 153 | <Order>4</Order> 154 | <RequiresUserInput>true</RequiresUserInput> 155 | </SynchronousCommand> 156 | <SynchronousCommand wcm:action="add"> 157 | <CommandLine>%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command Set-Item WSMan:\localhost\Service\Auth\Basic -Value $true</CommandLine> 158 | <Description>Enable Basic authentication</Description> 159 | <Order>5</Order> 160 | <RequiresUserInput>true</RequiresUserInput> 161 | </SynchronousCommand> 162 | <SynchronousCommand wcm:action="add"> 163 | <CommandLine>%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command Stop-Service WinRM</CommandLine> 164 | <Description>Stop the WinRM service to allow the dism process to finish before packer executes scripts</Description> 165 | <Order>6</Order> 166 | <RequiresUserInput>true</RequiresUserInput> 167 | </SynchronousCommand> 168 | <SynchronousCommand wcm:action="add"> 169 | <CommandLine>%SystemRoot%\System32\reg.exe ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ /v HideFileExt /t REG_DWORD /d 0 /f</CommandLine> 170 | <Order>7</Order> 171 | <Description>Show file extensions in Explorer</Description> 172 | </SynchronousCommand> 173 | <SynchronousCommand wcm:action="add"> 174 | <CommandLine>%SystemRoot%\System32\reg.exe ADD HKLM\SYSTEM\CurrentControlSet\Control\Power\ /v HibernateFileSizePercent /t REG_DWORD /d 0 /f</CommandLine> 175 | <Order>8</Order> 176 | <Description>Zero Hibernation File</Description> 177 | </SynchronousCommand> 178 | <SynchronousCommand wcm:action="add"> 179 | <CommandLine>%SystemRoot%\System32\reg.exe ADD HKLM\SYSTEM\CurrentControlSet\Control\Power\ /v HibernateEnabled /t REG_DWORD /d 0 /f</CommandLine> 180 | <Order>9</Order> 181 | <Description>Disable Hibernation Mode</Description> 182 | </SynchronousCommand> 183 | <SynchronousCommand wcm:action="add"> 184 | <CommandLine>%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command dism /online /quiet /set-edition:ServerDatacenter /productkey:CB7KF-BWN84-R7R2Y-793K2-8XDDG /accepteula</CommandLine> 185 | <Order>10</Order> 186 | <Description>Switch from EVAL to VL</Description> 187 | </SynchronousCommand> 188 | </FirstLogonCommands> 189 | <OOBE> 190 | <HideEULAPage>true</HideEULAPage> 191 | <HideLocalAccountScreen>true</HideLocalAccountScreen> 192 | <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen> 193 | <HideOnlineAccountScreens>true</HideOnlineAccountScreens> 194 | <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> 195 | <NetworkLocation>Home</NetworkLocation> 196 | <ProtectYourPC>1</ProtectYourPC> 197 | </OOBE> 198 | <UserAccounts> 199 | <AdministratorPassword> 200 | <Value>packer</Value> 201 | <PlainText>true</PlainText> 202 | </AdministratorPassword> 203 | </UserAccounts> 204 | <RegisteredOwner /> 205 | </component> 206 | </settings> 207 | <settings pass="offlineServicing"> 208 | <component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-LUA-Settings" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> 209 | <!-- https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-lua-settings --> 210 | <EnableLUA>false</EnableLUA> 211 | </component> 212 | </settings> 213 | <cpi:offlineImage xmlns:cpi="urn:schemas-microsoft-com:cpi" cpi:source="" /> 214 | </unattend> -------------------------------------------------------------------------------- /example/scripts/ks.cfg: -------------------------------------------------------------------------------- 1 | # Author: Tomasz Filipiec <tomasz.filipiec@gmail.com> 2 | # Desc.: Simple kickstart file for automated installation 3 | # OS: CentOS 7 Minimal (core) 4 | # Version: 1.0 5 | 6 | # Turning on text-mode installation (little quicker than GUI) 7 | text 8 | 9 | # Setting up authentication and keyboard 10 | auth --enableshadow --passalgo=sha512 11 | keyboard --vckeymap=us --xlayouts='us' 12 | 13 | # Installation files source (CentOS-7.0-1406-x86_64-Minimal.iso) 14 | cdrom 15 | 16 | # Using only primary disk, ignoring others 17 | ignoredisk --only-use=sda 18 | 19 | # Setting up language to English 20 | lang en-US.UTF-8 21 | 22 | # Setting up network interface to DHCP 23 | network --bootproto=dhcp --ipv6=auto --hostname=centos-ks.local --activate 24 | 25 | # Root password (remember that plaintext only for information purposes) 26 | rootpw --plaintext packer 27 | 28 | # Setting up firewall and enabling SSH for remote management 29 | firewall --enabled --service=ssh 30 | 31 | # Setting timezone 32 | timezone Europe/Berlin --isUtc 33 | 34 | # Setting up Security-Enhanced Linux into enforcing 35 | selinux --enforcing 36 | 37 | # Setting up MBR 38 | bootloader --location=mbr --boot-drive=sda 39 | 40 | # Setting up Logical Volume Manager and autopartitioning 41 | clearpart --all --drives=sda --initlabel 42 | autopart --type=lvm 43 | 44 | # Eject cdrom and reboot 45 | reboot --eject 46 | 47 | # Installing only packages for minimal install 48 | %packages 49 | @Core 50 | chrony 51 | %end -------------------------------------------------------------------------------- /example/scripts/win-update.ps1: -------------------------------------------------------------------------------- 1 | # Source : https://github.com/hashicorp/best-practices/blob/master/packer/scripts/windows/install_windows_updates.ps1 2 | # Silence progress bars in PowerShell, which can sometimes feed back strange 3 | # XML data to the Packer output. 4 | $ProgressPreference = "SilentlyContinue" 5 | 6 | Write-Output "Starting PSWindowsUpdate Installation" 7 | ## ACTUAL NOT WORKING SCRIPT 8 | # # Install PSWindowsUpdate for scriptable Windows Updates 9 | # $webDeployURL = "https://gallery.technet.microsoft.com/scriptcenter/2d191bcd-3308-4edd-9de2-88dff796b0bc/file/66095/1/PSWindowsUpdate_1.4.5.zip" 10 | # $filePath = "$($env:TEMP)\PSWindowsUpdate.zip" 11 | 12 | # (New-Object System.Net.WebClient).DownloadFile($webDeployURL, $filePath) 13 | 14 | # # Older versions of Powershell do not have 'Expand Archive' 15 | # # Use Shell.Application custom object to unzip 16 | # # https://stackoverflow.com/questions/27768303/how-to-unzip-a-file-in-powershell 17 | # $shell = New-Object -ComObject Shell.Application 18 | # $zipFile = $shell.NameSpace($filePath) 19 | # $destinationFolder = $shell.NameSpace("C:\Program Files\WindowsPowerShell\Modules") 20 | 21 | # $copyFlags = 0x00 22 | # $copyFlags += 0x04 # Hide progress dialogs 23 | # $copyFlags += 0x10 # Overwrite existing files 24 | 25 | # $destinationFolder.CopyHere($zipFile.Items(), $copyFlags) 26 | # # Clean up 27 | # Remove-Item -Force -Path $filePath 28 | 29 | # Write-Output "Ended PSWindowsUpdate Installation" 30 | 31 | # Write-Output "Starting Windows Update Installation" 32 | 33 | # Try 34 | # { 35 | # Import-Module PSWindowsUpdate -ErrorAction Stop 36 | # } 37 | # Catch 38 | # { 39 | # Write-Error "Unable to install PSWindowsUpdate" 40 | # exit 1 41 | # } 42 | 43 | # if (Test-Path C:\Windows\Temp\PSWindowsUpdate.log) { 44 | # # Save old logs 45 | # Rename-Item -Path C:\Windows\Temp\PSWindowsUpdate.log -NewName PSWindowsUpdate-$((Get-Date).Ticks).log 46 | 47 | # # Uncomment the line below to delete old logs instead 48 | # #Remove-Item -Path C:\Windows\Temp\PSWindowsUpdate.log 49 | # } 50 | 51 | # try { 52 | # $updateCommand = {ipmo PSWindowsUpdate; Get-WUInstall -AcceptAll -IgnoreReboot | Out-File C:\Windows\Temp\PSWindowsUpdate.log} 53 | # $TaskName = "PackerUpdate" 54 | 55 | # $User = [Security.Principal.WindowsIdentity]::GetCurrent() 56 | # $Scheduler = New-Object -ComObject Schedule.Service 57 | 58 | # $Task = $Scheduler.NewTask(0) 59 | 60 | # $RegistrationInfo = $Task.RegistrationInfo 61 | # $RegistrationInfo.Description = $TaskName 62 | # $RegistrationInfo.Author = $User.Name 63 | 64 | # $Settings = $Task.Settings 65 | # $Settings.Enabled = $True 66 | # $Settings.StartWhenAvailable = $True 67 | # $Settings.Hidden = $False 68 | 69 | # $Action = $Task.Actions.Create(0) 70 | # $Action.Path = "powershell" 71 | # $Action.Arguments = "-Command $updateCommand" 72 | 73 | # $Task.Principal.RunLevel = 1 74 | 75 | # $Scheduler.Connect() 76 | # $RootFolder = $Scheduler.GetFolder("\") 77 | # $RootFolder.RegisterTaskDefinition($TaskName, $Task, 6, "SYSTEM", $Null, 1) | Out-Null 78 | # $RootFolder.GetTask($TaskName).Run(0) | Out-Null 79 | 80 | # Write-Output "The Windows Update log will be displayed below this message. No additional output indicates no updates were needed." 81 | # do { 82 | # sleep 1 83 | # if ((Test-Path C:\Windows\Temp\PSWindowsUpdate.log) -and $script:reader -eq $null) { 84 | # $script:stream = New-Object System.IO.FileStream -ArgumentList "C:\Windows\Temp\PSWindowsUpdate.log", "Open", "Read", "ReadWrite" 85 | # $script:reader = New-Object System.IO.StreamReader $stream 86 | # } 87 | # if ($script:reader -ne $null) { 88 | # $line = $Null 89 | # do {$script:reader.ReadLine() 90 | # $line = $script:reader.ReadLine() 91 | # Write-Output $line 92 | # } while ($line -ne $null) 93 | # } 94 | # } while ($Scheduler.GetRunningTasks(0) | Where-Object {$_.Name -eq $TaskName}) 95 | # } finally { 96 | # $RootFolder.DeleteTask($TaskName,0) 97 | # [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Scheduler) | Out-Null 98 | # if ($script:reader -ne $null) { 99 | # $script:reader.Close() 100 | # $script:stream.Dispose() 101 | # } 102 | # } 103 | Write-Output "Ended Windows Update Installation" -------------------------------------------------------------------------------- /example/settings.auto.pkrvars.hcl: -------------------------------------------------------------------------------- 1 | nutanix_username = "<PC User>" 2 | nutanix_password = "<PC Password>" 3 | nutanix_insecure = false 4 | nutanix_endpoint = "<PC Endpoint>" 5 | nutanix_port = 9440 6 | nutanix_cluster = "<my-cluster-name>" 7 | nutanix_subnet = "<subnet-name>" 8 | centos_iso_image_name = "<name of a centos 7/8 iso from the Nutanix image library>" 9 | centos_disk_image_name = "<name of a centos 7/8 cloud image from the Nutanix image library>" 10 | ubuntu_disk_image_name = "<name of a Ubuntu cloud image from the Nutanix image library>" 11 | windows_2016_iso_image_name = "<name of a windows iso from the Nutanix image library>" 12 | virtio_iso_image_name = "<name of VirtIO tools iso from the Nutanix image library>" -------------------------------------------------------------------------------- /example/source.nutanix.pkr.hcl: -------------------------------------------------------------------------------- 1 | source "nutanix" "centos" { 2 | nutanix_username = var.nutanix_username 3 | nutanix_password = var.nutanix_password 4 | nutanix_endpoint = var.nutanix_endpoint 5 | nutanix_port = var.nutanix_port 6 | nutanix_insecure = var.nutanix_insecure 7 | cluster_name = var.nutanix_cluster 8 | os_type = "Linux" 9 | 10 | vm_disks { 11 | image_type = "DISK_IMAGE" 12 | source_image_name = var.centos_disk_image_name 13 | disk_size_gb = 40 14 | } 15 | 16 | vm_nics { 17 | subnet_name = var.nutanix_subnet 18 | } 19 | 20 | image_categories { 21 | key = "TemplateType" 22 | value = "Vm" 23 | } 24 | 25 | vm_categories { 26 | key = "Environment" 27 | value = "Dev" 28 | } 29 | 30 | // project = "myproject" 31 | 32 | image_name = "centos-packer-image" 33 | image_export = false 34 | force_deregister = true 35 | user_data = base64encode(file("scripts/cloud-init/cloud-config-centos.yaml")) 36 | 37 | shutdown_command = "echo 'packer' | sudo -S shutdown -P now" 38 | shutdown_timeout = "2m" 39 | ssh_password = "packer" 40 | ssh_username = "centos" 41 | } 42 | 43 | source "nutanix" "ubuntu" { 44 | nutanix_username = var.nutanix_username 45 | nutanix_password = var.nutanix_password 46 | nutanix_endpoint = var.nutanix_endpoint 47 | nutanix_port = var.nutanix_port 48 | nutanix_insecure = var.nutanix_insecure 49 | cluster_name = var.nutanix_cluster 50 | os_type = "Linux" 51 | 52 | vm_disks { 53 | image_type = "DISK_IMAGE" 54 | source_image_name = var.ubuntu_disk_image_name 55 | disk_size_gb = 40 56 | } 57 | 58 | vm_nics { 59 | subnet_name = var.nutanix_subnet 60 | } 61 | 62 | image_name = "ubuntu-packer-image" 63 | force_deregister = true 64 | user_data = base64encode(file("scripts/cloud-init/cloud-config-ubuntu.yaml")) 65 | 66 | shutdown_command = "echo 'packer' | sudo -S shutdown -P now" 67 | shutdown_timeout = "2m" 68 | ssh_password = "packer" 69 | ssh_username = "builder" 70 | } 71 | 72 | source "nutanix" "centos-kickstart" { 73 | nutanix_username = var.nutanix_username 74 | nutanix_password = var.nutanix_password 75 | nutanix_endpoint = var.nutanix_endpoint 76 | nutanix_port = var.nutanix_port 77 | nutanix_insecure = var.nutanix_insecure 78 | cluster_name = var.nutanix_cluster 79 | os_type = "Linux" 80 | 81 | 82 | vm_disks { 83 | image_type = "ISO_IMAGE" 84 | source_image_name = var.centos_iso_image_name 85 | } 86 | 87 | vm_disks { 88 | image_type = "DISK" 89 | disk_size_gb = 40 90 | } 91 | 92 | vm_nics { 93 | subnet_name = var.nutanix_subnet 94 | } 95 | 96 | cd_files = ["scripts/ks.cfg"] 97 | cd_label = "OEMDRV" 98 | 99 | boot_priority = "disk" 100 | 101 | image_name ="centos8-{{isotime `Jan-_2-15:04:05`}}" 102 | shutdown_command = "echo 'packer' | sudo -S shutdown -P now" 103 | shutdown_timeout = "2m" 104 | ssh_password = "packer" 105 | ssh_username = "root" 106 | } 107 | 108 | source "nutanix" "windows" { 109 | nutanix_username = var.nutanix_username 110 | nutanix_password = var.nutanix_password 111 | nutanix_endpoint = var.nutanix_endpoint 112 | nutanix_insecure = var.nutanix_insecure 113 | cluster_name = var.nutanix_cluster 114 | 115 | vm_disks { 116 | image_type = "ISO_IMAGE" 117 | source_image_name = var.windows_2016_iso_image_name 118 | } 119 | 120 | vm_disks { 121 | image_type = "ISO_IMAGE" 122 | source_image_name = var.virtio_iso_image_name 123 | } 124 | 125 | vm_disks { 126 | image_type = "DISK" 127 | disk_size_gb = 40 128 | } 129 | 130 | vm_nics { 131 | subnet_name = var.nutanix_subnet 132 | } 133 | 134 | cd_files = ["scripts/gui/autounattend.xml","scripts/win-update.ps1"] 135 | 136 | boot_priority = "disk" 137 | 138 | image_name ="win-{{isotime `Jan-_2-15:04:05`}}" 139 | shutdown_command = "shutdown /s /t 10 /f /d p:4:1 /c \"Packer Shutdown\"" 140 | shutdown_timeout = "3m" 141 | cpu = 2 142 | os_type = "Windows" 143 | memory_mb = "8192" 144 | communicator = "winrm" 145 | winrm_port = 5986 146 | winrm_insecure = true 147 | winrm_use_ssl = true 148 | winrm_timeout = "45m" 149 | winrm_password = "packer" 150 | winrm_username = "Administrator" 151 | } 152 | -------------------------------------------------------------------------------- /example/variables.pkr.hcl: -------------------------------------------------------------------------------- 1 | variable "nutanix_username" { 2 | type = string 3 | } 4 | 5 | variable "nutanix_password" { 6 | type = string 7 | sensitive = true 8 | } 9 | 10 | variable "nutanix_endpoint" { 11 | type = string 12 | } 13 | 14 | variable "nutanix_port" { 15 | type = number 16 | } 17 | 18 | variable "nutanix_insecure" { 19 | type = bool 20 | default = true 21 | } 22 | 23 | variable "nutanix_subnet" { 24 | type = string 25 | } 26 | 27 | variable "nutanix_cluster" { 28 | type = string 29 | } 30 | 31 | variable "centos_iso_image_name" { 32 | type = string 33 | } 34 | 35 | variable "centos_disk_image_name" { 36 | type = string 37 | } 38 | 39 | variable "ubuntu_disk_image_name" { 40 | type = string 41 | } 42 | 43 | variable "windows_2016_iso_image_name" { 44 | type = string 45 | } 46 | 47 | variable "virtio_iso_image_name" { 48 | type = string 49 | } -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/nutanix-cloud-native/packer-plugin-nutanix 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.4 6 | 7 | require ( 8 | github.com/hashicorp/hcl/v2 v2.23.0 9 | github.com/hashicorp/packer-plugin-sdk v0.6.1 10 | github.com/nutanix-cloud-native/prism-go-client v0.5.1 11 | github.com/zclconf/go-cty v1.15.1 12 | ) 13 | 14 | require ( 15 | cloud.google.com/go v0.110.8 // indirect 16 | cloud.google.com/go/compute v1.23.1 // indirect 17 | cloud.google.com/go/compute/metadata v0.2.3 // indirect 18 | cloud.google.com/go/iam v1.1.3 // indirect 19 | cloud.google.com/go/storage v1.35.1 // indirect 20 | github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect 21 | github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 // indirect 22 | github.com/PaesslerAG/gval v1.0.0 // indirect 23 | github.com/PaesslerAG/jsonpath v0.1.1 // indirect 24 | github.com/agext/levenshtein v1.2.3 // indirect 25 | github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect 26 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect 27 | github.com/armon/go-metrics v0.4.1 // indirect 28 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect 29 | github.com/aws/aws-sdk-go v1.44.114 // indirect 30 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect 31 | github.com/cenkalti/backoff/v3 v3.2.2 // indirect 32 | github.com/dylanmei/iso8601 v0.1.0 // indirect 33 | github.com/fatih/color v1.16.0 // indirect 34 | github.com/go-jose/go-jose/v4 v4.0.5 // indirect 35 | github.com/go-logr/logr v1.4.1 // indirect 36 | github.com/go-logr/zapr v1.2.3 // indirect 37 | github.com/go-openapi/analysis v0.23.0 // indirect 38 | github.com/go-openapi/errors v0.22.0 // indirect 39 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 40 | github.com/go-openapi/jsonreference v0.21.0 // indirect 41 | github.com/go-openapi/loads v0.22.0 // indirect 42 | github.com/go-openapi/spec v0.21.0 // indirect 43 | github.com/go-openapi/strfmt v0.23.0 // indirect 44 | github.com/go-openapi/swag v0.23.0 // indirect 45 | github.com/go-openapi/validate v0.24.0 // indirect 46 | github.com/gofrs/flock v0.8.1 // indirect 47 | github.com/gofrs/uuid v4.0.0+incompatible // indirect 48 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 49 | github.com/golang/protobuf v1.5.4 // indirect 50 | github.com/google/s2a-go v0.1.7 // indirect 51 | github.com/google/uuid v1.6.0 // indirect 52 | github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect 53 | github.com/googleapis/gax-go/v2 v2.12.0 // indirect 54 | github.com/hashicorp/consul/api v1.25.1 // indirect 55 | github.com/hashicorp/errwrap v1.1.0 // indirect 56 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 57 | github.com/hashicorp/go-getter/gcs/v2 v2.2.2 // indirect 58 | github.com/hashicorp/go-getter/s3/v2 v2.2.2 // indirect 59 | github.com/hashicorp/go-getter/v2 v2.2.2 // indirect 60 | github.com/hashicorp/go-hclog v1.6.3 // indirect 61 | github.com/hashicorp/go-immutable-radix v1.3.1 // indirect 62 | github.com/hashicorp/go-multierror v1.1.1 // indirect 63 | github.com/hashicorp/go-retryablehttp v0.7.7 // indirect 64 | github.com/hashicorp/go-rootcerts v1.0.2 // indirect 65 | github.com/hashicorp/go-safetemp v1.0.0 // indirect 66 | github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect 67 | github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect 68 | github.com/hashicorp/go-sockaddr v1.0.7 // indirect 69 | github.com/hashicorp/go-version v1.6.0 // indirect 70 | github.com/hashicorp/golang-lru v0.5.4 // indirect 71 | github.com/hashicorp/hcl v1.0.0 // indirect 72 | github.com/hashicorp/serf v0.10.1 // indirect 73 | github.com/hashicorp/vault/api v1.14.0 // indirect 74 | github.com/hashicorp/yamux v0.1.1 // indirect 75 | github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect 76 | github.com/jmespath/go-jmespath v0.4.0 // indirect 77 | github.com/josharian/intern v1.0.0 // indirect 78 | github.com/klauspost/compress v1.13.6 // indirect 79 | github.com/kr/fs v0.1.0 // indirect 80 | github.com/mailru/easyjson v0.7.7 // indirect 81 | github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect 82 | github.com/masterzen/winrm v0.0.0-20210623064412-3b76017826b0 // indirect 83 | github.com/mattn/go-colorable v0.1.13 // indirect 84 | github.com/mattn/go-isatty v0.0.20 // indirect 85 | github.com/mitchellh/go-fs v0.0.0-20180402235330-b7b9ca407fff // indirect 86 | github.com/mitchellh/go-homedir v1.1.0 // indirect 87 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect 88 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect 89 | github.com/mitchellh/iochan v1.0.0 // indirect 90 | github.com/mitchellh/mapstructure v1.5.0 // indirect 91 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 92 | github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect 93 | github.com/oklog/ulid v1.3.1 // indirect 94 | github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db // indirect 95 | github.com/pkg/sftp v1.13.2 // indirect 96 | github.com/ryanuber/go-glob v1.0.0 // indirect 97 | github.com/ugorji/go/codec v1.2.6 // indirect 98 | github.com/ulikunitz/xz v0.5.10 // indirect 99 | github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect 100 | github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect 101 | go.mongodb.org/mongo-driver v1.14.0 // indirect 102 | go.opencensus.io v0.24.0 // indirect 103 | go.uber.org/atomic v1.9.0 // indirect 104 | go.uber.org/multierr v1.7.0 // indirect 105 | go.uber.org/zap v1.24.0 // indirect 106 | golang.org/x/crypto v0.37.0 // indirect 107 | golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect 108 | golang.org/x/mod v0.24.0 // indirect 109 | golang.org/x/net v0.37.0 // indirect 110 | golang.org/x/oauth2 v0.13.0 // indirect 111 | golang.org/x/sync v0.13.0 // indirect 112 | golang.org/x/sys v0.32.0 // indirect 113 | golang.org/x/term v0.31.0 // indirect 114 | golang.org/x/text v0.24.0 // indirect 115 | golang.org/x/time v0.11.0 // indirect 116 | golang.org/x/tools v0.31.0 // indirect 117 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect 118 | google.golang.org/api v0.150.0 // indirect 119 | google.golang.org/appengine v1.6.7 // indirect 120 | google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect 121 | google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect 122 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect 123 | google.golang.org/grpc v1.59.0 // indirect 124 | google.golang.org/protobuf v1.33.0 // indirect 125 | gopkg.in/yaml.v3 v3.0.1 // indirect 126 | ) 127 | 128 | replace ( 129 | github.com/hashicorp/consul => github.com/hashicorp/consul v1.10.4 130 | github.com/hashicorp/vault => github.com/hashicorp/vault v1.4.2 131 | ) 132 | 133 | replace github.com/zclconf/go-cty => github.com/nywilken/go-cty v1.13.3 // added by packer-sdc fix as noted in github.com/hashicorp/packer-plugin-sdk/issues/187 134 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/hashicorp/packer-plugin-sdk/plugin" 8 | "github.com/nutanix-cloud-native/packer-plugin-nutanix/builder/nutanix" 9 | "github.com/nutanix-cloud-native/packer-plugin-nutanix/version" 10 | ) 11 | 12 | func main() { 13 | pps := plugin.NewSet() 14 | pps.RegisterBuilder(plugin.DEFAULT_NAME, new(nutanix.Builder)) 15 | pps.SetVersion(version.PluginVersion) 16 | err := pps.Run() 17 | if err != nil { 18 | fmt.Fprintln(os.Stderr, err.Error()) 19 | os.Exit(1) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/e2e/centos-img/build.pkr.hcl: -------------------------------------------------------------------------------- 1 | build { 2 | source "nutanix.centos" { 3 | name = "centos" 4 | } 5 | 6 | provisioner "shell" { 7 | environment_vars = [ 8 | "FOO=hello world", 9 | ] 10 | inline = [ 11 | "echo \"FOO is $FOO\" > example.txt", 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/e2e/centos-img/source.pkr.hcl: -------------------------------------------------------------------------------- 1 | source "nutanix" "centos" { 2 | nutanix_username = var.nutanix_username 3 | nutanix_password = var.nutanix_password 4 | nutanix_endpoint = var.nutanix_endpoint 5 | nutanix_port = var.nutanix_port 6 | nutanix_insecure = var.nutanix_insecure 7 | cluster_name = var.nutanix_cluster 8 | os_type = "Linux" 9 | 10 | vm_disks { 11 | image_type = "DISK_IMAGE" 12 | source_image_uri = "https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-2111.qcow2" 13 | source_image_checksum = "4c34278cd7ba51e47d864a5cb34301a2ec7853786cb73877f3fe61bb1040edd4" 14 | source_image_checksum_type = "sha256" 15 | disk_size_gb = 20 16 | } 17 | 18 | vm_nics { 19 | subnet_name = var.nutanix_subnet 20 | } 21 | 22 | image_categories { 23 | key = "TemplateType" 24 | value = "Vm" 25 | } 26 | 27 | vm_categories { 28 | key = "Environment" 29 | value = "Testing" 30 | } 31 | 32 | vm_name = "e2e-packer-${var.test}-${formatdate("MDYYhms", timestamp())}" 33 | 34 | image_name = "e2e-packer-${var.test}-${formatdate("MDYYhms", timestamp())}" 35 | image_delete = true 36 | 37 | force_deregister = true 38 | user_data = "I2Nsb3VkLWNvbmZpZwp1c2VyczoKICAtIG5hbWU6IGNlbnRvcwogICAgc3VkbzogWydBTEw9KEFMTCkgTk9QQVNTV0Q6QUxMJ10KY2hwYXNzd2Q6CiAgbGlzdDogfAogICAgY2VudG9zOnBhY2tlcgogIGV4cGlyZTogRmFsc2UKc3NoX3B3YXV0aDogVHJ1ZQ==" 39 | 40 | shutdown_command = "echo 'packer' | sudo -S shutdown -P now" 41 | shutdown_timeout = "2m" 42 | ssh_password = "packer" 43 | ssh_username = "centos" 44 | } 45 | -------------------------------------------------------------------------------- /test/e2e/centos-img/variables.pkr.hcl: -------------------------------------------------------------------------------- 1 | variable "nutanix_username" { 2 | type = string 3 | } 4 | 5 | variable "nutanix_password" { 6 | type = string 7 | sensitive = true 8 | } 9 | 10 | variable "nutanix_endpoint" { 11 | type = string 12 | } 13 | 14 | variable "nutanix_port" { 15 | type = number 16 | } 17 | 18 | variable "nutanix_insecure" { 19 | type = bool 20 | default = true 21 | } 22 | 23 | variable "nutanix_subnet" { 24 | type = string 25 | } 26 | 27 | variable "nutanix_cluster" { 28 | type = string 29 | } 30 | 31 | variable "test" { 32 | type = string 33 | } -------------------------------------------------------------------------------- /test/e2e/centos-iso/build.pkr.hcl: -------------------------------------------------------------------------------- 1 | build { 2 | source "nutanix.centos" { 3 | name = "centos" 4 | } 5 | 6 | provisioner "shell" { 7 | environment_vars = [ 8 | "FOO=hello world", 9 | ] 10 | inline = [ 11 | "echo \"FOO is $FOO\" > example.txt", 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/e2e/centos-iso/scripts/ks.cfg: -------------------------------------------------------------------------------- 1 | # Author: Tomasz Filipiec <tomasz.filipiec@gmail.com> 2 | # Desc.: Simple kickstart file for automated installation 3 | # OS: CentOS 7 Minimal (core) 4 | # Version: 1.0 5 | 6 | # Turning on text-mode installation (little quicker than GUI) 7 | text 8 | 9 | # Setting up authentication and keyboard 10 | auth --enableshadow --passalgo=sha512 11 | keyboard --vckeymap=us --xlayouts='us' 12 | 13 | # Installation files source (CentOS-7.0-1406-x86_64-Minimal.iso) 14 | cdrom 15 | 16 | # Using only primary disk, ignoring others 17 | ignoredisk --only-use=sda 18 | 19 | # Setting up language to English 20 | lang en-US.UTF-8 21 | 22 | # Setting up network interface to DHCP 23 | network --bootproto=dhcp --ipv6=auto --hostname=centos-ks.local --activate 24 | 25 | # Root password (remember that plaintext only for information purposes) 26 | rootpw --plaintext packer 27 | 28 | # Setting up firewall and enabling SSH for remote management 29 | firewall --enabled --service=ssh 30 | 31 | # Setting timezone 32 | timezone Europe/Berlin --isUtc 33 | 34 | # Setting up Security-Enhanced Linux into enforcing 35 | selinux --enforcing 36 | 37 | # Setting up MBR 38 | bootloader --location=mbr --boot-drive=sda 39 | 40 | # Setting up Logical Volume Manager and autopartitioning 41 | clearpart --all --drives=sda,sdb --initlabel 42 | ignoredisk --only-use=sda,sdb 43 | autopart --type=lvm 44 | 45 | # Eject cdrom and reboot 46 | reboot --eject 47 | 48 | # Installing only packages for minimal install 49 | %packages 50 | @Core 51 | chrony 52 | %end -------------------------------------------------------------------------------- /test/e2e/centos-iso/source.pkr.hcl: -------------------------------------------------------------------------------- 1 | source "nutanix" "centos" { 2 | nutanix_username = var.nutanix_username 3 | nutanix_password = var.nutanix_password 4 | nutanix_endpoint = var.nutanix_endpoint 5 | nutanix_port = var.nutanix_port 6 | nutanix_insecure = var.nutanix_insecure 7 | cluster_name = var.nutanix_cluster 8 | os_type = "Linux" 9 | 10 | vm_disks { 11 | image_type = "ISO_IMAGE" 12 | source_image_uri = "https://vault.centos.org/7.9.2009/isos/x86_64/CentOS-7-x86_64-Minimal-2009.iso" 13 | source_image_checksum = "07b94e6b1a0b0260b94c83d6bb76b26bf7a310dc78d7a9c7432809fb9bc6194a" 14 | source_image_checksum_type = "sha256" 15 | } 16 | 17 | vm_disks { 18 | image_type = "DISK" 19 | disk_size_gb = 40 20 | } 21 | 22 | vm_disks { 23 | image_type = "DISK" 24 | disk_size_gb = 20 25 | } 26 | 27 | vm_nics { 28 | subnet_name = var.nutanix_subnet 29 | } 30 | 31 | image_categories { 32 | key = "TemplateType" 33 | value = "Vm" 34 | } 35 | 36 | vm_categories { 37 | key = "Environment" 38 | value = "Testing" 39 | } 40 | 41 | cd_files = ["scripts/ks.cfg"] 42 | cd_label = "OEMDRV" 43 | 44 | vm_name = "e2e-packer-${var.test}-${formatdate("MDYYhms", timestamp())}" 45 | image_name = "e2e-packer-${var.test}-${formatdate("MDYYhms", timestamp())}" 46 | image_delete = true 47 | 48 | boot_priority = "disk" 49 | 50 | force_deregister = true 51 | 52 | shutdown_command = "echo 'packer' | sudo -S shutdown -P now" 53 | shutdown_timeout = "2m" 54 | ssh_password = "packer" 55 | ssh_username = "root" 56 | } 57 | -------------------------------------------------------------------------------- /test/e2e/centos-iso/variables.pkr.hcl: -------------------------------------------------------------------------------- 1 | variable "nutanix_username" { 2 | type = string 3 | } 4 | 5 | variable "nutanix_password" { 6 | type = string 7 | sensitive = true 8 | } 9 | 10 | variable "nutanix_endpoint" { 11 | type = string 12 | } 13 | 14 | variable "nutanix_port" { 15 | type = number 16 | } 17 | 18 | variable "nutanix_insecure" { 19 | type = bool 20 | default = true 21 | } 22 | 23 | variable "nutanix_subnet" { 24 | type = string 25 | } 26 | 27 | variable "nutanix_cluster" { 28 | type = string 29 | } 30 | 31 | variable "test" { 32 | type = string 33 | } -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "github.com/hashicorp/packer-plugin-sdk/version" 5 | ) 6 | 7 | var ( 8 | // Version is the main version number that is being run at the moment. 9 | Version = "0.12.2" 10 | 11 | // VersionPrerelease is A pre-release marker for the Version. If this is "" 12 | // (empty string) then it means that it is a final release. Otherwise, this 13 | // is a pre-release such as "dev" (in development), "beta", "rc1", etc. 14 | VersionPrerelease = "dev" 15 | 16 | // VersionMetadata 17 | VersionMetadata = "" 18 | 19 | // PluginVersion is used by the plugin set to allow Packer to recognize 20 | // what version this plugin is. 21 | PluginVersion = version.NewPluginVersion(Version, VersionPrerelease, VersionMetadata) 22 | ) 23 | --------------------------------------------------------------------------------