Skip to content

Commit 08527f1

Browse files
castorskyRyan Johnson
authored and
Ryan Johnson
committed
add: new datasource and documentation
1 parent 05133be commit 08527f1

File tree

17 files changed

+1246
-0
lines changed

17 files changed

+1246
-0
lines changed

.web-docs/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ packer plugins install github.com/hashicorp/vsphere
4949
This builder deploys and publishes new virtual machine to a vSphere Supervisor cluster using VM
5050
Service.
5151

52+
#### Data Sources
53+
54+
- [vsphere-virtual_machine](/packer/integrations/hashicorp/vsphere/latest/components/data-source/vsphere-virtual_machine) -
55+
This datasource returns name of existing virtual machine that matches all defined filters to use
56+
it as a builder source for `vsphere-clone`.
57+
5258
#### Post-Processors
5359

5460
- [vsphere](/packer/integrations/hashicorp/vsphere/latest/components/post-processor/vsphere) -
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
Type: `vsphere-virtual_machine`
2+
Artifact BuilderId: `vsphere.virtual_machine`
3+
4+
This datasource is able to get information about existing virtual machines from vSphere
5+
and return name of one virtual machine that matches all specified filters. This virtual
6+
machine can later be used in the vSphere Clone builder to select template.
7+
8+
## Configuration Reference
9+
10+
### Filters Configuration
11+
12+
**Optional:**
13+
14+
<!-- Code generated from the comments of the Config struct in datasource/virtual_machine/data.go; DO NOT EDIT MANUALLY -->
15+
16+
- `name` (string) - Basic filter with glob support (e.g. `nginx_basic*`). Defaults to `*`.
17+
Using strict globs will not reduce execution time because vSphere API returns the full inventory.
18+
But can be used for better readability over regular expressions.
19+
20+
- `name_regex` (string) - Extended name filter with regular expressions support (e.g. `nginx[-_]basic[0-9]*`). Default is empty.
21+
The match of the regular expression is checked by substring. Use `^` and `$` to define a full string.
22+
E.g. the `^[^_]+$` filter will search names without any underscores.
23+
The expression must use [Go Regex Syntax](https://pkg.go.dev/regexp/syntax).
24+
25+
- `template` (bool) - Filter to return only objects that are virtual machine templates.
26+
Defaults to `false` and returns all VMs.
27+
28+
- `node` (string) - Filter to search virtual machines only on the specified node.
29+
30+
- `vm_tags` ([]Tag) - Filter to return only that virtual machines that have attached all specifies tags.
31+
Specify one or more `vm_tags` blocks to define list of tags that will make up the filter.
32+
Should work since vCenter 6.7. To avoid incompatibility, REST client is being
33+
initialized only when at least one tag has been defined in the config.
34+
35+
- `latest` (bool) - This filter determines how to handle multiple machines that were matched with all
36+
previous filters. Machine creation time is being used to find latest.
37+
By default, multiple matching machines results in an error.
38+
39+
<!-- End of code generated from the comments of the Config struct in datasource/virtual_machine/data.go; -->
40+
41+
42+
### Tags Filter Configuration
43+
44+
<!-- Code generated from the comments of the Tag struct in datasource/virtual_machine/data.go; DO NOT EDIT MANUALLY -->
45+
46+
Example of multiple vm_tags blocks in HCL format:
47+
```
48+
49+
vm_tags {
50+
category = "team"
51+
name = "operations"
52+
}
53+
vm_tags {
54+
category = "SLA"
55+
name = "gold"
56+
}
57+
58+
```
59+
60+
<!-- End of code generated from the comments of the Tag struct in datasource/virtual_machine/data.go; -->
61+
62+
63+
**Required:**
64+
65+
<!-- Code generated from the comments of the Tag struct in datasource/virtual_machine/data.go; DO NOT EDIT MANUALLY -->
66+
67+
- `name` (string) - Tag with this name must be attached to virtual machine which should pass the Tags Filter.
68+
69+
- `category` (string) - Name of the category that contains this tag. Both tag and category must be specified.
70+
71+
<!-- End of code generated from the comments of the Tag struct in datasource/virtual_machine/data.go; -->
72+
73+
74+
### Connection Configuration
75+
76+
**Optional:**
77+
78+
<!-- Code generated from the comments of the ConnectConfig struct in builder/vsphere/common/step_connect.go; DO NOT EDIT MANUALLY -->
79+
80+
- `vcenter_server` (string) - The fully qualified domain name or IP address of the vCenter Server
81+
instance.
82+
83+
- `username` (string) - The username to authenticate with the vCenter Server instance.
84+
85+
- `password` (string) - The password to authenticate with the vCenter Server instance.
86+
87+
- `insecure_connection` (bool) - Do not validate the certificate of the vCenter Server instance.
88+
Defaults to `false`.
89+
90+
-> **Note:** This option is beneficial in scenarios where the certificate
91+
is self-signed or does not meet standard validation criteria.
92+
93+
- `datacenter` (string) - The name of the datacenter object in the vSphere inventory.
94+
95+
-> **Note:** Required if more than one datacenter object exists in the
96+
vSphere inventory.
97+
98+
<!-- End of code generated from the comments of the ConnectConfig struct in builder/vsphere/common/step_connect.go; -->
99+
100+
101+
## Output
102+
103+
<!-- Code generated from the comments of the DatasourceOutput struct in datasource/virtual_machine/data.go; DO NOT EDIT MANUALLY -->
104+
105+
- `vm_name` (string) - Name of the found virtual machine.
106+
107+
<!-- End of code generated from the comments of the DatasourceOutput struct in datasource/virtual_machine/data.go; -->
108+
109+
110+
## Example Usage
111+
112+
This is a very basic example that connects to vSphere cluster and tries to search
113+
the latest virtual machine that matches all filters. The machine name is then printed
114+
to console as output variable.
115+
```hcl
116+
data "vsphere-virtual_machine" "default" {
117+
vcenter_server = "vcenter.example.org"
118+
insecure_connection = true
119+
username = "[email protected]"
120+
password = "St4ongPa$$w0rd"
121+
datacenter = "AZ1"
122+
latest = true
123+
vm_tags {
124+
category = "team"
125+
name = "operations"
126+
}
127+
vm_tags {
128+
category = "SLA"
129+
name = "gold"
130+
}
131+
132+
}
133+
134+
locals {
135+
vm_name = data.vsphere-virtual_machine.default.vm_name
136+
}
137+
138+
source "null" "basic-example" {
139+
communicator = "none"
140+
}
141+
142+
build {
143+
sources = [
144+
"source.null.basic-example"
145+
]
146+
147+
provisioner "shell-local" {
148+
inline = [
149+
"echo vm_name: ${local.vm_name}",
150+
]
151+
}
152+
}
153+
154+
155+
```

.web-docs/metadata.hcl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,9 @@ integration {
3333
name = "vSphere Template"
3434
slug = "vsphere-template"
3535
}
36+
component {
37+
type = "data-source"
38+
name = "vSphere Virtual Machine"
39+
slug = "vsphere-virtual_machine"
40+
}
3641
}

