Skip to content

Commit

Permalink
edit: rename package from template to virtualmachine
Browse files Browse the repository at this point in the history
Renamed package and corrected documentation accordingly.
  • Loading branch information
castorsky committed Dec 10, 2024
1 parent 9adc728 commit 4a177f7
Show file tree
Hide file tree
Showing 14 changed files with 212 additions and 158 deletions.
7 changes: 4 additions & 3 deletions .web-docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Packer is able to target both ISO and existing Cloud-Init images.

#### Data Sources

- [data source](/packer/integrations/hashicorp/proxmox/latest/components/datasource/template) - The proxmox template
datasource is able to get info about existing guests from Proxmox cluster and return VM ID of a single guest that
matches all specified filters. This ID can later be used in the 'clone' builder to select template.
- [virtualmachine](/packer/integrations/hashicorp/proxmox/latest/components/datasource/virtualmachine) - The proxmox
virtual machine datasource retrieves information about existing virtual machines
from Proxmox cluster and returns VM ID of one virtual machine that matches all
specified filters. This ID can be used in the clone builder to select a template.
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
Type: `proxmox-template`
Artifact BuilderId: `proxmox.template`
Type: `proxmox-virtualmachine`
Artifact BuilderId: `proxmox.virtualmachine`

The `proxmox-template` datasource is able to get info about existing guests
from [Proxmox](https://www.proxmox.com/en/proxmox-ve) cluster and return VM
ID of a single guest that matches all specified filters. This ID can later
be used in the `proxmox-clone` builder to select template.
The `proxmox-virtualmachine` datasource retrieves information about existing virtual machines
from [Proxmox](https://www.proxmox.com/en/proxmox-ve) cluster and returns VM ID of one virtual machine
that matches all specified filters. This ID can be used in the `proxmox-clone` builder to select a template.

## Configuration Reference

<!-- Code generated from the comments of the Config struct in datasource/proxmox/data.go; DO NOT EDIT MANUALLY -->
<!-- Code generated from the comments of the Config struct in datasource/virtualmachine/data.go; DO NOT EDIT MANUALLY -->

Datasource has a bunch of filters which you can use, for example, to find the latest available
template in the cluster that matches defined filters.

You can combine any number of filters but all of them will be conjuncted with AND.
When datasource cannot return only one (zero or >1) guest identifiers it will return error.

<!-- End of code generated from the comments of the Config struct in datasource/proxmox/data.go; -->
<!-- End of code generated from the comments of the Config struct in datasource/virtualmachine/data.go; -->


## Optional:

<!-- Code generated from the comments of the Config struct in datasource/proxmox/data.go; DO NOT EDIT MANUALLY -->
<!-- Code generated from the comments of the Config struct in datasource/virtualmachine/data.go; DO NOT EDIT MANUALLY -->

- `proxmox_url` (string) - URL to the Proxmox API, including the full path,
so `https://<server>:<port>/api2/json` for example.
Expand Down Expand Up @@ -50,60 +49,70 @@ When datasource cannot return only one (zero or >1) guest identifiers it will re
- `task_timeout` (duration string | ex: "1h5m2s") - `task_timeout` (duration string | ex: "10m") - The timeout for
Promox API operations, e.g. clones. Defaults to 1 minute.

- `name` (string) - Filter that returns `vm_id` for guest which name exactly matches this value.
- `name` (string) - Filter that returns `vm_id` for virtual machine which name exactly matches this value.
Options `name` and `name_regex` are mutually exclusive.

- `name_regex` (string) - Filter that returns `vm_id` for guest which name matches the regular expression.
- `name_regex` (string) - Filter that returns `vm_id` for virtual machine which name matches the regular expression.
Expression must use [Go Regex Syntax](https://pkg.go.dev/regexp/syntax).
Options `name` and `name_regex` are mutually exclusive.

- `template` (bool) - Filter that returns guest `vm_id` only when guest is template.
- `template` (bool) - Filter that returns virtual machine `vm_id` only when virtual machine is template.

- `node` (string) - Filter that returns `vm_id` only when guest is located on the specified PVE node.
- `node` (string) - Filter that returns `vm_id` only when virtual machine is located on the specified PVE node.

- `vm_tags` (string) - Filter that returns `vm_id` for guest which has all these tags. When you need to
- `vm_tags` (string) - Filter that returns `vm_id` for virtual machine which has all these tags. When you need to
specify more than one tag, use semicolon as separator (`"tag1;tag2"`).
Every specified tag must exist in guest.
Every specified tag must exist in virtual machine.

- `latest` (bool) - This filter determines how to handle multiple guests that were matched with all
previous filters. Guest creation time is being used to find latest.
By default, multiple matching guests results in an error.
- `latest` (bool) - This filter determines how to handle multiple virtual machines that were matched with all
previous filters. Virtual machine creation time is being used to find latest.
By default, multiple matching virtual machines results in an error.

<!-- End of code generated from the comments of the Config struct in datasource/proxmox/data.go; -->
<!-- End of code generated from the comments of the Config struct in datasource/virtualmachine/data.go; -->


## Output:

<!-- Code generated from the comments of the DatasourceOutput struct in datasource/proxmox/data.go; DO NOT EDIT MANUALLY -->
<!-- Code generated from the comments of the DatasourceOutput struct in datasource/virtualmachine/data.go; DO NOT EDIT MANUALLY -->

- `vm_id` (uint) - Identifier of the found guest.
- `vm_id` (uint) - Identifier of the found virtual machine.

- `vm_name` (string) - Name of the found guest.
- `vm_name` (string) - Name of the found virtual machine.

- `vm_tags` (string) - Tags of the found guest separated with semicolon.
- `vm_tags` (string) - Tags of the found virtual machine separated with semicolon.

<!-- End of code generated from the comments of the DatasourceOutput struct in datasource/proxmox/data.go; -->
<!-- End of code generated from the comments of the DatasourceOutput struct in datasource/virtualmachine/data.go; -->


## Example Usage

This is a very basic example which connects to local PVE host, finds the latest
guest which name matches the regex `image-.*` and which type is `template`. The
ID is then printed to console as output variable.
ID of the virtual machine is printed to console as output variable.

```hcl
data "proxmox-template" "default" {
proxmox_url = "https://localhost:8006/api2/json"
variable "password" {
type = string
default = "supersecret"
}
variable "username" {
type = string
default = "apiuser@pve"
}
data "proxmox-virtualmachine" "default" {
proxmox_url = "https://my-proxmox.my-domain:8006/api2/json"
insecure_skip_tls_verify = true
username = "root@pam"
password = "password"
username = "${var.username}"
password = "${var.password}"
name_regex = "image-.*"
template = true
latest = true
}
locals {
vm_id = data.proxmox-template.default.vm_id
vm_id = data.proxmox-virtualmachine.default.vm_id
}
source "null" "basic-example" {
Expand Down
4 changes: 2 additions & 2 deletions .web-docs/metadata.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ integration {
}
component {
type = "data-source"
name = "Proxmox Template"
slug = "template"
name = "Proxmox Virtual Machine"
slug = "virtualmachine"
}
}
75 changes: 51 additions & 24 deletions datasource/proxmox/data.go → datasource/virtualmachine/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//go:generate packer-sdc struct-markdown
//go:generate packer-sdc mapstructure-to-hcl2 -type Config,DatasourceOutput

package proxmoxtemplate
package virtualmachine

import (
"crypto/tls"
Expand Down Expand Up @@ -63,24 +63,24 @@ type Config struct {
// `task_timeout` (duration string | ex: "10m") - The timeout for
// Promox API operations, e.g. clones. Defaults to 1 minute.
TaskTimeout time.Duration `mapstructure:"task_timeout"`
// Filter that returns `vm_id` for guest which name exactly matches this value.
// Filter that returns `vm_id` for virtual machine which name exactly matches this value.
// Options `name` and `name_regex` are mutually exclusive.
Name string `mapstructure:"name"`
// Filter that returns `vm_id` for guest which name matches the regular expression.
// Filter that returns `vm_id` for virtual machine which name matches the regular expression.
// Expression must use [Go Regex Syntax](https://pkg.go.dev/regexp/syntax).
// Options `name` and `name_regex` are mutually exclusive.
NameRegex string `mapstructure:"name_regex"`
// Filter that returns guest `vm_id` only when guest is template.
// Filter that returns virtual machine `vm_id` only when virtual machine is template.
Template bool `mapstructure:"template"`
// Filter that returns `vm_id` only when guest is located on the specified PVE node.
// Filter that returns `vm_id` only when virtual machine is located on the specified PVE node.
Node string `mapstructure:"node"`
// Filter that returns `vm_id` for guest which has all these tags. When you need to
// Filter that returns `vm_id` for virtual machine which has all these tags. When you need to
// specify more than one tag, use semicolon as separator (`"tag1;tag2"`).
// Every specified tag must exist in guest.
// Every specified tag must exist in virtual machine.
VmTags string `mapstructure:"vm_tags"`
// This filter determines how to handle multiple guests that were matched with all
// previous filters. Guest creation time is being used to find latest.
// By default, multiple matching guests results in an error.
// This filter determines how to handle multiple virtual machines that were matched with all
// previous filters. Virtual machine creation time is being used to find latest.
// By default, multiple matching virtual machines results in an error.
Latest bool `mapstructure:"latest"`
}

Expand All @@ -89,11 +89,11 @@ type Datasource struct {
}

type DatasourceOutput struct {
// Identifier of the found guest.
// Identifier of the found virtual machine.
VmId uint `mapstructure:"vm_id"`
// Name of the found guest.
// Name of the found virtual machine.
VmName string `mapstructure:"vm_name"`
// Tags of the found guest separated with semicolon.
// Tags of the found virtual machine separated with semicolon.
VmTags string `mapstructure:"vm_tags"`
}

Expand Down Expand Up @@ -180,7 +180,7 @@ func (d *Datasource) Execute() (cty.Value, error) {

filteredVms := filterGuests(d.config, vmList)
if len(filteredVms) == 0 {
return cty.NullVal(cty.EmptyObject), errors.New("not a single vm matches the configured filters")
return cty.NullVal(cty.EmptyObject), errors.New("no virtual machine matches the filters")
}

if d.config.Latest {
Expand All @@ -195,11 +195,11 @@ func (d *Datasource) Execute() (cty.Value, error) {
}

vmId = latestConfig["vmid"].(uint)
vmName = latestConfig["name"].(string)
vmTags = latestConfig["tags"].(string)
vmName = configValueOrEmpty(&latestConfig, "name")
vmTags = configValueOrEmpty(&latestConfig, "tags")
} else {
if len(filteredVms) > 1 {
return cty.NullVal(cty.EmptyObject), errors.New("more than one guest passed filters, cannot return vm_id")
return cty.NullVal(cty.EmptyObject), errors.New("more than one virtual machine matched the filters")
}
vmId = filteredVms[0].Id
vmName = filteredVms[0].Name
Expand Down Expand Up @@ -230,13 +230,13 @@ func findLatestConfig(configs []vmConfig) (vmConfig, error) {
result = configs[i]
}
} else {
return nil, errors.New("no meta field in the guest config")
return nil, errors.New("no meta field in the virtual machine config")
}
}
return result, nil
}

// Get configs from PVE in 'map[string]interface{}' format for all VMs in the list.
// getVmConfigs retrieves configs from PVE in 'map[string]interface{}' format for all VMs in the list.
// Also add value of VM ID to every config (useful for further steps).
func getVmConfigs(client *proxmox.Client, vmList []proxmox.GuestResource) ([]vmConfig, error) {
var result []vmConfig
Expand All @@ -253,7 +253,7 @@ func getVmConfigs(client *proxmox.Client, vmList []proxmox.GuestResource) ([]vmC
return result, nil
}

// filterGuests removes guests from the `guests` list that do not match some filters in the datasource config.
// filterGuests removes virtual machines from the `guests` list that do not match some filters in the datasource config.
func filterGuests(config Config, guests []proxmox.GuestResource) []proxmox.GuestResource {
filterFuncs := make([]func(proxmox.GuestResource) bool, 0)

Expand Down Expand Up @@ -292,6 +292,9 @@ func filterGuests(config Config, guests []proxmox.GuestResource) []proxmox.Guest
result := make([]proxmox.GuestResource, 0)
for _, guest := range guests {
var ok bool
if len(filterFuncs) == 0 {
ok = true
}
for _, guestPassedFilter := range filterFuncs {
if ok = guestPassedFilter(guest); !ok {
break
Expand All @@ -305,6 +308,8 @@ func filterGuests(config Config, guests []proxmox.GuestResource) []proxmox.Guest
return result
}

// configTagsMatchNodeTags compares two lists of strings and returns true only when all
// elements from the first list are present in the second list.
func configTagsMatchNodeTags(configTags []string, nodeTags []proxmox.Tag) bool {
var countOfMatchedTags int
for _, configTag := range configTags {
Expand All @@ -325,14 +330,15 @@ func configTagsMatchNodeTags(configTags []string, nodeTags []proxmox.Tag) bool {
return true
}

// newProxmoxClient creates new client and tries to connect and log in to Proxmox instance.
func newProxmoxClient(config Config) (*proxmox.Client, error) {
tlsConfig := &tls.Config{
InsecureSkipVerify: config.SkipCertValidation,
}

client, err := proxmox.NewClient(strings.TrimSuffix(config.proxmoxURL.String(), "/"), nil, "", tlsConfig, "", int(config.TaskTimeout.Seconds()))
if err != nil {
return nil, err
return nil, fmt.Errorf("could not connect to Proxmox: %w", err)
}

*proxmox.Debug = config.PackerDebug
Expand All @@ -346,17 +352,19 @@ func newProxmoxClient(config Config) (*proxmox.Client, error) {
log.Print("using password auth")
err = client.Login(config.Username, config.Password, "")
if err != nil {
return nil, err
return nil, fmt.Errorf("could not log in to Proxmox: %w", err)
}
}

return client, nil
}

// parseMetaField parses the string from the `meta` field and returns integer value
// representing the creation date of the virtual machine in epoch seconds format.
func parseMetaField(field string) (int, error) {
re, err := regexp.Compile(`.*ctime=(?P<ctime>[0-9]+).*`)
if err != nil {
return 0, err
return 0, fmt.Errorf("could not compile regex to parse meta field: %w", err)
}

matched := re.MatchString(field)
Expand All @@ -366,15 +374,34 @@ func parseMetaField(field string) (int, error) {
valueStr := re.ReplaceAllString(field, "${ctime}")
value, err := strconv.Atoi(valueStr)
if err != nil {
return 0, err
return 0, fmt.Errorf("could not convert date field to int: %w", err)
}
return value, nil
}

// joinTags used to combine list of strings into one string with defined separator.
func joinTags(tags []proxmox.Tag, separator string) string {
tagsAsStrings := make([]string, len(tags))
for i, tag := range tags {
tagsAsStrings[i] = string(tag)
}
return strings.Join(tagsAsStrings, separator)
}

// configValueOrEmpty tries to retrieve string by key from dynamic map of interfaces.
// In case when key not found or there was an error, this function returns empty string.
func configValueOrEmpty(values *vmConfig, key string) string {
result := ""
if values != nil {
value, exists := (*values)[key]
if !exists {
return result
}
strValue, ok := value.(string)
if !ok {
return result
}
result = strValue
}
return result
}

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

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package proxmoxtemplate
package virtualmachine

import (
"fmt"
Expand Down Expand Up @@ -126,6 +126,14 @@ func TestExecute(t *testing.T) {
Latest: true,
},
},
{
name: "found latest guest at cluster, no error",
expectFailure: false,
expectedVmId: 102,
configDiff: Config{
Latest: true,
},
},
{
name: "proxmox host not found, error",
expectFailure: true,
Expand Down
9 changes: 0 additions & 9 deletions docs-partials/datasource/proxmox/DatasourceOutput.mdx

This file was deleted.

Loading

0 comments on commit 4a177f7

Please sign in to comment.