Skip to content

Commit 621fb0d

Browse files
authored
Fix image_profile resource read issue (#238)
* Fix image_profile resource read issue * Fix image_profile resource read issue - allows the import to function correctly * Add data source for image_profile * Add example for image_profile resource and data source Fixes #234. Signed-off-by: Deepak Mettem <[email protected]>
1 parent 0372717 commit 621fb0d

15 files changed

+810
-113
lines changed

examples/image_profile/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# image_profile example
2+
3+
This is an example on how to create an image profile with an image id in vRealie Automation.
4+
Image profile represents a structure that holds a list of image mappings defined for the particular region.
5+
Flavor profile represents a structure that holds flavor mappings defined for the corresponding cloud end-point region.
6+
7+
## Getting Started
8+
9+
There are variables which need to be added to terraform.tfvars. The first are for connecting to the vRealize Automation (vRA) endpoint. There are names of cloud_account, region, zone, project, already setup in vRA.
10+
11+
* `url` - The URL for the vRealize Automation (vRA) endpoint
12+
* `refresh_token` - The refresh token (API token) for the vRA user account
13+
* `cloud_account` - The name of the cloud account added in vRA
14+
* `region` - The region within in the cloud account. For vSphere, it is the externalRegionId such as `Datacenter:datacenter-2` and for AWS, it is region name such as `us-east-1`, etc.
15+
* `image_name1` - The name of the fabric image corresponding to the cloud endpoint, such as ami-id for AWS, template name for vSphere, etc.
16+
* `image_name2` - The name of the fabric image corresponding to the cloud endpoint, such as ami-id for AWS, template name for vSphere, etc.
17+
18+
To facilitate adding these variables, a sample tfvars file can be copied first:
19+
```shell
20+
cp terraform.tfvars.sample terraform.tfvars
21+
```
22+
23+
Follow these examples for setting up specific cloud accounts:
24+
25+
* Setup [cloud\_account\_aws](../cloud_account_aws/README.md)
26+
* Setup [cloud\_account\_azure](../cloud_account_azure/README.md)
27+
* Setup [cloud\_account\_gcp](../cloud_account_gcp/README.md)
28+
* Setup [cloud\_account\_vmc](../cloud_account_vmc/README.md)
29+
* Setup [cloud\_account\_vsphere](../cloud_account_vsphere/README.md)
30+
31+
Once the information is added to `terraform.tfvars`, the image profile can be created via:
32+
33+
```shell
34+
terraform init
35+
terraform apply
36+
```

examples/image_profile/main.tf

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
provider "vra" {
2+
url = var.url
3+
refresh_token = var.refresh_token
4+
}
5+
6+
data "vra_cloud_account_vsphere" "this" {
7+
name = var.cloud_account
8+
}
9+
10+
data "vra_region" "this" {
11+
cloud_account_id = data.vra_cloud_account_vsphere.this.id
12+
region = var.region
13+
}
14+
15+
data "vra_image" "centos" {
16+
filter = "name eq '${var.image_name1}' and cloudAccountId eq '${data.vra_cloud_account_vsphere.this.id}' and externalRegionId eq '${var.region}'"
17+
}
18+
19+
data "vra_image" "photon" {
20+
filter = "name eq '${var.image_name2}' and cloudAccountId eq '${data.vra_cloud_account_vsphere.this.id}'"
21+
}
22+
23+
resource "vra_image_profile" "this" {
24+
name = "vra-image-profile"
25+
description = "test image profile"
26+
region_id = data.vra_region.this.id
27+
28+
image_mapping {
29+
name = "centos"
30+
image_id = data.vra_image.centos.id
31+
32+
constraints {
33+
mandatory = true
34+
expression = "!env:Test"
35+
}
36+
constraints {
37+
mandatory = false
38+
expression = "foo:bar"
39+
}
40+
}
41+
42+
image_mapping {
43+
name = "photon"
44+
image_id = data.vra_image.photon.id
45+
46+
cloud_config = "runcmd echo 'Hello'"
47+
}
48+
}
49+
50+
// Image profile data source by region id
51+
data "vra_image_profile" "this" {
52+
region_id = vra_image_profile.this.region_id
53+
54+
depends_on = [vra_image_profile.this]
55+
}
56+
57+
// Image profile data source by name
58+
data "vra_image_profile" "name" {
59+
name = vra_image_profile.this.name
60+
61+
depends_on = [vra_image_profile.this]
62+
}
63+
64+
// Image profile data source by filter
65+
data "vra_image_profile" "filter" {
66+
filter = "regionId eq '${vra_image_profile.this.region_id}'"
67+
68+
depends_on = [vra_image_profile.this]
69+
}
70+
71+
// Image profile data source by id
72+
data "vra_image_profile" "id" {
73+
id = vra_image_profile.this.id
74+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
refresh_token = ""
2+
url = ""
3+
cloud_account = ""
4+
region = ""
5+
image_name1 = ""
6+
image_name2 = ""
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
variable "refresh_token" {
2+
}
3+
4+
variable "url" {
5+
}
6+
7+
variable "cloud_account" {
8+
}
9+
10+
variable "region" {
11+
}
12+
13+
variable "image_name1" {
14+
15+
}
16+
17+
variable "image_name2" {
18+
19+
}

examples/image_profile/versions.tf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
terraform {
2+
required_providers {
3+
vra = {
4+
source = "terraform-providers/vra"
5+
}
6+
}
7+
required_version = ">= 0.12"
8+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ require (
66
github.com/go-openapi/runtime v0.19.15
77
github.com/go-openapi/strfmt v0.19.5
88
github.com/hashicorp/terraform-plugin-sdk v1.15.0
9-
github.com/vmware/vra-sdk-go v0.2.10
9+
github.com/vmware/vra-sdk-go v0.2.11
1010
)

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,8 @@ github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4A
276276
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
277277
github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU=
278278
github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
279-
github.com/vmware/vra-sdk-go v0.2.10 h1:nw1NSroINtw8lnaGUtTBuvDWEepBjEkTjEukYSqfJxM=
280-
github.com/vmware/vra-sdk-go v0.2.10/go.mod h1:W6OERth9ssa4FgvS98SOVusGgBNWJs+D4Hq/5ueTgIQ=
279+
github.com/vmware/vra-sdk-go v0.2.11 h1:QSnlKItvEmnhd9yfaCLesTlb/I928F/OhQ7LpaGljyI=
280+
github.com/vmware/vra-sdk-go v0.2.11/go.mod h1:W6OERth9ssa4FgvS98SOVusGgBNWJs+D4Hq/5ueTgIQ=
281281
github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
282282
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
283283
github.com/zclconf/go-cty v1.2.1 h1:vGMsygfmeCl4Xb6OA5U5XVAaQZ69FvoG7X2jUtQujb8=

vra/constraints.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,20 @@ import (
99
// constraintsSchema returns the schema to use for the constraints property
1010
func constraintsSchema() *schema.Schema {
1111
return &schema.Schema{
12-
Type: schema.TypeSet,
13-
Optional: true,
12+
Type: schema.TypeSet,
13+
Optional: true,
14+
Description: "Constraints that are used to drive placement policies for entities such as image, network, storage, etc. Constraint expressions are matched against tags on existing placement targets.",
1415
Elem: &schema.Resource{
1516
Schema: map[string]*schema.Schema{
1617
"mandatory": {
17-
Type: schema.TypeBool,
18-
Required: true,
18+
Type: schema.TypeBool,
19+
Required: true,
20+
Description: "Indicates whether this constraint should be strictly enforced or not.",
1921
},
2022
"expression": {
21-
Type: schema.TypeString,
22-
Required: true,
23+
Type: schema.TypeString,
24+
Required: true,
25+
Description: "An expression of the form \"[!]tag-key[:[tag-value]]\", used to indicate a constraint match on keys and values of tags.",
2326
},
2427
},
2528
},

vra/data_source_image_profile.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package vra
2+
3+
import (
4+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
5+
"github.com/vmware/vra-sdk-go/pkg/client/image_profile"
6+
"github.com/vmware/vra-sdk-go/pkg/models"
7+
8+
"fmt"
9+
"log"
10+
"strings"
11+
)
12+
13+
func dataSourceImageProfile() *schema.Resource {
14+
return &schema.Resource{
15+
Read: dataSourceImageProfileRead,
16+
17+
Schema: map[string]*schema.Schema{
18+
"created_at": {
19+
Type: schema.TypeString,
20+
Computed: true,
21+
Description: "Date when the entity was created. The date is in ISO 8601 and UTC.",
22+
},
23+
"description": {
24+
Type: schema.TypeString,
25+
Optional: true,
26+
Description: "A human-friendly description.",
27+
},
28+
"external_region_id": {
29+
Type: schema.TypeString,
30+
Computed: true,
31+
Description: "The id of the region for which this profile is defined as in the cloud provider.",
32+
},
33+
"filter": {
34+
Type: schema.TypeString,
35+
Optional: true,
36+
Description: "Filter query string that is supported by vRA multi-cloud IaaS API. Example: regionId eq '<regionId>' and cloudAccountId eq '<cloudAccountId>'. Only one of 'filter', 'id', 'name' or 'region_id' must be specified.",
37+
ConflictsWith: []string{"id", "name", "region_id"},
38+
},
39+
"id": {
40+
Type: schema.TypeString,
41+
Optional: true,
42+
Computed: true,
43+
Description: "The id of the image profile instance. Only one of 'filter', 'id', 'name' or 'region_id' must be specified.",
44+
ConflictsWith: []string{"filter", "name", "region_id"},
45+
},
46+
"image_mapping": imageMappingSchema(),
47+
"name": {
48+
Type: schema.TypeString,
49+
Optional: true,
50+
Computed: true,
51+
Description: "A human-friendly name used as an identifier in APIs that support this option. Only one of 'filter', 'id', 'name' or 'region_id' must be specified.",
52+
ConflictsWith: []string{"filter", "id", "region_id"},
53+
},
54+
"owner": {
55+
Type: schema.TypeString,
56+
Computed: true,
57+
Description: "Email of the user that owns the entity.",
58+
},
59+
"region_id": {
60+
Type: schema.TypeString,
61+
Optional: true,
62+
Computed: true,
63+
Description: "The id of the region for which this profile is defined as in vRealize Automation(vRA). Only one of 'filter', 'id', 'name' or 'region_id' must be specified.",
64+
ConflictsWith: []string{"filter", "id", "name"},
65+
},
66+
"updated_at": {
67+
Type: schema.TypeString,
68+
Computed: true,
69+
Description: "Date when the entity was last updated. The date is ISO 8601 and UTC.",
70+
},
71+
},
72+
}
73+
}
74+
75+
func dataSourceImageProfileRead(d *schema.ResourceData, m interface{}) error {
76+
log.Printf("Reading the vra_image_profile data source with filter %s", d.Get("filter"))
77+
78+
apiClient := m.(*Client).apiClient
79+
var imageProfile *models.ImageProfile
80+
var filter string
81+
82+
id := d.Get("id").(string)
83+
name := d.Get("name").(string)
84+
configFilter := d.Get("filter").(string)
85+
regionID := d.Get("region_id").(string)
86+
87+
if id == "" && name == "" && configFilter == "" && regionID == "" {
88+
return fmt.Errorf("one of (id, name, region_id, filter) is required")
89+
}
90+
91+
setFields := func(account *models.ImageProfile) error {
92+
d.SetId(*account.ID)
93+
d.Set("created_at", imageProfile.CreatedAt)
94+
d.Set("description", imageProfile.Description)
95+
d.Set("external_region_id", imageProfile.ExternalRegionID)
96+
d.Set("name", imageProfile.Name)
97+
d.Set("owner", imageProfile.Owner)
98+
d.Set("updated_at", imageProfile.UpdatedAt)
99+
100+
if regionLink, ok := imageProfile.Links["region"]; ok {
101+
if regionLink.Href != "" {
102+
d.Set("region_id", strings.TrimPrefix(regionLink.Href, "/iaas/api/regions/"))
103+
}
104+
}
105+
106+
if err := d.Set("image_mapping", flattenImageMappings(imageProfile.ImageMappings.Mapping)); err != nil {
107+
return fmt.Errorf("error setting image mappings - error: %#v", err)
108+
}
109+
return nil
110+
}
111+
112+
if id != "" {
113+
getResp, err := apiClient.ImageProfile.GetImageProfile(image_profile.NewGetImageProfileParams().WithID(id))
114+
115+
if err != nil {
116+
return err
117+
}
118+
119+
imageProfile = getResp.GetPayload()
120+
return setFields(imageProfile)
121+
122+
} else if regionID != "" {
123+
filter = fmt.Sprintf("regionId eq '%v'", regionID)
124+
} else if name != "" {
125+
filter = fmt.Sprintf("name eq '%v'", name)
126+
} else if configFilter != "" {
127+
filter = configFilter
128+
}
129+
130+
getResp, err := apiClient.ImageProfile.GetImageProfiles(image_profile.NewGetImageProfilesParams().WithDollarFilter(withString(filter)))
131+
if err != nil {
132+
return err
133+
}
134+
135+
imageProfiles := *getResp.Payload
136+
if len(imageProfiles.Content) > 1 {
137+
return fmt.Errorf("vra_image_profile must filter to an image profile")
138+
}
139+
if len(imageProfiles.Content) == 0 {
140+
return fmt.Errorf("vra_image_profile filter did not match any image profile")
141+
}
142+
143+
imageProfile = imageProfiles.Content[0]
144+
145+
return setFields(imageProfile)
146+
}

0 commit comments

Comments
 (0)