From 9f79e2d8cb7b3f8382b0a2a97024b86f20648303 Mon Sep 17 00:00:00 2001 From: Zhiwei Liang Date: Tue, 19 Mar 2024 16:46:28 -0400 Subject: [PATCH 01/11] Move NewLinodeClient and ErrorHelper to the new helper package --- builder/linode/builder.go | 3 ++- builder/linode/step_create_image.go | 3 ++- builder/linode/step_create_linode.go | 3 ++- builder/linode/step_create_ssh_key.go | 3 ++- builder/linode/step_shutdown_linode.go | 3 ++- builder/linode/linode.go => helper/client.go | 6 +++--- builder/linode/helper.go => helper/error.go | 4 ++-- 7 files changed, 15 insertions(+), 10 deletions(-) rename builder/linode/linode.go => helper/client.go (88%) rename builder/linode/helper.go => helper/error.go (85%) diff --git a/builder/linode/builder.go b/builder/linode/builder.go index 1306a44c..0f609667 100644 --- a/builder/linode/builder.go +++ b/builder/linode/builder.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/packer-plugin-sdk/multistep" "github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" + "github.com/linode/packer-plugin-linode/helper" ) // The unique ID for this builder. @@ -39,7 +40,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (ret packersdk.Artifact, err error) { ui.Say("Running builder ...") - client := newLinodeClient(b.config.PersonalAccessToken) + client := helper.NewLinodeClient(b.config.PersonalAccessToken) if err != nil { ui.Error(err.Error()) diff --git a/builder/linode/step_create_image.go b/builder/linode/step_create_image.go index d7932e9f..003ee892 100644 --- a/builder/linode/step_create_image.go +++ b/builder/linode/step_create_image.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/linode/linodego" + "github.com/linode/packer-plugin-linode/helper" ) type stepCreateImage struct { @@ -19,7 +20,7 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul instance := state.Get("instance").(*linodego.Instance) handleError := func(prefix string, err error) multistep.StepAction { - return errorHelper(state, ui, prefix, err) + return helper.ErrorHelper(state, ui, prefix, err) } creationPoller, err := s.client.NewEventPoller( diff --git a/builder/linode/step_create_linode.go b/builder/linode/step_create_linode.go index 4bfac553..b7a6a509 100644 --- a/builder/linode/step_create_linode.go +++ b/builder/linode/step_create_linode.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/linode/linodego" + "github.com/linode/packer-plugin-linode/helper" ) type stepCreateLinode struct { @@ -41,7 +42,7 @@ func (s *stepCreateLinode) Run(ctx context.Context, state multistep.StateBag) mu ui := state.Get("ui").(packersdk.Ui) handleError := func(prefix string, err error) multistep.StepAction { - return errorHelper(state, ui, prefix, err) + return helper.ErrorHelper(state, ui, prefix, err) } ui.Say("Creating Linode...") diff --git a/builder/linode/step_create_ssh_key.go b/builder/linode/step_create_ssh_key.go index 08dc7221..d0e52d5b 100644 --- a/builder/linode/step_create_ssh_key.go +++ b/builder/linode/step_create_ssh_key.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" + "github.com/linode/packer-plugin-linode/helper" "golang.org/x/crypto/ssh" ) @@ -27,7 +28,7 @@ func (s *StepCreateSSHKey) Run(_ context.Context, state multistep.StateBag) mult config := state.Get("config").(*Config) handleError := func(prefix string, err error) multistep.StepAction { - return errorHelper(state, ui, prefix, err) + return helper.ErrorHelper(state, ui, prefix, err) } if config.Comm.SSHPrivateKeyFile != "" { diff --git a/builder/linode/step_shutdown_linode.go b/builder/linode/step_shutdown_linode.go index 0ba8a6f7..3ad2f498 100644 --- a/builder/linode/step_shutdown_linode.go +++ b/builder/linode/step_shutdown_linode.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/linode/linodego" + "github.com/linode/packer-plugin-linode/helper" ) type stepShutdownLinode struct { @@ -18,7 +19,7 @@ func (s *stepShutdownLinode) Run(ctx context.Context, state multistep.StateBag) instance := state.Get("instance").(*linodego.Instance) handleError := func(prefix string, err error) multistep.StepAction { - return errorHelper(state, ui, prefix, err) + return helper.ErrorHelper(state, ui, prefix, err) } ui.Say("Shutting down Linode...") diff --git a/builder/linode/linode.go b/helper/client.go similarity index 88% rename from builder/linode/linode.go rename to helper/client.go index c8ca3f77..ad4ee7dd 100644 --- a/builder/linode/linode.go +++ b/helper/client.go @@ -1,4 +1,4 @@ -package linode +package helper import ( "fmt" @@ -9,8 +9,8 @@ import ( "golang.org/x/oauth2" ) -func newLinodeClient(pat string) linodego.Client { - tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: pat}) +func NewLinodeClient(token string) linodego.Client { + tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}) oauthTransport := &oauth2.Transport{ Source: tokenSource, diff --git a/builder/linode/helper.go b/helper/error.go similarity index 85% rename from builder/linode/helper.go rename to helper/error.go index d7623bea..34a61e3f 100644 --- a/builder/linode/helper.go +++ b/helper/error.go @@ -1,4 +1,4 @@ -package linode +package helper import ( "fmt" @@ -9,7 +9,7 @@ import ( // errorHelper is a helper function to reduce the amount of bloat and complexity // caused by redundant error handling logic. -func errorHelper(state multistep.StateBag, ui packersdk.Ui, prefix string, err error) multistep.StepAction { +func ErrorHelper(state multistep.StateBag, ui packersdk.Ui, prefix string, err error) multistep.StepAction { wrappedError := fmt.Errorf("%s: %w", prefix, err) state.Put("error", wrappedError) ui.Error(wrappedError.Error()) From ab323a0b26dc237be12e39396573ef53b6b76556 Mon Sep 17 00:00:00 2001 From: Zhiwei Liang Date: Wed, 20 Mar 2024 01:09:57 -0400 Subject: [PATCH 02/11] Create LinodeCommon struct --- builder/linode/config.go | 4 ++-- builder/linode/config.hcl2spec.go | 4 ++-- builder/linode/config_test.go | 16 ++++++++++------ helper/common.go | 7 +++++++ 4 files changed, 21 insertions(+), 10 deletions(-) create mode 100644 helper/common.go diff --git a/builder/linode/config.go b/builder/linode/config.go index 4e375c90..9e28de51 100644 --- a/builder/linode/config.go +++ b/builder/linode/config.go @@ -16,6 +16,7 @@ import ( packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/hashicorp/packer-plugin-sdk/template/config" "github.com/hashicorp/packer-plugin-sdk/template/interpolate" + "github.com/linode/packer-plugin-linode/helper" ) type InterfaceIPv4 struct { @@ -35,11 +36,10 @@ type Interface struct { type Config struct { common.PackerConfig `mapstructure:",squash"` + helper.LinodeCommon `mapstructure:",squash"` ctx interpolate.Context Comm communicator.Config `mapstructure:",squash"` - PersonalAccessToken string `mapstructure:"linode_token"` - Interfaces []Interface `mapstructure:"interface"` Region string `mapstructure:"region"` AuthorizedKeys []string `mapstructure:"authorized_keys"` diff --git a/builder/linode/config.hcl2spec.go b/builder/linode/config.hcl2spec.go index 5f751af5..3ca9481a 100644 --- a/builder/linode/config.hcl2spec.go +++ b/builder/linode/config.hcl2spec.go @@ -18,6 +18,7 @@ type FlatConfig struct { PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` + PersonalAccessToken *string `mapstructure:"linode_token" cty:"linode_token" hcl:"linode_token"` Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"` PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"` SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"` @@ -67,7 +68,6 @@ type FlatConfig struct { WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"` WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` - PersonalAccessToken *string `mapstructure:"linode_token" cty:"linode_token" hcl:"linode_token"` Interfaces []FlatInterface `mapstructure:"interface" cty:"interface" hcl:"interface"` Region *string `mapstructure:"region" cty:"region" hcl:"region"` AuthorizedKeys []string `mapstructure:"authorized_keys" cty:"authorized_keys" hcl:"authorized_keys"` @@ -108,6 +108,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "linode_token": &hcldec.AttrSpec{Name: "linode_token", Type: cty.String, Required: false}, "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, "pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false}, "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, @@ -157,7 +158,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false}, "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, - "linode_token": &hcldec.AttrSpec{Name: "linode_token", Type: cty.String, Required: false}, "interface": &hcldec.BlockListSpec{TypeName: "interface", Nested: hcldec.ObjectSpec((*FlatInterface)(nil).HCL2Spec())}, "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, "authorized_keys": &hcldec.AttrSpec{Name: "authorized_keys", Type: cty.List(cty.String), Required: false}, diff --git a/builder/linode/config_test.go b/builder/linode/config_test.go index ea281bca..75099edf 100644 --- a/builder/linode/config_test.go +++ b/builder/linode/config_test.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/packer-plugin-sdk/communicator" "github.com/hashicorp/packer-plugin-sdk/template/interpolate" + "github.com/linode/packer-plugin-linode/helper" ) func TestPrepare(t *testing.T) { @@ -15,12 +16,15 @@ func TestPrepare(t *testing.T) { } config := &Config{ - ctx: interpolate.Context{}, - Comm: communicator.Config{SSH: data}, - PersonalAccessToken: "test-linode-access-token", - Region: "us-ord", - InstanceType: "g6-standard-1", - Image: "linode/debian10", + LinodeCommon: helper.LinodeCommon{ + PersonalAccessToken: "test-linode-access-token", + }, + ctx: interpolate.Context{}, + Comm: communicator.Config{SSH: data}, + + Region: "us-ord", + InstanceType: "g6-standard-1", + Image: "linode/debian10", } warnings, err := config.Prepare() diff --git a/helper/common.go b/helper/common.go new file mode 100644 index 00000000..d57dcf4c --- /dev/null +++ b/helper/common.go @@ -0,0 +1,7 @@ +//go:generate packer-sdc struct-markdown +package helper + +type LinodeCommon struct { + // The Linode API token. This can also be specified in LINODE_TOKEN environment variable + PersonalAccessToken string `mapstructure:"linode_token"` +} From f59313c45d6d3290674dc89c6a544f0b5f7d9934 Mon Sep 17 00:00:00 2001 From: Zhiwei Liang Date: Wed, 20 Mar 2024 01:21:52 -0400 Subject: [PATCH 03/11] Add unit tests to `make test` --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b2b5235f..9cc02873 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ build: fmtcheck @go build -o ${BINARY} .PHONY: test -test: dev fmtcheck acctest +test: fmtcheck unit-test int-test .PHONY: install-packer-sdc install-packer-sdc: ## Install packer sofware development command @@ -72,3 +72,4 @@ deps: install-packer-sdc clean: @rm -rf .docs @rm -rf ./packer-plugin-linode + @rm -rf ./docs-partials From 19cb03c922678c7cfc4359a1f18162d0a59e0fc3 Mon Sep 17 00:00:00 2001 From: Zhiwei Liang Date: Wed, 20 Mar 2024 01:22:29 -0400 Subject: [PATCH 04/11] Organize deps list --- go.mod | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/go.mod b/go.mod index e5189dba..a344c0f6 100644 --- a/go.mod +++ b/go.mod @@ -6,13 +6,12 @@ require ( github.com/hashicorp/hcl/v2 v2.20.0 github.com/hashicorp/packer-plugin-sdk v0.5.2 github.com/linode/linodego v1.30.0 + github.com/mitchellh/mapstructure v1.5.0 github.com/zclconf/go-cty v1.13.3 golang.org/x/crypto v0.21.0 golang.org/x/oauth2 v0.18.0 ) -require github.com/mitchellh/mapstructure v1.5.0 - require ( cloud.google.com/go v0.110.2 // indirect cloud.google.com/go/compute v1.20.1 // indirect From 638f175765896194191d4a377827daa69a68c8dc Mon Sep 17 00:00:00 2001 From: Zhiwei Liang Date: Wed, 20 Mar 2024 01:33:52 -0400 Subject: [PATCH 05/11] Add go doc for LinodeCommon --- helper/common.go | 1 + 1 file changed, 1 insertion(+) diff --git a/helper/common.go b/helper/common.go index d57dcf4c..efb4eabf 100644 --- a/helper/common.go +++ b/helper/common.go @@ -1,6 +1,7 @@ //go:generate packer-sdc struct-markdown package helper +// The common configuration options related to Linode services type LinodeCommon struct { // The Linode API token. This can also be specified in LINODE_TOKEN environment variable PersonalAccessToken string `mapstructure:"linode_token"` From 5f53cf91414e17f004a14845c4f6e8dd69b5b56d Mon Sep 17 00:00:00 2001 From: Zhiwei Liang Date: Wed, 20 Mar 2024 02:51:25 -0400 Subject: [PATCH 06/11] Add linode-image data source --- .github/workflows/go-test-multiplatform.yml | 4 +- .../components/data-source/image/README.md | 108 ++++++++++ Makefile | 8 +- datasource/image/data.go | 190 ++++++++++++++++++ datasource/image/data.hcl2spec.go | 106 ++++++++++ datasource/image/filter.go | 80 ++++++++ docs/datasources/image.mdx | 64 ++++++ main.go | 2 + 8 files changed, 556 insertions(+), 6 deletions(-) create mode 100644 .web-docs/components/data-source/image/README.md create mode 100644 datasource/image/data.go create mode 100644 datasource/image/data.hcl2spec.go create mode 100644 datasource/image/filter.go create mode 100644 docs/datasources/image.mdx diff --git a/.github/workflows/go-test-multiplatform.yml b/.github/workflows/go-test-multiplatform.yml index 098be93e..af7346b1 100644 --- a/.github/workflows/go-test-multiplatform.yml +++ b/.github/workflows/go-test-multiplatform.yml @@ -40,7 +40,7 @@ jobs: go-version: ${{ needs.get-go-version.outputs.go-version }} - run: | echo "Testing with Go ${{ needs.get-go-version.outputs.go-version }}" - go test -race -count 1 ./builder/linode/... -timeout=3m -v + go test -race -count 1 ./... -timeout=3m -v windows-go-tests: needs: @@ -55,7 +55,7 @@ jobs: # Running unit tests directly with `go test` due to gofmt/gofumpt issues in Windows - run: | echo "Testing with Go ${{ needs.get-go-version.outputs.go-version }}" - go test -race -count 1 ./builder/linode/... -timeout=3m -v + go test -race -count 1 ./... -timeout=3m -v linux-go-tests: needs: diff --git a/.web-docs/components/data-source/image/README.md b/.web-docs/components/data-source/image/README.md new file mode 100644 index 00000000..07152521 --- /dev/null +++ b/.web-docs/components/data-source/image/README.md @@ -0,0 +1,108 @@ +Type: `linode-image` + +The Linode Image data source matches or filters the ID or label of both public images on +Linode and private images in your account using regular expression (regex) or an exact +match. + +You can get the latest list of available public images on Linode via the +[Linode Image List API](https://www.linode.com/docs/api/images/#images-list). + +## Examples + +```hcl +data "linode-image" "latest_ubuntu" { + id_regex = "linode/ubuntu.*" + latest = true +} + +source "linode" "example" { + image = linode-image.latest_ubuntu.id + image_description = "My Private Image" + image_label = "my-packaer-private-linode-image-test" + instance_label = "temporary-linode-image" + instance_type = "g6-nanode-1" + region = "us-mia" + ssh_username = "root" +} + +build { + sources = ["source.linode.example"] +} +``` + +```hcl +data "linode-image" "latest_ubuntu_lts" { + label_regex = "Ubuntu [0-9]+\\.[0-9]+ LTS" + latest = true +} +``` + +```hcl +data "linode-image" "ubuntu22_lts" { + id = "linode/ubuntu22.04" + latest = true +} +``` + +## Configuration Reference: + + + +- `label` (string) - Matching the label of an image by exact label + +- `label_regex` (string) - Matching the label of an image by a regular expression + +- `id` (string) - Matching the ID of an image by exact ID + +- `id_regex` (string) - Matching the ID of an image by a regular expression + +- `latest` (bool) - Whether to use the latest created image when there are multiple matches + + + + + +- `linode_token` (string) - The Linode API token. This can also be specified in LINODE_TOKEN environment variable + + + + +## Output: + + + +- `id` (string) - The unique ID of this Image. + +- `capabilities` ([]string) - A list containing the following possible capabilities of this Image: + - cloud-init: This Image supports cloud-init with Metadata. Only applies to public Images. + +- `created` (string) - When this Image was created. + +- `created_by` (string) - The name of the User who created this Image, or “linode” for public Images. + +- `deprecated` (bool) - Whether or not this Image is deprecated. Will only be true for deprecated public Images. + +- `description` (string) - A detailed description of this Image. + +- `eol` (string) - The date of the public Image’s planned end of life. `null` for private Images. + +- `expiry` (string) - Expiry date of the image. + Only Images created automatically from a deleted Linode (type=automatic) will expire. + +- `is_public` (bool) - True if the Image is a public distribution image. + False if Image is private Account-specific Image. + +- `label` (string) - A short description of the Image. + +- `size` (int) - The minimum size this Image needs to deploy. Size is in MB. + +- `type` (string) - Enum: `manual` `automatic` + How the Image was created. + "Manual" Images can be created at any time. + "Automatic" Images are created automatically from a deleted Linode. + +- `updated` (string) - When this Image was last updated. + +- `vendor` (string) - The upstream distribution vendor. `null` for private Images. + + diff --git a/Makefile b/Makefile index 9cc02873..ff21cd7a 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ NAME=linode BINARY=packer-plugin-${NAME} PLUGIN_FQN="$(shell grep -E '^module' 0 { + return errs + } + + return nil +} + +type DatasourceOutput struct { + // The unique ID of this Image. + ID string `mapstructure:"id"` + + // A list containing the following possible capabilities of this Image: + // - cloud-init: This Image supports cloud-init with Metadata. Only applies to public Images. + Capabilities []string `mapstructure:"capabilities"` + + // When this Image was created. + Created string `mapstructure:"created"` + + // The name of the User who created this Image, or “linode” for public Images. + CreatedBy string `mapstructure:"created_by"` + + // Whether or not this Image is deprecated. Will only be true for deprecated public Images. + Deprecated bool `mapstructure:"deprecated"` + + // A detailed description of this Image. + Description string `mapstructure:"description"` + + // The date of the public Image’s planned end of life. `null` for private Images. + EOL string `mapstructure:"eol"` + + // Expiry date of the image. + // Only Images created automatically from a deleted Linode (type=automatic) will expire. + Expiry string `mapstructure:"expiry"` + + // True if the Image is a public distribution image. + // False if Image is private Account-specific Image. + IsPublic bool `mapstructure:"is_public"` + + // A short description of the Image. + Label string `mapstructure:"label"` + + // The minimum size this Image needs to deploy. Size is in MB. + Size int `mapstructure:"size"` + + // Enum: `manual` `automatic` + // How the Image was created. + // "Manual" Images can be created at any time. + // "Automatic" Images are created automatically from a deleted Linode. + Type string `mapstructure:"type"` + + // When this Image was last updated. + Updated string `mapstructure:"updated"` + + // The upstream distribution vendor. `null` for private Images. + Vendor string `mapstructure:"vendor"` +} + +func (d *Datasource) OutputSpec() hcldec.ObjectSpec { + return (&DatasourceOutput{}).FlatMapstructure().HCL2Spec() +} + +func (d *Datasource) Execute() (cty.Value, error) { + client := helper.NewLinodeClient(d.config.PersonalAccessToken) + + filters := linodego.Filter{} + + // Label is API filterable + if d.config.Label != "" { + filters.AddField(linodego.Eq, "label", d.config.Label) + } + + // we only want available images for the obvious reason + filters.AddField(linodego.Eq, "status", "available") + + filterString, err := filters.MarshalJSON() + if err != nil { + return cty.NullVal(cty.EmptyObject), err + } + + images, err := client.ListImages( + context.Background(), + linodego.NewListOptions(0, string(filterString)), + ) + if err != nil { + return cty.NullVal(cty.EmptyObject), err + } + + // filtering non-API filterable attributes + image, err := filterImageResults(images, d.config) + if err != nil { + return cty.NullVal(cty.EmptyObject), err + } + + return hcl2helper.HCL2ValueFromConfig(getOutput(image), d.OutputSpec()), nil +} + +func getOutput(image linodego.Image) DatasourceOutput { + output := DatasourceOutput{ + ID: image.ID, + Capabilities: image.Capabilities, + CreatedBy: image.CreatedBy, + Deprecated: image.Deprecated, + Description: image.Description, + IsPublic: image.IsPublic, + Label: image.Label, + Size: image.Size, + Type: image.Type, + Vendor: image.Vendor, + Created: image.Created.Format(time.RFC3339), + Updated: image.Updated.Format(time.RFC3339), + } + + if image.EOL != nil { + output.EOL = image.EOL.Format(time.RFC3339) + } + + if image.Expiry != nil { + output.Expiry = image.Expiry.Format(time.RFC3339) + } + + return output +} diff --git a/datasource/image/data.hcl2spec.go b/datasource/image/data.hcl2spec.go new file mode 100644 index 00000000..1b2192d2 --- /dev/null +++ b/datasource/image/data.hcl2spec.go @@ -0,0 +1,106 @@ +// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT. + +package image + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` + PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` + PersonalAccessToken *string `mapstructure:"linode_token" cty:"linode_token" hcl:"linode_token"` + Label *string `mapstructure:"label" cty:"label" hcl:"label"` + LabelRegex *string `mapstructure:"label_regex" cty:"label_regex" hcl:"label_regex"` + ID *string `mapstructure:"id" cty:"id" hcl:"id"` + IDRegex *string `mapstructure:"id_regex" cty:"id_regex" hcl:"id_regex"` + Latest *bool `mapstructure:"latest" cty:"latest" hcl:"latest"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, + "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "linode_token": &hcldec.AttrSpec{Name: "linode_token", Type: cty.String, Required: false}, + "label": &hcldec.AttrSpec{Name: "label", Type: cty.String, Required: false}, + "label_regex": &hcldec.AttrSpec{Name: "label_regex", Type: cty.String, Required: false}, + "id": &hcldec.AttrSpec{Name: "id", Type: cty.String, Required: false}, + "id_regex": &hcldec.AttrSpec{Name: "id_regex", Type: cty.String, Required: false}, + "latest": &hcldec.AttrSpec{Name: "latest", Type: cty.Bool, Required: false}, + } + return s +} + +// FlatDatasourceOutput is an auto-generated flat version of DatasourceOutput. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatDatasourceOutput struct { + ID *string `mapstructure:"id" cty:"id" hcl:"id"` + Capabilities []string `mapstructure:"capabilities" cty:"capabilities" hcl:"capabilities"` + Created *string `mapstructure:"created" cty:"created" hcl:"created"` + CreatedBy *string `mapstructure:"created_by" cty:"created_by" hcl:"created_by"` + Deprecated *bool `mapstructure:"deprecated" cty:"deprecated" hcl:"deprecated"` + Description *string `mapstructure:"description" cty:"description" hcl:"description"` + EOL *string `mapstructure:"eol" cty:"eol" hcl:"eol"` + Expiry *string `mapstructure:"expiry" cty:"expiry" hcl:"expiry"` + IsPublic *bool `mapstructure:"is_public" cty:"is_public" hcl:"is_public"` + Label *string `mapstructure:"label" cty:"label" hcl:"label"` + Size *int `mapstructure:"size" cty:"size" hcl:"size"` + Type *string `mapstructure:"type" cty:"type" hcl:"type"` + Updated *string `mapstructure:"updated" cty:"updated" hcl:"updated"` + Vendor *string `mapstructure:"vendor" cty:"vendor" hcl:"vendor"` +} + +// FlatMapstructure returns a new FlatDatasourceOutput. +// FlatDatasourceOutput is an auto-generated flat version of DatasourceOutput. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*DatasourceOutput) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatDatasourceOutput) +} + +// HCL2Spec returns the hcl spec of a DatasourceOutput. +// This spec is used by HCL to read the fields of DatasourceOutput. +// The decoded values from this spec will then be applied to a FlatDatasourceOutput. +func (*FlatDatasourceOutput) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "id": &hcldec.AttrSpec{Name: "id", Type: cty.String, Required: false}, + "capabilities": &hcldec.AttrSpec{Name: "capabilities", Type: cty.List(cty.String), Required: false}, + "created": &hcldec.AttrSpec{Name: "created", Type: cty.String, Required: false}, + "created_by": &hcldec.AttrSpec{Name: "created_by", Type: cty.String, Required: false}, + "deprecated": &hcldec.AttrSpec{Name: "deprecated", Type: cty.Bool, Required: false}, + "description": &hcldec.AttrSpec{Name: "description", Type: cty.String, Required: false}, + "eol": &hcldec.AttrSpec{Name: "eol", Type: cty.String, Required: false}, + "expiry": &hcldec.AttrSpec{Name: "expiry", Type: cty.String, Required: false}, + "is_public": &hcldec.AttrSpec{Name: "is_public", Type: cty.Bool, Required: false}, + "label": &hcldec.AttrSpec{Name: "label", Type: cty.String, Required: false}, + "size": &hcldec.AttrSpec{Name: "size", Type: cty.Number, Required: false}, + "type": &hcldec.AttrSpec{Name: "type", Type: cty.String, Required: false}, + "updated": &hcldec.AttrSpec{Name: "updated", Type: cty.String, Required: false}, + "vendor": &hcldec.AttrSpec{Name: "vendor", Type: cty.String, Required: false}, + } + return s +} diff --git a/datasource/image/filter.go b/datasource/image/filter.go new file mode 100644 index 00000000..629c69ba --- /dev/null +++ b/datasource/image/filter.go @@ -0,0 +1,80 @@ +package image + +import ( + "errors" + "log" + "regexp" + "sort" + + "github.com/linode/linodego" +) + +type ImageFilter func(linodego.Image) bool + +func filterImages(images []linodego.Image, filter ImageFilter) []linodego.Image { + result := make([]linodego.Image, 0) + + for _, image := range images { + if filter(image) { + result = append(result, image) + } + } + + return result +} + +func filterImagesByID(images []linodego.Image, id string) []linodego.Image { + idFilter := func(image linodego.Image) bool { + return image.ID == id + } + return filterImages(images, idFilter) +} + +func filterImagesByIDRegex(images []linodego.Image, idRegex string) []linodego.Image { + r := regexp.MustCompile(idRegex) + idRegexFilter := func(image linodego.Image) bool { + return r.MatchString(image.ID) + } + return filterImages(images, idRegexFilter) +} + +func filterImagesByLabelRegex(images []linodego.Image, labelRegex string) []linodego.Image { + r := regexp.MustCompile(labelRegex) + labelRegexFilter := func(image linodego.Image) bool { + return r.MatchString(image.Label) + } + return filterImages(images, labelRegexFilter) +} + +func filterImageResults(images []linodego.Image, config Config) (linodego.Image, error) { + if config.LabelRegex != "" { + images = filterImagesByLabelRegex(images, config.LabelRegex) + } + if config.ID != "" { + images = filterImagesByID(images, config.ID) + } + if config.IDRegex != "" { + images = filterImagesByIDRegex(images, config.IDRegex) + } + if len(images) > 1 { + + if config.Latest { + log.Default().Print(images) + sort.Slice(images, func(i, j int) bool { + return images[i].Created.After(*images[j].Created) + }) + log.Default().Print(images) + return images[0], nil + } + + return linodego.Image{}, errors.New( + "Multiple images found. Please try a more specific search, " + + "or set latest to true in the data source config block.", + ) + } + if len(images) == 0 { + return linodego.Image{}, errors.New("No image found.") + } + + return images[0], nil +} diff --git a/docs/datasources/image.mdx b/docs/datasources/image.mdx new file mode 100644 index 00000000..058bd31d --- /dev/null +++ b/docs/datasources/image.mdx @@ -0,0 +1,64 @@ +--- +description: | + The Linode Image data source for Packer is for matching and filtering images on Linode. +page_title: Linode Image - Data Source +nav_title: Linode Image +--- + +# Linode Image Data Source + +Type: `linode-image` + +The Linode Image data source matches or filters the ID or label of both public images on +Linode and private images in your account using regular expression (regex) or an exact +match. + +You can get the latest list of available public images on Linode via the +[Linode Image List API](https://www.linode.com/docs/api/images/#images-list). + +## Examples + +```hcl +data "linode-image" "latest_ubuntu" { + id_regex = "linode/ubuntu.*" + latest = true +} + +source "linode" "example" { + image = data.linode-image.latest_ubuntu.id + image_description = "My Private Image" + image_label = "my-packaer-private-linode-image-test" + instance_label = "temporary-linode-image" + instance_type = "g6-nanode-1" + region = "us-mia" + ssh_username = "root" +} + +build { + sources = ["source.linode.example"] +} +``` + +```hcl +data "linode-image" "latest_ubuntu_lts" { + label_regex = "Ubuntu [0-9]+\\.[0-9]+ LTS" + latest = true +} +``` + +```hcl +data "linode-image" "ubuntu22_lts" { + id = "linode/ubuntu22.04" + latest = true +} +``` + +## Configuration Reference: + +@include 'datasource/image/Config-not-required.mdx' +@include 'helper/LinodeCommon-not-required.mdx' + +## Output: + +@include 'datasource/image/DatasourceOutput.mdx' + diff --git a/main.go b/main.go index ea424d76..4de489ca 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "os" "github.com/linode/packer-plugin-linode/builder/linode" + "github.com/linode/packer-plugin-linode/datasource/image" "github.com/linode/packer-plugin-linode/version" "github.com/hashicorp/packer-plugin-sdk/plugin" @@ -12,6 +13,7 @@ import ( func main() { pps := plugin.NewSet() + pps.RegisterDatasource("image", new(image.Datasource)) pps.RegisterBuilder(plugin.DEFAULT_NAME, new(linode.Builder)) pps.SetVersion(version.PluginVersion) err := pps.Run() From af3e2bf6e3bf60dafe126186da2c251d1c48dd26 Mon Sep 17 00:00:00 2001 From: Zhiwei Liang Date: Thu, 21 Mar 2024 19:04:58 -0400 Subject: [PATCH 07/11] Bump linodego to v1.31 --- go.mod | 8 ++++---- go.sum | 42 ++++++++++++++++++++++++++++-------------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index a344c0f6..1f358e2f 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21 require ( github.com/hashicorp/hcl/v2 v2.20.0 github.com/hashicorp/packer-plugin-sdk v0.5.2 - github.com/linode/linodego v1.30.0 + github.com/linode/linodego v1.31.0 github.com/mitchellh/mapstructure v1.5.0 github.com/zclconf/go-cty v1.13.3 golang.org/x/crypto v0.21.0 @@ -30,11 +30,11 @@ require ( github.com/dylanmei/iso8601 v0.1.0 // indirect github.com/fatih/color v1.14.1 // indirect github.com/go-jose/go-jose/v3 v3.0.3 // indirect - github.com/go-resty/resty/v2 v2.11.0 // indirect + github.com/go-resty/resty/v2 v2.12.0 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gofrs/uuid v4.0.0+incompatible // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/s2a-go v0.1.4 // indirect github.com/google/uuid v1.3.0 // indirect @@ -88,7 +88,7 @@ require ( golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.3.0 // indirect + golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.14.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.126.0 // indirect diff --git a/go.sum b/go.sum index 97da62af..0cbf7365 100644 --- a/go.sum +++ b/go.sum @@ -25,9 +25,12 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/antchfx/xmlquery v1.3.5 h1:I7TuBRqsnfFuL11ruavGm911Awx9IqSdiU6W/ztSmVw= +github.com/antchfx/xmlquery v1.3.5/go.mod h1:64w0Xesg2sTaawIdNqMB+7qaW/bSqkQm+ssPaCMWNnc= github.com/antchfx/xpath v1.1.11 h1:WOFtK8TVAjLm3lbgqeP0arlHpvCEeTANeWZ/csPpJkQ= +github.com/antchfx/xpath v1.1.11/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA= +github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= @@ -62,9 +65,11 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dylanmei/iso8601 v0.1.0 h1:812NGQDBcqquTfH5Yeo7lwR0nzx/cKdsmf3qMjPURUI= github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ= github.com/dylanmei/winrmtest v0.0.0-20210303004826-fbc9ae56efb6 h1:zWydSUQBJApHwpQ4guHi+mGyQN/8yN6xbKWdDtL3ZNM= +github.com/dylanmei/winrmtest v0.0.0-20210303004826-fbc9ae56efb6/go.mod h1:6BLLhzn1VEiJ4veuAGhINBTrBlV889Wd+aU4auxKOww= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -83,10 +88,11 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= -github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= +github.com/go-resty/resty/v2 v2.12.0 h1:rsVL8P90LFvkUYq/V5BTVe203WfRIU4gvcf+yfzJzGA= +github.com/go-resty/resty/v2 v2.12.0/go.mod h1:o0yGPrkS3lOe1+eFajk6kBW8ScXzwU3hD69/gt2yB/0= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= +github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -112,10 +118,11 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -128,6 +135,7 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -141,6 +149,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb github.com/hashicorp/consul/api v1.25.1 h1:CqrdhYzc8XZuPnhIYZWH45toM0LB9ZeYr/gvpLVI3PE= github.com/hashicorp/consul/api v1.25.1/go.mod h1:iiLVwR/htV7mas/sy0O+XSuEnrdBUUydemjxcUrAt4g= github.com/hashicorp/consul/sdk v0.14.1 h1:ZiwE2bKb+zro68sWzZ1SgHF3kRMBZ94TwOCFRF4ylPs= +github.com/hashicorp/consul/sdk v0.14.1/go.mod h1:vFt03juSzocLRFo59NkeQHHmQa6+g7oU0pfzdI1mUhg= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -162,6 +171,7 @@ github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJ github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -185,6 +195,7 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -207,6 +218,7 @@ github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5 github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww= +github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -224,11 +236,13 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/linode/linodego v1.30.0 h1:6HJli+LX7NGu+Sne2G+ux790EkVOWOV/SR4mK3jcs6k= -github.com/linode/linodego v1.30.0/go.mod h1:/46h/XpmWi//oSA92GX2p3FIxb8HbX7grslPPQalR2o= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/linode/linodego v1.31.0 h1:XY04rtgQfAzCWx3QUqIc9VVJ6usyLC5MoTEzrdAoIYY= +github.com/linode/linodego v1.31.0/go.mod h1:y8GDP9uLVH4jTB9qyrgw79qfKdYJmNCGUOJmfuiOcmI= github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= @@ -294,6 +308,7 @@ github.com/pkg/sftp v1.13.2 h1:taJnKntsWgU+qae21Rx52lIwndAdKrj0mfUNQsz1z4Q= github.com/pkg/sftp v1.13.2/go.mod h1:LzqnAvaD5TWeNBsZpfKxSYn1MbjWwOsCIAFFJbpIsK8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -331,7 +346,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= @@ -340,6 +356,7 @@ github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI= +github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= @@ -351,7 +368,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= @@ -385,7 +401,7 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -401,6 +417,7 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -433,7 +450,6 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -441,7 +457,6 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= @@ -453,11 +468,10 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= From 502dcb5d5cefb23cc9902901b2e28e3c68c14bea Mon Sep 17 00:00:00 2001 From: Zhiwei Liang Date: Fri, 22 Mar 2024 00:06:11 -0400 Subject: [PATCH 08/11] Improved builder test, added data source tests --- builder/linode/builder_acc_test.go | 34 ++++--------- datasource/image/data.go | 9 ++-- datasource/image/data_acc_test.go | 37 ++++++++++++++ datasource/image/data_test.go | 51 +++++++++++++++++++ datasource/image/filter_test.go | 80 ++++++++++++++++++++++++++++++ helper/acceptance/utils.go | 23 +++++++++ helper/client.go | 2 + 7 files changed, 208 insertions(+), 28 deletions(-) create mode 100644 datasource/image/data_acc_test.go create mode 100644 datasource/image/data_test.go create mode 100644 datasource/image/filter_test.go create mode 100644 helper/acceptance/utils.go diff --git a/builder/linode/builder_acc_test.go b/builder/linode/builder_acc_test.go index 3264f7a9..660296fa 100644 --- a/builder/linode/builder_acc_test.go +++ b/builder/linode/builder_acc_test.go @@ -1,14 +1,14 @@ package linode import ( - "os" "testing" "github.com/hashicorp/packer-plugin-sdk/acctest" + "github.com/linode/packer-plugin-linode/helper/acceptance" ) func TestBuilderAcc_basic(t *testing.T) { - if skip := testAccPreCheck(t); skip == true { + if skip := acceptance.TestAccPreCheck(t); skip == true { return } acctest.TestPlugin(t, &acctest.PluginTestCase{ @@ -18,29 +18,15 @@ func TestBuilderAcc_basic(t *testing.T) { }) } -func testAccPreCheck(t *testing.T) bool { - if os.Getenv(acctest.TestEnvVar) == "" { - t.Skipf("Acceptance tests skipped unless env '%s' set", - acctest.TestEnvVar) - return true - } - - if v := os.Getenv("LINODE_TOKEN"); v == "" { - t.Fatal("LINODE_TOKEN must be set for acceptance tests") - return true - } - return false +const testBuilderAccBasic = ` +source "linode" "example" { + image = "linode/ubuntu22.04" + instance_type = "g6-nanode-1" + region = "us-mia" + ssh_username = "root" } -const testBuilderAccBasic = ` -{ - "builders": [{ - "type": "linode", - "region": "us-mia", - "instance_type": "g6-nanode-1", - "image": "linode/debian12", - "ssh_username": "root", - "cloud_init": true - }] +build { + sources = ["source.linode.example"] } ` diff --git a/datasource/image/data.go b/datasource/image/data.go index 5154abc0..06dd5057 100644 --- a/datasource/image/data.go +++ b/datasource/image/data.go @@ -4,7 +4,7 @@ package image //go:generate packer-sdc mapstructure-to-hcl2 -type DatasourceOutput,Config import ( "context" - "errors" + "fmt" "os" "time" @@ -55,12 +55,13 @@ func (d *Datasource) Configure(raws ...interface{}) error { var errs *packersdk.MultiError if d.config.PersonalAccessToken == "" { - envToken := os.Getenv("LINODE_TOKEN") + envToken := os.Getenv(helper.TokenEnvVar) if envToken == "" { - errs = packersdk.MultiErrorAppend(errs, errors.New( + errs = packersdk.MultiErrorAppend(errs, fmt.Errorf( "A Linode API token is required. You can specify it in an "+ - "environment variable LINODE_TOKEN or set linode_token "+ + "environment variable %q or set linode_token "+ "attribute in the datasource block.", + helper.TokenEnvVar, )) } d.config.PersonalAccessToken = envToken diff --git a/datasource/image/data_acc_test.go b/datasource/image/data_acc_test.go new file mode 100644 index 00000000..14aae3b9 --- /dev/null +++ b/datasource/image/data_acc_test.go @@ -0,0 +1,37 @@ +package image + +import ( + "testing" + + "github.com/hashicorp/packer-plugin-sdk/acctest" + "github.com/linode/packer-plugin-linode/helper/acceptance" +) + +func TestImageDataSourceAcc_basic(t *testing.T) { + if skip := acceptance.TestAccPreCheck(t); skip == true { + return + } + acctest.TestPlugin(t, &acctest.PluginTestCase{ + Name: "test-linode-image-data-source-basic", + Type: "linode", + Template: testImageDataSourceAccBasic, + }) +} + +const testImageDataSourceAccBasic = ` +data "linode-image" "latest_ubuntu" { + id_regex = "linode/ubuntu.*" + latest = true +} + +source "linode" "example" { + image = data.linode-image.latest_ubuntu.id + instance_type = "g6-nanode-1" + region = "us-mia" + ssh_username = "root" +} + +build { + sources = ["source.linode.example"] +} +` diff --git a/datasource/image/data_test.go b/datasource/image/data_test.go new file mode 100644 index 00000000..950b85a4 --- /dev/null +++ b/datasource/image/data_test.go @@ -0,0 +1,51 @@ +package image + +import ( + "testing" + + "github.com/linode/packer-plugin-linode/helper" +) + +func TestImageDatasourceConfigure_MissingToken(t *testing.T) { + t.Setenv(helper.TokenEnvVar, "") + + datasource := Datasource{ + config: Config{}, + } + if err := datasource.Configure(nil); err == nil { + t.Fatalf( + "Should error if both environment variable %q "+ + "and linode_token config are unset", + helper.TokenEnvVar, + ) + } +} + +func TestImageDatasourceConfigure_EnvToken(t *testing.T) { + t.Setenv(helper.TokenEnvVar, "IAMATOKEN") + + datasource := Datasource{ + config: Config{}, + } + if err := datasource.Configure(nil); err != nil { + t.Fatalf( + "Should not error if environment variable %q is set.", + helper.TokenEnvVar, + ) + } +} + +func TestImageDatasourceConfigure_ConfigToken(t *testing.T) { + t.Setenv(helper.TokenEnvVar, "") + + datasource := Datasource{ + config: Config{ + LinodeCommon: helper.LinodeCommon{ + PersonalAccessToken: "IAMATOKEN", + }, + }, + } + if err := datasource.Configure(nil); err != nil { + t.Fatalf("Should not error if linode_token is configured.") + } +} diff --git a/datasource/image/filter_test.go b/datasource/image/filter_test.go new file mode 100644 index 00000000..bb929d15 --- /dev/null +++ b/datasource/image/filter_test.go @@ -0,0 +1,80 @@ +package image + +import ( + "testing" + + "github.com/linode/linodego" +) + +func TestImageDatasourceFilter_IDExactFilter(t *testing.T) { + targetID := "test_image" + + images := []linodego.Image{ + {ID: "some_other_image"}, + {ID: targetID}, + } + + config := Config{ID: targetID} + + image, err := filterImageResults(images, config) + if err != nil { + t.Fatalf("error filtering by exact image ID: %v", err) + } + if image.ID != targetID { + t.Fatalf( + "incorrect image with ID '%q' got selected, image "+ + "with ID '%q' should be selected instead", + image.ID, targetID, + ) + } +} + +func TestImageDatasourceFilter_IDRegexFilter(t *testing.T) { + targetPartialID := "test_image" + targetIDRegex := targetPartialID + "*" + targetID := targetPartialID + "1.0" + + images := []linodego.Image{ + {ID: "some_other_image1.0"}, + {ID: targetID}, + } + + config := Config{IDRegex: targetIDRegex} + + image, err := filterImageResults(images, config) + if err != nil { + t.Fatalf("error filtering by regex image ID: %v", err) + } + if image.ID != targetID { + t.Fatalf( + "incorrect image with ID '%q' got selected, image "+ + "with ID '%q' should be selected instead", + image.ID, targetID, + ) + } +} + +func TestImageDatasourceFilter_LabelRegexFilter(t *testing.T) { + targetPartialLabel := "test_image" + targetLabelRegex := targetPartialLabel + "*" + targetLabel := targetPartialLabel + "1.0" + + images := []linodego.Image{ + {Label: "some_other_image1.0"}, + {Label: targetLabel}, + } + + config := Config{LabelRegex: targetLabelRegex} + + image, err := filterImageResults(images, config) + if err != nil { + t.Fatalf("error filtering by regex image label: %v", err) + } + if image.Label != targetLabel { + t.Fatalf( + "incorrect image with label '%q' got selected, image "+ + "with label '%q' should be selected instead", + image.Label, targetLabel, + ) + } +} diff --git a/helper/acceptance/utils.go b/helper/acceptance/utils.go new file mode 100644 index 00000000..2559720f --- /dev/null +++ b/helper/acceptance/utils.go @@ -0,0 +1,23 @@ +package acceptance + +import ( + "os" + "testing" + + "github.com/hashicorp/packer-plugin-sdk/acctest" + "github.com/linode/packer-plugin-linode/helper" +) + +func TestAccPreCheck(t *testing.T) bool { + if os.Getenv(acctest.TestEnvVar) == "" { + t.Skipf("Acceptance tests skipped unless env '%s' set", + acctest.TestEnvVar) + return true + } + + if v := os.Getenv(helper.TokenEnvVar); v == "" { + t.Fatalf("%q must be set for acceptance tests", helper.TokenEnvVar) + return true + } + return false +} diff --git a/helper/client.go b/helper/client.go index ad4ee7dd..429169dd 100644 --- a/helper/client.go +++ b/helper/client.go @@ -9,6 +9,8 @@ import ( "golang.org/x/oauth2" ) +const TokenEnvVar = "LINODE_TOKEN" + func NewLinodeClient(token string) linodego.Client { tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}) From 791e3eac2e318ca82f84c2eab7cc93a88c1c38bf Mon Sep 17 00:00:00 2001 From: Zhiwei Liang Date: Fri, 22 Mar 2024 00:06:29 -0400 Subject: [PATCH 09/11] Update go doc for PersonalAccessToken in LinodeCommon --- helper/common.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/helper/common.go b/helper/common.go index efb4eabf..bbc13db0 100644 --- a/helper/common.go +++ b/helper/common.go @@ -3,6 +3,7 @@ package helper // The common configuration options related to Linode services type LinodeCommon struct { - // The Linode API token. This can also be specified in LINODE_TOKEN environment variable + // The Linode API token required for provision Linode resources. + // This can also be specified in LINODE_TOKEN environment variable. PersonalAccessToken string `mapstructure:"linode_token"` } From 82991837017116387baff0b9617a3857689d3bb1 Mon Sep 17 00:00:00 2001 From: Zhiwei Liang Date: Mon, 25 Mar 2024 12:33:36 -0400 Subject: [PATCH 10/11] Remove temp debug log --- datasource/image/filter.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/datasource/image/filter.go b/datasource/image/filter.go index 629c69ba..823ff4d4 100644 --- a/datasource/image/filter.go +++ b/datasource/image/filter.go @@ -2,7 +2,6 @@ package image import ( "errors" - "log" "regexp" "sort" @@ -59,11 +58,9 @@ func filterImageResults(images []linodego.Image, config Config) (linodego.Image, if len(images) > 1 { if config.Latest { - log.Default().Print(images) sort.Slice(images, func(i, j int) bool { return images[i].Created.After(*images[j].Created) }) - log.Default().Print(images) return images[0], nil } From 12bf0c9654fb9431f63d82add588732d0eda6616 Mon Sep 17 00:00:00 2001 From: Zhiwei Liang Date: Mon, 25 Mar 2024 12:36:10 -0400 Subject: [PATCH 11/11] Regenerate docs --- .web-docs/components/data-source/image/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.web-docs/components/data-source/image/README.md b/.web-docs/components/data-source/image/README.md index 07152521..90505b43 100644 --- a/.web-docs/components/data-source/image/README.md +++ b/.web-docs/components/data-source/image/README.md @@ -16,7 +16,7 @@ data "linode-image" "latest_ubuntu" { } source "linode" "example" { - image = linode-image.latest_ubuntu.id + image = data.linode-image.latest_ubuntu.id image_description = "My Private Image" image_label = "my-packaer-private-linode-image-test" instance_label = "temporary-linode-image" @@ -62,7 +62,8 @@ data "linode-image" "ubuntu22_lts" { -- `linode_token` (string) - The Linode API token. This can also be specified in LINODE_TOKEN environment variable +- `linode_token` (string) - The Linode API token required for provision Linode resources. + This can also be specified in LINODE_TOKEN environment variable.