Skip to content

Commit 35a4db1

Browse files
authored
Added support for enterprise account sharing images (#62)
Signed-off-by: Ujjwal Kumar <ujjwal.kumar1@ibm.com>
1 parent 6860797 commit 35a4db1

14 files changed

+504
-140
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ security_group_id | string | The security group identifier to use. If not specif
190190
vsi_base_image_id | string | The base image identifier used to created the VSI. Use `ibmcloud is images` for available options.
191191
| OR |
192192
vsi_base_image_name | string | The base image name used to created the VSI. Use `ibmcloud is images` for available options.
193+
| OR |
194+
catalog_offering_crn | string | The [catalog](https://cloud.ibm.com/docs/account?topic=account-restrict-by-user) offering version to use when provisioning this virtual server instance. The specified offering version may be in a different account in the same enterprise, subject to IAM policies. Identifies a [catalog](https://cloud.ibm.com/docs/account?topic=account-restrict-by-user) offering by a unique property. Optional.
195+
| OR |
196+
catalog_offering_version_crn | string | The [catalog](https://cloud.ibm.com/docs/account?topic=account-restrict-by-user) offering version to use when provisioning this virtual server instance. The specified offering version may be in a different account in the same enterprise, subject to IAM policies. Identifies a version of a [catalog](https://cloud.ibm.com/docs/account?topic=account-restrict-by-user) offering by a unique property. Optional.
193197
| |
194198
vsi_profile | string | The profile this VSI uses. Required.
195199
vsi_interface | string | Set it as "public" to create a Floating IP to connect to the temp VSI. Set it as "private" to use private interface to connect to the temp VSI. Later option requires you run packer plugin inside your VPC.

builder/ibmcloud/vpc/config.go

+28-16
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,23 @@ type Config struct {
1818
common.PackerConfig `mapstructure:",squash"`
1919
Comm communicator.Config `mapstructure:",squash"`
2020

21-
IBMApiKey string `mapstructure:"api_key"`
22-
Region string `mapstructure:"region"`
23-
Endpoint string `mapstructure:"vpc_endpoint_url"`
24-
EncryptionKeyCRN string `mapstructure:"encryption_key_crn"`
25-
IAMEndpoint string `mapstructure:"iam_url"`
26-
Zone string `mapstructure-to-hcl2:",skip"`
27-
VPCID string `mapstructure-to-hcl2:",skip"`
28-
SubnetID string `mapstructure:"subnet_id"`
29-
ResourceGroupID string `mapstructure:"resource_group_id"`
30-
SecurityGroupID string `mapstructure:"security_group_id"`
31-
VSIBaseImageID string `mapstructure:"vsi_base_image_id"`
32-
VSIBaseImageName string `mapstructure:"vsi_base_image_name"`
33-
VSIProfile string `mapstructure:"vsi_profile"`
34-
VSIInterface string `mapstructure:"vsi_interface"`
35-
VSIUserDataFile string `mapstructure:"vsi_user_data_file"`
21+
IBMApiKey string `mapstructure:"api_key"`
22+
Region string `mapstructure:"region"`
23+
Endpoint string `mapstructure:"vpc_endpoint_url"`
24+
EncryptionKeyCRN string `mapstructure:"encryption_key_crn"`
25+
IAMEndpoint string `mapstructure:"iam_url"`
26+
Zone string `mapstructure-to-hcl2:",skip"`
27+
VPCID string `mapstructure-to-hcl2:",skip"`
28+
SubnetID string `mapstructure:"subnet_id"`
29+
CatalogOfferingCRN string `mapstructure:"catalog_offering_crn"`
30+
CatalogOfferingVersionCRN string `mapstructure:"catalog_offering_version_crn"`
31+
ResourceGroupID string `mapstructure:"resource_group_id"`
32+
SecurityGroupID string `mapstructure:"security_group_id"`
33+
VSIBaseImageID string `mapstructure:"vsi_base_image_id"`
34+
VSIBaseImageName string `mapstructure:"vsi_base_image_name"`
35+
VSIProfile string `mapstructure:"vsi_profile"`
36+
VSIInterface string `mapstructure:"vsi_interface"`
37+
VSIUserDataFile string `mapstructure:"vsi_user_data_file"`
3638

3739
ImageName string `mapstructure:"image_name"`
3840

@@ -82,7 +84,17 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
8284
}
8385

8486
if c.VSIBaseImageID == "" && c.VSIBaseImageName == "" {
85-
errs = packer.MultiErrorAppend(errs, errors.New("a vsi_base_image_id or vsi_base_image_name must be specified"))
87+
if c.CatalogOfferingCRN == "" && c.CatalogOfferingVersionCRN == "" {
88+
errs = packer.MultiErrorAppend(errs, errors.New("one of (vsi_base_image_id or vsi_base_image_name) or (catalog_offering_crn or catalog_offering_version_crn) is required"))
89+
} else if c.CatalogOfferingCRN != "" && c.CatalogOfferingVersionCRN != "" {
90+
errs = packer.MultiErrorAppend(errs, errors.New("only one of (catalog_offering_crn or catalog_offering_version_crn) is required"))
91+
}
92+
} else if c.CatalogOfferingCRN == "" && c.CatalogOfferingVersionCRN == "" {
93+
if c.VSIBaseImageID != "" && c.VSIBaseImageName != "" {
94+
errs = packer.MultiErrorAppend(errs, errors.New("only one of (vsi_base_image_id or vsi_base_image_name) is required"))
95+
}
96+
} else {
97+
errs = packer.MultiErrorAppend(errs, errors.New("only one of (vsi_base_image_id or vsi_base_image_name) or (catalog_offering_crn or catalog_offering_version_crn) is required"))
8698
}
8799

88100
if c.VSIProfile == "" {

builder/ibmcloud/vpc/config.hcl2spec.go

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

builder/ibmcloud/vpc/step_create_instance.go

+129-62
Original file line numberDiff line numberDiff line change
@@ -24,104 +24,171 @@ func (step *stepCreateInstance) Run(_ context.Context, state multistep.StateBag)
2424

2525
vsiBaseImageName := config.VSIBaseImageName
2626
vsiBaseImageID := config.VSIBaseImageID
27+
vsiCatalogOfferingCrn := config.CatalogOfferingCRN
28+
vsiCatalogOfferingVersionCrn := config.CatalogOfferingVersionCRN
2729

28-
keyIDentityModel := &vpcv1.KeyIdentityByID{
30+
keyIdentityModel := &vpcv1.KeyIdentityByID{
2931
ID: &[]string{state.Get("vpc_ssh_key_id").(string)}[0],
3032
}
3133
instanceProfileIdentityModel := &vpcv1.InstanceProfileIdentityByName{
3234
Name: &[]string{config.VSIProfile}[0],
3335
}
34-
vpcIDentityModel := &vpcv1.VPCIdentityByID{
36+
vpcIdentityModel := &vpcv1.VPCIdentityByID{
3537
ID: &[]string{state.Get("vpc_id").(string)}[0],
3638
}
37-
subnetIDentityModel := &vpcv1.SubnetIdentityByID{
39+
subnetIdentityModel := &vpcv1.SubnetIdentityByID{
3840
ID: &[]string{config.SubnetID}[0],
3941
}
4042
networkInterfacePrototypeModel := &vpcv1.NetworkInterfacePrototype{
4143
Name: &[]string{"my-instance-modified"}[0],
42-
Subnet: subnetIDentityModel,
44+
Subnet: subnetIdentityModel,
4345
}
4446
zoneIdentityModel := &vpcv1.ZoneIdentityByName{
4547
Name: &[]string{state.Get("zone").(string)}[0],
4648
}
4749

4850
ui.Say("Creating Instance...")
4951

50-
// Get Image ID
51-
if vsiBaseImageName != "" {
52-
ui.Say("Fetching ImageID...")
52+
// For catalog images
53+
if vsiCatalogOfferingCrn != "" || vsiCatalogOfferingVersionCrn != "" {
5354

54-
options := &vpcv1.ListImagesOptions{}
55-
options.SetName(vsiBaseImageName)
56-
image, _, err := vpcService.ListImages(options)
55+
catalogOfferingPrototype := &vpcv1.InstanceCatalogOfferingPrototype{}
5756

58-
if err != nil {
59-
err := fmt.Errorf("[ERROR] Error getting image with name: %s", err)
60-
state.Put("error", err)
61-
ui.Error(err.Error())
62-
return multistep.ActionHalt
57+
// offering crn
58+
if vsiCatalogOfferingCrn != "" {
59+
offering := &vpcv1.CatalogOfferingIdentityCatalogOfferingByCRN{
60+
CRN: &vsiCatalogOfferingCrn,
61+
}
62+
catalogOfferingPrototype.Offering = offering
63+
} else {
64+
versionOffering := &vpcv1.CatalogOfferingVersionIdentityCatalogOfferingVersionByCRN{
65+
CRN: &vsiCatalogOfferingVersionCrn,
66+
}
67+
catalogOfferingPrototype.Version = versionOffering
6368
}
64-
if image != nil && len(image.Images) == 0 {
65-
err := fmt.Errorf("[ERROR] Image %s not found", vsiBaseImageName)
66-
state.Put("error", err)
67-
ui.Error(err.Error())
68-
return multistep.ActionHalt
69+
instancePrototypeModel := &vpcv1.InstancePrototypeInstanceByCatalogOffering{
70+
Keys: []vpcv1.KeyIdentityIntf{keyIdentityModel},
71+
Name: &[]string{config.VSIName}[0],
72+
Profile: instanceProfileIdentityModel,
73+
VPC: vpcIdentityModel,
74+
PrimaryNetworkInterface: networkInterfacePrototypeModel,
75+
Zone: zoneIdentityModel,
6976
}
70-
vsiBaseImageID = *image.Images[0].ID
71-
ui.Say(fmt.Sprintf("ImageID fetched: %s", string(vsiBaseImageName)))
72-
}
77+
instancePrototypeModel.CatalogOffering = catalogOfferingPrototype
7378

74-
imageIDentityModel := &vpcv1.ImageIdentityByID{
75-
ID: &[]string{vsiBaseImageID}[0],
76-
}
77-
instancePrototypeModel := &vpcv1.InstancePrototypeInstanceByImage{
78-
Keys: []vpcv1.KeyIdentityIntf{keyIDentityModel},
79-
Name: &[]string{config.VSIName}[0],
80-
Profile: instanceProfileIdentityModel,
81-
VPC: vpcIDentityModel,
82-
Image: imageIDentityModel,
83-
PrimaryNetworkInterface: networkInterfacePrototypeModel,
84-
Zone: zoneIdentityModel,
85-
}
79+
userDataFilePath := config.VSIUserDataFile
80+
if userDataFilePath != "" {
81+
content, err := ioutil.ReadFile(userDataFilePath)
82+
if err != nil {
83+
err := fmt.Errorf("[ERROR] Error reading user data file. Error: %s", err)
84+
state.Put("error", err)
85+
ui.Error(err.Error())
86+
return multistep.ActionHalt
87+
}
88+
instancePrototypeModel.UserData = &[]string{string(content)}[0]
89+
}
90+
91+
if config.ResourceGroupID != "" {
92+
instancePrototypeModel.ResourceGroup = &vpcv1.ResourceGroupIdentityByID{
93+
ID: &config.ResourceGroupID,
94+
}
95+
}
96+
97+
state.Put("instance_definition", *instancePrototypeModel)
8698

87-
userDataFilePath := config.VSIUserDataFile
88-
if userDataFilePath != "" {
89-
content, err := ioutil.ReadFile(userDataFilePath)
99+
createInstanceOptions := vpcService.NewCreateInstanceOptions(
100+
instancePrototypeModel,
101+
)
102+
instanceData, _, err := vpcService.CreateInstance(createInstanceOptions)
103+
// End
90104
if err != nil {
91-
err := fmt.Errorf("[ERROR] Error reading user data file. Error: %s", err)
105+
err := fmt.Errorf("[ERROR] Error creating the instance: %s", err)
92106
state.Put("error", err)
93107
ui.Error(err.Error())
108+
// log.Fatalf(err.Error())
94109
return multistep.ActionHalt
95110
}
96-
instancePrototypeModel.UserData = &[]string{string(content)}[0]
97-
}
111+
state.Put("instance_data", instanceData)
112+
ui.Say("Instance successfully created!")
113+
ui.Say(fmt.Sprintf("Instance's Name: %s", *instanceData.Name))
114+
ui.Say(fmt.Sprintf("Instance's ID: %s", *instanceData.ID))
115+
116+
} else {
117+
// Get Image ID
118+
if vsiBaseImageName != "" {
119+
ui.Say("Fetching ImageID...")
120+
121+
options := &vpcv1.ListImagesOptions{}
122+
options.SetName(vsiBaseImageName)
123+
image, _, err := vpcService.ListImages(options)
124+
125+
if err != nil {
126+
err := fmt.Errorf("[ERROR] Error getting image with name: %s", err)
127+
state.Put("error", err)
128+
ui.Error(err.Error())
129+
return multistep.ActionHalt
130+
}
131+
if image != nil && len(image.Images) == 0 {
132+
err := fmt.Errorf("[ERROR] Image %s not found", vsiBaseImageName)
133+
state.Put("error", err)
134+
ui.Error(err.Error())
135+
return multistep.ActionHalt
136+
}
137+
vsiBaseImageID = *image.Images[0].ID
138+
ui.Say(fmt.Sprintf("ImageID fetched: %s", string(vsiBaseImageName)))
139+
}
98140

99-
if config.ResourceGroupID != "" {
100-
instancePrototypeModel.ResourceGroup = &vpcv1.ResourceGroupIdentityByID{
101-
ID: &config.ResourceGroupID,
141+
imageIdentityModel := &vpcv1.ImageIdentityByID{
142+
ID: &[]string{vsiBaseImageID}[0],
143+
}
144+
instancePrototypeModel := &vpcv1.InstancePrototypeInstanceByImage{
145+
Keys: []vpcv1.KeyIdentityIntf{keyIdentityModel},
146+
Name: &[]string{config.VSIName}[0],
147+
Profile: instanceProfileIdentityModel,
148+
VPC: vpcIdentityModel,
149+
Image: imageIdentityModel,
150+
PrimaryNetworkInterface: networkInterfacePrototypeModel,
151+
Zone: zoneIdentityModel,
102152
}
103-
}
104153

105-
state.Put("instance_definition", *instancePrototypeModel)
154+
userDataFilePath := config.VSIUserDataFile
155+
if userDataFilePath != "" {
156+
content, err := ioutil.ReadFile(userDataFilePath)
157+
if err != nil {
158+
err := fmt.Errorf("[ERROR] Error reading user data file. Error: %s", err)
159+
state.Put("error", err)
160+
ui.Error(err.Error())
161+
return multistep.ActionHalt
162+
}
163+
instancePrototypeModel.UserData = &[]string{string(content)}[0]
164+
}
106165

107-
createInstanceOptions := vpcService.NewCreateInstanceOptions(
108-
instancePrototypeModel,
109-
)
110-
instanceData, _, err := vpcService.CreateInstance(createInstanceOptions)
111-
// End
112-
if err != nil {
113-
err := fmt.Errorf("[ERROR] Error creating the instance: %s", err)
114-
state.Put("error", err)
115-
ui.Error(err.Error())
116-
// log.Fatalf(err.Error())
117-
return multistep.ActionHalt
118-
}
166+
if config.ResourceGroupID != "" {
167+
instancePrototypeModel.ResourceGroup = &vpcv1.ResourceGroupIdentityByID{
168+
ID: &config.ResourceGroupID,
169+
}
170+
}
171+
172+
state.Put("instance_definition", *instancePrototypeModel)
119173

120-
state.Put("instance_data", instanceData)
174+
createInstanceOptions := vpcService.NewCreateInstanceOptions(
175+
instancePrototypeModel,
176+
)
177+
instanceData, _, err := vpcService.CreateInstance(createInstanceOptions)
178+
// End
179+
if err != nil {
180+
err := fmt.Errorf("[ERROR] Error creating the instance: %s", err)
181+
state.Put("error", err)
182+
ui.Error(err.Error())
183+
// log.Fatalf(err.Error())
184+
return multistep.ActionHalt
185+
}
186+
state.Put("instance_data", instanceData)
187+
ui.Say("Instance successfully created!")
188+
ui.Say(fmt.Sprintf("Instance's Name: %s", *instanceData.Name))
189+
ui.Say(fmt.Sprintf("Instance's ID: %s", *instanceData.ID))
190+
}
121191

122-
ui.Say("Instance successfully created!")
123-
ui.Say(fmt.Sprintf("Instance's Name: %s", *instanceData.Name))
124-
ui.Say(fmt.Sprintf("Instance's ID: %s", *instanceData.ID))
125192
return multistep.ActionContinue
126193
}
127194

developer/Makefile

+10-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,16 @@ validate-all:
1818
cd ..; packer validate -var-file="developer/variables.pkrvars.hcl" developer/examples/build.vpc.centos-ansible.pkr.hcl
1919
cd ..; packer validate -var-file="developer/variables.pkrvars.hcl" developer/examples/build.vpc.windows.pkr.hcl
2020
cd ..; packer validate -var-file="developer/variables.pkrvars.hcl" developer/examples/build.vpc.rhel.pkr.hcl
21-
22-
21+
cd ..; packer validate -var-file="developer/variables-encrypted.pkrvars.hcl" developer/examples/build.vpc.centos-encrypted.pkr.hcl
22+
cd ..; packer validate -var-file="developer/variables-catalog-offering.pkrvars.hcl" developer/examples/build.vpc.catalogimage-offering.pkr.hcl
23+
cd ..; packer validate -var-file="developer/variables-catalog-version.pkrvars.hcl" developer/examples/build.vpc.catalogimage-version.pkr.hcl
24+
25+
run-catalog:
26+
cd ..; packer validate -var-file="developer/variables-catalog-offering.pkrvars.hcl" developer/examples/build.vpc.catalogimage-offering.pkr.hcl
27+
cd ..; packer build -var-file="developer/variables-catalog-offering.pkrvars.hcl" developer/examples/build.vpc.catalogimage-offering.pkr.hcl
28+
cd ..; packer validate -var-file="developer/variables-catalog-version.pkrvars.hcl" developer/examples/build.vpc.catalogimage-version.pkr.hcl
29+
cd ..; packer build -var-file="developer/variables-catalog-version.pkrvars.hcl" developer/examples/build.vpc.catalogimage-version.pkr.hcl
30+
2331
run-centos:
2432
cd ..; packer validate -var-file="developer/variables.pkrvars.hcl" developer/examples/build.vpc.centos.pkr.hcl
2533
cd ..; packer build -var-file="developer/variables.pkrvars.hcl" developer/examples/build.vpc.centos.pkr.hcl

0 commit comments

Comments
 (0)