datasource/virtual_machine/data.go

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
//go:generate packer-sdc struct-markdown
5+
//go:generate packer-sdc mapstructure-to-hcl2 -type Config,Tag,DatasourceOutput
6+
package virtual_machine
7+
8+
import (
9+
"github.com/hashicorp/hcl/v2/hcldec"
10+
"github.com/hashicorp/packer-plugin-sdk/common"
11+
"github.com/hashicorp/packer-plugin-sdk/hcl2helper"
12+
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
13+
"github.com/hashicorp/packer-plugin-sdk/template/config"
14+
vsCommon "github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/common"
15+
"github.com/pkg/errors"
16+
"github.com/zclconf/go-cty/cty"
17+
)
18+
19+
// Example of multiple vm_tags blocks in HCL format:
20+
// ```
21+
//
22+
// vm_tags {
23+
// category = "team"
24+
// name = "operations"
25+
// }
26+
// vm_tags {
27+
// category = "SLA"
28+
// name = "gold"
29+
// }
30+
//
31+
// ```
32+
type Tag struct {
33+
// Tag with this name must be attached to virtual machine which should pass the Tags Filter.
34+
Name string `mapstructure:"name" required:"true"`
35+
// Name of the category that contains this tag. Both tag and category must be specified.
36+
Category string `mapstructure:"category" required:"true"`
37+
}
38+
39+
type Config struct {
40+
common.PackerConfig `mapstructure:",squash"`
41+
vsCommon.ConnectConfig `mapstructure:",squash"`
42+
43+
// Basic filter with glob support (e.g. `nginx_basic*`). Defaults to `*`.
44+
// Using strict globs will not reduce execution time because vSphere API returns the full inventory.
45+
// But can be used for better readability over regular expressions.
46+
Name string `mapstructure:"name"`
47+
// Extended name filter with regular expressions support (e.g. `nginx[-_]basic[0-9]*`). Default is empty.
48+
// The match of the regular expression is checked by substring. Use `^` and `$` to define a full string.
49+
// E.g. the `^[^_]+$` filter will search names without any underscores.
50+
// The expression must use [Go Regex Syntax](https://pkg.go.dev/regexp/syntax).
51+
NameRegex string `mapstructure:"name_regex"`
52+
// Filter to return only objects that are virtual machine templates.
53+
// Defaults to `false` and returns all VMs.
54+
Template bool `mapstructure:"template"`
55+
// Filter to search virtual machines only on the specified node.
56+
Node string `mapstructure:"node"`
57+
// Filter to return only that virtual machines that have attached all specifies tags.
58+
// Specify one or more `vm_tags` blocks to define list of tags that will make up the filter.
59+
// Should work since vCenter 6.7. To avoid incompatibility, REST client is being
60+
// initialized only when at least one tag has been defined in the config.
61+
VmTags []Tag `mapstructure:"vm_tags"`
62+
// This filter determines how to handle multiple machines that were matched with all
63+
// previous filters. Machine creation time is being used to find latest.
64+
// By default, multiple matching machines results in an error.
65+
Latest bool `mapstructure:"latest"`
66+
}
67+
68+
type Datasource struct {
69+
config Config
70+
}
71+
72+
type DatasourceOutput struct {
73+
// Name of the found virtual machine.
74+
VmName string `mapstructure:"vm_name"`
75+
}
76+
77+
func (d *Datasource) ConfigSpec() hcldec.ObjectSpec {
78+
return d.config.FlatMapstructure().HCL2Spec()
79+
}
80+
81+
func (d *Datasource) Configure(raws ...interface{}) error {
82+
err := config.Decode(&d.config, nil, raws...)
83+
if err != nil {
84+
return err
85+
}
86+
87+
if d.config.Name == "" {
88+
d.config.Name = "*"
89+
}
90+
91+
var errs *packersdk.MultiError
92+
if d.config.VCenterServer == "" {
93+
errs = packersdk.MultiErrorAppend(errs, errors.New("'vcenter_server' is required"))
94+
}
95+
if d.config.Username == "" {
96+
errs = packersdk.MultiErrorAppend(errs, errors.New("'username' is required"))
97+
}
98+
if d.config.Password == "" {
99+
errs = packersdk.MultiErrorAppend(errs, errors.New("'password' is required"))
100+
}
101+
if len(d.config.VmTags) > 0 {
102+
for _, tag := range d.config.VmTags {
103+
if tag.Name == "" || tag.Category == "" {
104+
errs = packersdk.MultiErrorAppend(errs, errors.New("both name and category are required for tag"))
105+
}
106+
}
107+
}
108+
109+
if errs != nil && len(errs.Errors) > 0 {
110+
return errs
111+
}
112+
113+
return nil
114+
}
115+
116+
func (d *Datasource) OutputSpec() hcldec.ObjectSpec {
117+
return (&DatasourceOutput{}).FlatMapstructure().HCL2Spec()
118+
}
119+
120+
func (d *Datasource) Execute() (cty.Value, error) {
121+
driver, err := newDriver(d.config)
122+
if err != nil {
123+
return cty.NullVal(cty.EmptyObject), errors.Wrap(err, "failed to initialize driver")
124+
}
125+
126+
// This is the first level of filters
127+
// (the finder with glob will return filtered list or drop an error if found nothing).
128+
filteredVms, err := driver.finder.VirtualMachineList(driver.ctx, d.config.Name)
129+
if err != nil {
130+
return cty.NullVal(cty.EmptyObject), errors.Wrap(err, "failed to retrieve virtual machines list")
131+
}
132+
133+
// Chain of other filters that will be executed only when defined
134+
// and previous filter in chain left some virtual machines in the list.
135+
if d.config.NameRegex != "" {
136+
filteredVms = filterByNameRegex(filteredVms, d.config.NameRegex)
137+
}
138+
139+
if len(filteredVms) > 0 && d.config.Template {
140+
filteredVms, err = filterByTemplate(driver, filteredVms)
141+
if err != nil {
142+
return cty.NullVal(cty.EmptyObject), errors.Wrap(err, "failed to filter by template attribute")
143+
}
144+
}
145+
146+
if len(filteredVms) > 0 && d.config.Node != "" {
147+
filteredVms, err = filterByNode(driver, d.config, filteredVms)
148+
if err != nil {
149+
return cty.NullVal(cty.EmptyObject), errors.Wrap(err, "failed to filter by node attribute")
150+
}
151+
}
152+
153+
if len(filteredVms) > 0 && d.config.VmTags != nil {
154+
filteredVms, err = filterByTags(driver, d.config.VmTags, filteredVms)
155+
if err != nil {
156+
return cty.NullVal(cty.EmptyObject), errors.Wrap(err, "failed to filter by tags")
157+
}
158+
}
159+
160+
// No VMs passed the filter chain. Nothing to return.
161+
if len(filteredVms) == 0 {
162+
return cty.NullVal(cty.EmptyObject), errors.New("not a single VM matches the configured filters")
163+
}
164+
165+
if len(filteredVms) > 1 {
166+
if d.config.Latest {
167+
filteredVms, err = filterByLatest(driver, filteredVms)
168+
if err != nil {
169+
return cty.NullVal(cty.EmptyObject), errors.Wrap(err, "failed to find the latest VM")
170+
}
171+
} else {
172+
// Too many machines passed the filter chain. Cannot decide which machine to return.
173+
return cty.NullVal(cty.EmptyObject), errors.New("multiple VMs match the configured filters")
174+
}
175+
}
176+
177+
output := DatasourceOutput{
178+
VmName: filteredVms[0].Name(),
179+
}
180+
181+
return hcl2helper.HCL2ValueFromConfig(output, d.OutputSpec()), nil
182+
}

0 commit comments

Comments
 (0)