Skip to content

Commit 42c302d

Browse files
authored
Merge pull request #498 from castorsky/feature/datasource-virtual_machine
feat: add datasource for virtual machines
2 parents cc8ebd4 + d5d1e04 commit 42c302d

File tree

30 files changed

+1563
-187
lines changed

30 files changed

+1563
-187
lines changed

.web-docs/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ 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-virtualmachine](/packer/integrations/hashicorp/vsphere/latest/components/data-source/vsphere-virtualmachine) -
55+
This data source returns the name of a virtual machine that matches all defined filters.
56+
5257
#### Post-Processors
5358

5459
- [vsphere](/packer/integrations/hashicorp/vsphere/latest/components/post-processor/vsphere) -
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
Type: `vsphere-virtualmachine`
2+
Artifact BuilderId: `vsphere.virtualmachine`
3+
4+
This data source retrieves information about existing virtual machines from vSphere
5+
and returns the name of a virtual machine that matches all specified filters. This
6+
virtual machine can be used in the vSphere Clone builder to select a 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/virtualmachine/data.go; DO NOT EDIT MANUALLY -->
15+
16+
- `name` (string) - Basic filter with glob support (e.g. `ubuntu_basic*`). Defaults to `*`.
17+
Using strict globs will not reduce execution time because vSphere API
18+
returns the full inventory. But can be used for better readability over
19+
regular expressions.
20+
21+
- `name_regex` (string) - Extended name filter with regular expressions support
22+
(e.g. `ubuntu[-_]basic[0-9]*`). Default is empty. The match of the
23+
regular expression is checked by substring. Use `^` and `$` to define a
24+
full string. For example, the `^[^_]+$` filter will search names
25+
without any underscores. The expression must use
26+
[Go Regex Syntax](https://pkg.go.dev/regexp/syntax).
27+
28+
- `template` (bool) - Filter to return only objects that are virtual machine templates.
29+
Defaults to `false` and returns all virtual machines.
30+
31+
- `host` (string) - Filter to search virtual machines only on the specified ESX host.
32+
33+
- `tag` ([]Tag) - Filter to return only the virtual machines that have attached all specified tags.
34+
Specify one or more `tag` blocks to define list of tags for the filter.
35+
36+
HCL Example:
37+
38+
```hcl
39+
tag {
40+
category = "team"
41+
name = "operations"
42+
}
43+
tag {
44+
category = "sla"
45+
name = "gold"
46+
}
47+
```
48+
49+
- `latest` (bool) - This filter determines how to handle multiple virtual machines that were matched
50+
with all previous filters. Virtual machine creation time is being used to find
51+
the latest. By default, multiple matching machines results in an error.
52+
53+
<!-- End of code generated from the comments of the Config struct in datasource/virtualmachine/data.go; -->
54+
55+
56+
### Tags Filter Configuration
57+
58+
**Required:**
59+
60+
<!-- Code generated from the comments of the Tag struct in datasource/virtualmachine/data.go; DO NOT EDIT MANUALLY -->
61+
62+
- `name` (string) - Name of the tag added to virtual machine which must pass the `tag`
63+
filter.
64+
65+
- `category` (string) - Name of the tag category that contains the tag.
66+
67+
-> **Note:** Both `name` and `category` must be specified in the `tag`
68+
filter.
69+
70+
<!-- End of code generated from the comments of the Tag struct in datasource/virtualmachine/data.go; -->
71+
72+
73+
### Connection Configuration
74+
75+
**Optional:**
76+
77+
<!-- Code generated from the comments of the ConnectConfig struct in builder/vsphere/common/step_connect.go; DO NOT EDIT MANUALLY -->
78+
79+
- `vcenter_server` (string) - The fully qualified domain name or IP address of the vCenter instance
80+
instance.
81+
82+
- `username` (string) - The username to authenticate with the vCenter instance.
83+
84+
- `password` (string) - The password to authenticate with the vCenter instance.
85+
86+
- `insecure_connection` (bool) - Do not validate the certificate of the vCenter instance.
87+
Defaults to `false`.
88+
89+
-> **Note:** This option is beneficial in scenarios where the certificate
90+
is self-signed or does not meet standard validation criteria.
91+
92+
- `datacenter` (string) - The name of the datacenter object in the vSphere inventory.
93+
94+
-> **Note:** Required if more than one datacenter object exists in the
95+
vSphere inventory.
96+
97+
<!-- End of code generated from the comments of the ConnectConfig struct in builder/vsphere/common/step_connect.go; -->
98+
99+
100+
## Output
101+
102+
<!-- Code generated from the comments of the DatasourceOutput struct in datasource/virtualmachine/data.go; DO NOT EDIT MANUALLY -->
103+
104+
- `vm_name` (string) - Name of the found virtual machine.
105+
106+
<!-- End of code generated from the comments of the DatasourceOutput struct in datasource/virtualmachine/data.go; -->
107+
108+
109+
## Example Usage
110+
111+
### Filter by Tags and Get Latest
112+
113+
This example demonstrates how to connect to vSphere cluster and search for the latest
114+
virtual machine that matches the specified tag filters.
115+
116+
```hcl
117+
data "vsphere-virtualmachine" "example" {
118+
vcenter_server = "vcenter.example.com"
119+
username = "administrator@vsphere.local"
120+
password = "VMware1!"
121+
datacenter = "dc-01"
122+
latest = true
123+
124+
tag {
125+
category = "team"
126+
name = "operations"
127+
}
128+
129+
tag {
130+
category = "sla"
131+
name = "gold"
132+
}
133+
}
134+
135+
locals {
136+
vm_name = data.vsphere-virtualmachine.example.vm_name
137+
}
138+
139+
source "null" "example" {
140+
communicator = "none"
141+
}
142+
143+
build {
144+
sources = [
145+
"source.null.example"
146+
]
147+
148+
provisioner "shell-local" {
149+
inline = [
150+
"echo vm_name: ${local.vm_name}",
151+
]
152+
}
153+
}
154+
```
155+
156+
### Filter by Name with Glob Pattern
157+
158+
This example shows how to use the `name` filter with glob patterns to find virtual
159+
machines matching a specific naming convention.
160+
161+
```hcl
162+
data "vsphere-virtualmachine" "example" {
163+
vcenter_server = "vcenter.example.com"
164+
username = "administrator@vsphere.local"
165+
password = "VMware1!"
166+
datacenter = "dc-01"
167+
name = "linux-debian-13-*"
168+
latest = true
169+
}
170+
171+
locals {
172+
vm_result = data.vsphere-virtualmachine.example.vm_name
173+
}
174+
```
175+
176+
### Filter by Name with Regular Expression
177+
178+
This example demonstrates using the `name_regex` filter for more complex pattern
179+
matching with regular expressions.
180+
181+
```hcl
182+
data "vsphere-virtualmachine" "example" {
183+
vcenter_server = "vcenter.example.com"
184+
username = "administrator@vsphere.local"
185+
password = "VMware1!"
186+
datacenter = "dc-01"
187+
name_regex = "^linux-debian-[0-9]+-.+$"
188+
latest = true
189+
}
190+
191+
locals {
192+
vm_result = data.vsphere-virtualmachine.example.vm_name
193+
}
194+
```
195+
196+
### Filter for VM Templates Only
197+
198+
This example shows how to use the `template` filter to search only for virtual
199+
machine templates.
200+
201+
```hcl
202+
data "vsphere-virtualmachine" "example" {
203+
vcenter_server = "vcenter.example.com"
204+
username = "administrator@vsphere.local"
205+
password = "VMware1!"
206+
datacenter = "dc-01"
207+
name = "linux-debian-13-*"
208+
template = true
209+
}
210+
211+
locals {
212+
result_template = data.vsphere-virtualmachine.example.vm_name
213+
}
214+
```
215+
216+
### Filter by ESX Host
217+
218+
This example demonstrates filtering virtual machines by a specific ESX host,
219+
combined with other filters.
220+
221+
```hcl
222+
data "vsphere-virtualmachine" "example" {
223+
vcenter_server = "vcenter.example.com"
224+
username = "administrator@vsphere.local"
225+
password = "VMware1!"
226+
datacenter = "dc-01"
227+
host = "esxi-01.example.com"
228+
name = "linux-debian-13-*"
229+
latest = true
230+
}
231+
232+
locals {
233+
vm_result = data.vsphere-virtualmachine.example.vm_name
234+
}
235+
```

.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-virtualmachine"
40+
}
3641
}

