diff --git a/builder/azure/chroot/shared_image_gallery_destination.go b/builder/azure/chroot/shared_image_gallery_destination.go index 32337b072..773f65ad4 100644 --- a/builder/azure/chroot/shared_image_gallery_destination.go +++ b/builder/azure/chroot/shared_image_gallery_destination.go @@ -16,8 +16,9 @@ type SharedImageGalleryDestination struct { ImageName string `mapstructure:"image_name" required:"true"` ImageVersion string `mapstructure:"image_version" required:"true"` - TargetRegions []TargetRegion `mapstructure:"target_regions"` - ExcludeFromLatest bool `mapstructure:"exlude_from_latest"` + TargetRegions []TargetRegion `mapstructure:"target_regions"` + ExcludeFromLatest bool `mapstructure:"exclude_from_latest"` + ExcludeFromLatestTypo bool `mapstructure:"exlude_from_latest"` } // TargetRegion describes a region where the shared image should be replicated @@ -61,5 +62,10 @@ func (sigd *SharedImageGalleryDestination) Validate(prefix string) (errs []error warns = append(warns, fmt.Sprintf("%s.target_regions is empty; image will only be available in the region of the gallery", prefix)) } + if sigd.ExcludeFromLatestTypo == true && sigd.ExcludeFromLatest == false { + warns = append(warns, + fmt.Sprintf("%s.exlude_from_latest is being deprecated, please use exclude_from_latest", prefix)) + sigd.ExcludeFromLatest = sigd.ExcludeFromLatestTypo + } return } diff --git a/builder/azure/chroot/shared_image_gallery_destination.hcl2spec.go b/builder/azure/chroot/shared_image_gallery_destination.hcl2spec.go index c74f12f72..e73a8ba0c 100644 --- a/builder/azure/chroot/shared_image_gallery_destination.hcl2spec.go +++ b/builder/azure/chroot/shared_image_gallery_destination.hcl2spec.go @@ -9,12 +9,13 @@ import ( // FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatSharedImageGalleryDestination struct { - ResourceGroup *string `mapstructure:"resource_group" required:"true" cty:"resource_group" hcl:"resource_group"` - GalleryName *string `mapstructure:"gallery_name" required:"true" cty:"gallery_name" hcl:"gallery_name"` - ImageName *string `mapstructure:"image_name" required:"true" cty:"image_name" hcl:"image_name"` - ImageVersion *string `mapstructure:"image_version" required:"true" cty:"image_version" hcl:"image_version"` - TargetRegions []FlatTargetRegion `mapstructure:"target_regions" cty:"target_regions" hcl:"target_regions"` - ExcludeFromLatest *bool `mapstructure:"exlude_from_latest" cty:"exlude_from_latest" hcl:"exlude_from_latest"` + ResourceGroup *string `mapstructure:"resource_group" required:"true" cty:"resource_group" hcl:"resource_group"` + GalleryName *string `mapstructure:"gallery_name" required:"true" cty:"gallery_name" hcl:"gallery_name"` + ImageName *string `mapstructure:"image_name" required:"true" cty:"image_name" hcl:"image_name"` + ImageVersion *string `mapstructure:"image_version" required:"true" cty:"image_version" hcl:"image_version"` + TargetRegions []FlatTargetRegion `mapstructure:"target_regions" cty:"target_regions" hcl:"target_regions"` + ExcludeFromLatest *bool `mapstructure:"exclude_from_latest" cty:"exclude_from_latest" hcl:"exclude_from_latest"` + ExcludeFromLatestTypo *bool `mapstructure:"exlude_from_latest" cty:"exlude_from_latest" hcl:"exlude_from_latest"` } // FlatMapstructure returns a new FlatSharedImageGalleryDestination. @@ -29,12 +30,13 @@ func (*SharedImageGalleryDestination) FlatMapstructure() interface{ HCL2Spec() m // The decoded values from this spec will then be applied to a FlatSharedImageGalleryDestination. func (*FlatSharedImageGalleryDestination) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ - "resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false}, - "gallery_name": &hcldec.AttrSpec{Name: "gallery_name", Type: cty.String, Required: false}, - "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, - "image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false}, - "target_regions": &hcldec.BlockListSpec{TypeName: "target_regions", Nested: hcldec.ObjectSpec((*FlatTargetRegion)(nil).HCL2Spec())}, - "exlude_from_latest": &hcldec.AttrSpec{Name: "exlude_from_latest", Type: cty.Bool, Required: false}, + "resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false}, + "gallery_name": &hcldec.AttrSpec{Name: "gallery_name", Type: cty.String, Required: false}, + "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, + "image_version": &hcldec.AttrSpec{Name: "image_version", Type: cty.String, Required: false}, + "target_regions": &hcldec.BlockListSpec{TypeName: "target_regions", Nested: hcldec.ObjectSpec((*FlatTargetRegion)(nil).HCL2Spec())}, + "exclude_from_latest": &hcldec.AttrSpec{Name: "exclude_from_latest", Type: cty.Bool, Required: false}, + "exlude_from_latest": &hcldec.AttrSpec{Name: "exlude_from_latest", Type: cty.Bool, Required: false}, } return s } diff --git a/builder/azure/chroot/shared_image_gallery_destination_test.go b/builder/azure/chroot/shared_image_gallery_destination_test.go index 17cab0eef..480c8ac7b 100644 --- a/builder/azure/chroot/shared_image_gallery_destination_test.go +++ b/builder/azure/chroot/shared_image_gallery_destination_test.go @@ -21,12 +21,13 @@ func TestSharedImageGalleryDestination_ResourceID(t *testing.T) { func TestSharedImageGalleryDestination_Validate(t *testing.T) { type fields struct { - ResourceGroup string - GalleryName string - ImageName string - ImageVersion string - TargetRegions []TargetRegion - ExcludeFromLatest bool + ResourceGroup string + GalleryName string + ImageName string + ImageVersion string + TargetRegions []TargetRegion + ExcludeFromLatest bool + ExcludeFromLatestTypo bool } tests := []struct { name string @@ -66,6 +67,29 @@ func TestSharedImageGalleryDestination_Validate(t *testing.T) { }, wantWarns: []string{"sigdest.target_regions is empty; image will only be available in the region of the gallery"}, }, + { + name: "warn if using exlude_from_latest", + fields: fields{ + ResourceGroup: "ResourceGroup", + GalleryName: "GalleryName", + ImageName: "ImageName", + ImageVersion: "0.1.2", + TargetRegions: []TargetRegion{ + TargetRegion{ + Name: "region1", + ReplicaCount: 5, + StorageAccountType: "Standard_ZRS", + }, + TargetRegion{ + Name: "region2", + ReplicaCount: 3, + StorageAccountType: "Standard_LRS", + }, + }, + ExcludeFromLatestTypo: true, + }, + wantWarns: []string{"sigdest.exlude_from_latest is being deprecated, please use exclude_from_latest"}, + }, { name: "version format", wantErrs: []string{ @@ -105,12 +129,13 @@ func TestSharedImageGalleryDestination_Validate(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sigd := &SharedImageGalleryDestination{ - ResourceGroup: tt.fields.ResourceGroup, - GalleryName: tt.fields.GalleryName, - ImageName: tt.fields.ImageName, - ImageVersion: tt.fields.ImageVersion, - TargetRegions: tt.fields.TargetRegions, - ExcludeFromLatest: tt.fields.ExcludeFromLatest, + ResourceGroup: tt.fields.ResourceGroup, + GalleryName: tt.fields.GalleryName, + ImageName: tt.fields.ImageName, + ImageVersion: tt.fields.ImageVersion, + TargetRegions: tt.fields.TargetRegions, + ExcludeFromLatest: tt.fields.ExcludeFromLatest, + ExcludeFromLatestTypo: tt.fields.ExcludeFromLatestTypo, } gotErrs, gotWarns := sigd.Validate("sigdest") diff --git a/examples/azure/ubuntu-chroot.json b/examples/azure/ubuntu-chroot.json new file mode 100644 index 000000000..e7cd330f7 --- /dev/null +++ b/examples/azure/ubuntu-chroot.json @@ -0,0 +1,41 @@ +{ + "variables": { + "client_id": "{{env `ARM_CLIENT_ID`}}", + "client_secret": "{{env `ARM_CLIENT_SECRET`}}", + "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}", + "resource_group": "{{env `ARM_IMAGE_RESOURCEGROUP_ID`}}", + "gallery_name": "{{env `ARM_GALLERY_NAME`}}" + }, + "builders": [{ + "type": "azure-chroot", + + "client_id": "{{user `client_id`}}", + "client_secret": "{{user `client_secret`}}", + "subscription_id": "{{user `subscription_id`}}", + + "source": "Canonical:UbuntuServer:20.04-LTS:latest", + + "shared_image_destination": { + "resource_group": "{{user `resource_group`}}", + "gallery_name": "{{user `gallery_name`}}", + "image_name": "MyUbuntuOSImage", + "image_version": "1.0.0", + "exclude_from_latest": false, + "target_regions": [ + { + "name": "eastus", + "replicas": "1", + "storage_account_type": "standard_zrs" + } + ] + } + }], + "provisioners": [{ + "inline": [ + "apt update", + "apt upgrade -y" + ], + "inline_shebang": "/bin/sh -x", + "type": "shell" + }] +} diff --git a/fix/fixer.go b/fix/fixer.go index d77c23152..7f0beef10 100644 --- a/fix/fixer.go +++ b/fix/fixer.go @@ -58,6 +58,7 @@ func init() { "vsphere-iso-net-disk": new(FixerVSphereNetworkDisk), "iso-checksum-type-and-url": new(FixerISOChecksumTypeAndURL), "qemu-host-port": new(FixerQEMUHostPort), + "azure-exclude_from_latest": new(FixerAzureExcludeFromLatest), } FixerOrder = []string{ @@ -93,5 +94,6 @@ func init() { "vsphere-iso-net-disk", "iso-checksum-type-and-url", "qemu-host-port", + "azure-exclude_from_latest", } } diff --git a/fix/fixer_azure_exclude_from_latest.go b/fix/fixer_azure_exclude_from_latest.go new file mode 100644 index 000000000..d46049c1b --- /dev/null +++ b/fix/fixer_azure_exclude_from_latest.go @@ -0,0 +1,70 @@ +package fix + +import ( + "strings" + + "github.com/mitchellh/mapstructure" +) + +// FixerAzureExcludeFromLatest fix the spelling of "exclude_from_latest" +// template in an Azure builder +type FixerAzureExcludeFromLatest struct{} + +func (FixerAzureExcludeFromLatest) DeprecatedOptions() []string { + return []string{"exlude_from_latest"} +} + +func (FixerAzureExcludeFromLatest) Fix(input map[string]interface{}) (map[string]interface{}, error) { + // The type we'll decode into; we only care about builders + type template struct { + Builders []map[string]interface{} + } + + // Decode the input into our structure, if we can + var tpl template + if err := mapstructure.Decode(input, &tpl); err != nil { + return nil, err + } + + for _, builder := range tpl.Builders { + builderTypeRaw, ok := builder["type"] + if !ok { + continue + } + + builderType, ok := builderTypeRaw.(string) + if !ok { + continue + } + + if !strings.HasPrefix(builderType, "azure-chroot") { + continue + } + + if !strings.HasPrefix(builderType, "azure-chroot") { + continue + } + + sharedImageDestination, ok := builder["shared_image_destination"].(map[string]interface{}) + if !ok { + continue + } + + excludeFromLatest, ok := sharedImageDestination["exlude_from_latest"] + if !ok { + continue + } + + sharedImageDestination["exclude_from_latest"] = excludeFromLatest + delete(sharedImageDestination, "exlude_from_latest") + + builder["shared_image_destination"] = sharedImageDestination + } + + input["builders"] = tpl.Builders + return input, nil +} + +func (FixerAzureExcludeFromLatest) Synopsis() string { + return `Changes "exlude_from_latest" to "exclude_from_latest" in Azure builders.` +} diff --git a/fix/fixer_azure_exclude_from_latest_test.go b/fix/fixer_azure_exclude_from_latest_test.go new file mode 100644 index 000000000..27e10d57f --- /dev/null +++ b/fix/fixer_azure_exclude_from_latest_test.go @@ -0,0 +1,66 @@ +package fix + +import ( + "reflect" + "testing" +) + +func TestFixerAzureExcludeFromLatest(t *testing.T) { + var _ Fixer = new(FixerAzureExcludeFromLatest) +} + +func TestFixerAzureExcludeFromLatest_Fix_exlude_from_latest(t *testing.T) { + cases := []struct { + Input map[string]interface{} + Expected map[string]interface{} + }{ + // No shared_image_destination field + { + Input: map[string]interface{}{ + "type": "azure-chroot", + }, + + Expected: map[string]interface{}{ + "type": "azure-chroot", + }, + }, + + // exlude_from_latest field + { + Input: map[string]interface{}{ + "type": "azure-chroot", + "shared_image_destination": map[string]interface{}{ + "exlude_from_latest": "false", + }, + }, + + Expected: map[string]interface{}{ + "type": "azure-chroot", + "shared_image_destination": map[string]interface{}{ + "exclude_from_latest": "false", + }, + }, + }, + } + + for _, tc := range cases { + var f FixerAzureExcludeFromLatest + + input := map[string]interface{}{ + "builders": []map[string]interface{}{tc.Input}, + } + + expected := map[string]interface{}{ + "builders": []map[string]interface{}{tc.Expected}, + } + + output, err := f.Fix(input) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !reflect.DeepEqual(output, expected) { + t.Fatalf("unexpected: %#v\nexpected: %#v\n", output, expected) + } + } +} diff --git a/helper/config/deprecated_options.go b/helper/config/deprecated_options.go index 538d50fe5..0ad66d740 100644 --- a/helper/config/deprecated_options.go +++ b/helper/config/deprecated_options.go @@ -40,4 +40,5 @@ var DeprecatedOptions = []string{ "iso_checksum_type", "ssh_host_port_max", "ssh_host_port_min", + "exlude_from_latest", } diff --git a/website/pages/partials/builder/azure/chroot/SharedImageGalleryDestination-not-required.mdx b/website/pages/partials/builder/azure/chroot/SharedImageGalleryDestination-not-required.mdx index f0f7c92e3..f986d6962 100644 --- a/website/pages/partials/builder/azure/chroot/SharedImageGalleryDestination-not-required.mdx +++ b/website/pages/partials/builder/azure/chroot/SharedImageGalleryDestination-not-required.mdx @@ -2,4 +2,6 @@ - `target_regions` ([]TargetRegion) - Target Regions -- `exlude_from_latest` (bool) - Exclude From Latest +- `exclude_from_latest` (bool) - Exclude From Latest + +- `exlude_from_latest` (bool) - Exclude From Latest Typo