builder/vsphere/common/step_connect.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ type StepConnect struct {
5959
}
6060

6161
func (s *StepConnect) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
62+
// Convert common.ConnectConfig to driver.ConnectConfig
63+
// Both structs have identical fields to avoid import cycles
6264
d, err := driver.NewDriver(&driver.ConnectConfig{
6365
VCenterServer: s.Config.VCenterServer,
6466
Username: s.Config.Username,

builder/vsphere/driver/cluster.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type Cluster struct {
1414
// Returns a Cluster object or an error if not found or if the retrieval
1515
// process fails.
1616
func (d *VCenterDriver) FindCluster(name string) (*Cluster, error) {
17-
c, err := d.finder.ClusterComputeResource(d.ctx, name)
17+
c, err := d.Finder.ClusterComputeResource(d.Ctx, name)
1818
if err != nil {
1919
return nil, err
2020
}

builder/vsphere/driver/datastore.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type DatastoreDriver struct {
3737
// NewDatastore creates a new Datastore object.
3838
func (d *VCenterDriver) NewDatastore(ref *types.ManagedObjectReference) Datastore {
3939
return &DatastoreDriver{
40-
ds: object.NewDatastore(d.client.Client, *ref),
40+
ds: object.NewDatastore(d.Client.Client, *ref),
4141
driver: d,
4242
}
4343
}
@@ -68,7 +68,7 @@ func (d *VCenterDriver) FindDatastore(name string, host string) (Datastore, erro
6868
name = inf.Name
6969
}
7070

71-
ds, err := d.finder.Datastore(d.ctx, name)
71+
ds, err := d.Finder.Datastore(d.Ctx, name)
7272
if err != nil {
7373
return nil, fmt.Errorf("error finding datastore with name %s: %s", name, err)
7474
}
@@ -87,10 +87,10 @@ func (d *VCenterDriver) GetDatastoreName(id string) (string, error) {
8787
Type: "Datastore",
8888
Value: id,
8989
}
90-
pc := property.DefaultCollector(d.vimClient)
90+
pc := property.DefaultCollector(d.VimClient)
9191
var me mo.ManagedEntity
9292

93-
err := pc.RetrieveOne(d.ctx, obj, []string{"name"}, &me)
93+
err := pc.RetrieveOne(d.Ctx, obj, []string{"name"}, &me)
9494
if err != nil {
9595
return id, err
9696
}
@@ -108,22 +108,22 @@ func (ds *DatastoreDriver) Info(params ...string) (*mo.Datastore, error) {
108108
p = params
109109
}
110110
var info mo.Datastore
111-
err := ds.ds.Properties(ds.driver.ctx, ds.ds.Reference(), p, &info)
111+
err := ds.ds.Properties(ds.driver.Ctx, ds.ds.Reference(), p, &info)
112112
if err != nil {
113113
return nil, err
114114
}
115115
return &info, nil
116116
}
117117

118118
func (ds *DatastoreDriver) DirExists(filepath string) bool {
119-
_, err := ds.ds.Stat(ds.driver.ctx, filepath)
119+
_, err := ds.ds.Stat(ds.driver.Ctx, filepath)
120120
var notFoundError *object.DatastoreNoSuchDirectoryError
121121
return !errors.As(err, &notFoundError)
122122
}
123123

124124
// FileExists checks if a file exists in a datastore.
125125
func (ds *DatastoreDriver) FileExists(path string) bool {
126-
_, err := ds.ds.Stat(ds.driver.ctx, path)
126+
_, err := ds.ds.Stat(ds.driver.Ctx, path)
127127
return err == nil
128128
}
129129

@@ -145,9 +145,9 @@ func (ds *DatastoreDriver) ResolvePath(path string) string {
145145
// GetDatastoreFilePath retrieves the full path of a file in a specified datastore directory by its datastore ID and name.
146146
func (d *VCenterDriver) GetDatastoreFilePath(datastoreID, dir, filename string) (string, error) {
147147
ref := types.ManagedObjectReference{Type: "Datastore", Value: datastoreID}
148-
ds := object.NewDatastore(d.vimClient, ref)
148+
ds := object.NewDatastore(d.VimClient, ref)
149149

150-
b, err := ds.Browser(d.ctx)
150+
b, err := ds.Browser(d.Ctx)
151151
if err != nil {
152152
return filename, err
153153
}
@@ -157,12 +157,12 @@ func (d *VCenterDriver) GetDatastoreFilePath(datastoreID, dir, filename string)
157157
MatchPattern: []string{pat},
158158
}
159159

160-
task, err := b.SearchDatastore(d.ctx, dir, &spec)
160+
task, err := b.SearchDatastore(d.Ctx, dir, &spec)
161161
if err != nil {
162162
return filename, err
163163
}
164164

165-
info, err := task.WaitForResult(d.ctx, nil)
165+
info, err := task.WaitForResult(d.Ctx, nil)
166166
if err != nil {
167167
return filename, err
168168
}
@@ -182,7 +182,7 @@ func (d *VCenterDriver) GetDatastoreFilePath(datastoreID, dir, filename string)
182182
// in the datastore, with optional host context.
183183
func (ds *DatastoreDriver) UploadFile(src, dst, host string, setHost bool) error {
184184
p := soap.DefaultUpload
185-
ctx := ds.driver.ctx
185+
ctx := ds.driver.Ctx
186186

187187
if setHost && host != "" {
188188
h, err := ds.driver.FindHost(host)
@@ -197,22 +197,22 @@ func (ds *DatastoreDriver) UploadFile(src, dst, host string, setHost bool) error
197197

198198
// Delete deletes a file from a datastore by a path.
199199
func (ds *DatastoreDriver) Delete(path string) error {
200-
dc, err := ds.driver.finder.Datacenter(ds.driver.ctx, ds.ds.DatacenterPath)
200+
dc, err := ds.driver.Finder.Datacenter(ds.driver.Ctx, ds.ds.DatacenterPath)
201201
if err != nil {
202202
return err
203203
}
204204
fm := ds.ds.NewFileManager(dc, false)
205-
return fm.Delete(ds.driver.ctx, path)
205+
return fm.Delete(ds.driver.Ctx, path)
206206
}
207207

208208
// MakeDirectory creates a directory in a datastore by a path.
209209
func (ds *DatastoreDriver) MakeDirectory(path string) error {
210-
dc, err := ds.driver.finder.Datacenter(ds.driver.ctx, ds.ds.DatacenterPath)
210+
dc, err := ds.driver.Finder.Datacenter(ds.driver.Ctx, ds.ds.DatacenterPath)
211211
if err != nil {
212212
return err
213213
}
214214
fm := ds.ds.NewFileManager(dc, false)
215-
return fm.FileManager.MakeDirectory(ds.driver.ctx, path, dc, true)
215+
return fm.FileManager.MakeDirectory(ds.driver.Ctx, path, dc, true)
216216
}
217217

218218
// RemoveDatastorePrefix removes the datastore prefix from a path.

0 commit comments

Comments
 (0)