github.com/hashicorp/packer@v1.14.3/datasource/hcp-packer-version/data.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  //go:generate packer-sdc struct-markdown
     5  //go:generate packer-sdc mapstructure-to-hcl2 -type DatasourceOutput,Config
     6  package hcp_packer_version
     7  
     8  import (
     9  	"context"
    10  	"fmt"
    11  	"log"
    12  
    13  	hcpPackerModels "github.com/hashicorp/hcp-sdk-go/clients/cloud-packer-service/stable/2023-01-01/models"
    14  	"github.com/zclconf/go-cty/cty"
    15  
    16  	"github.com/hashicorp/hcl/v2/hcldec"
    17  	"github.com/hashicorp/packer-plugin-sdk/common"
    18  	"github.com/hashicorp/packer-plugin-sdk/hcl2helper"
    19  	packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
    20  	"github.com/hashicorp/packer-plugin-sdk/template/config"
    21  	hcpapi "github.com/hashicorp/packer/internal/hcp/api"
    22  )
    23  
    24  type Datasource struct {
    25  	config Config
    26  }
    27  
    28  type Config struct {
    29  	common.PackerConfig `mapstructure:",squash"`
    30  	// The bucket name in the HCP Packer Registry.
    31  	BucketName string `mapstructure:"bucket_name" required:"true"`
    32  	// The channel name in the given bucket to use for retrieving the version.
    33  	ChannelName string `mapstructure:"channel_name" required:"true"`
    34  }
    35  
    36  func (d *Datasource) ConfigSpec() hcldec.ObjectSpec {
    37  	return d.config.FlatMapstructure().HCL2Spec()
    38  }
    39  
    40  func (d *Datasource) Configure(raws ...interface{}) error {
    41  	err := config.Decode(&d.config, nil, raws...)
    42  	if err != nil {
    43  		return err
    44  	}
    45  
    46  	var errs *packersdk.MultiError
    47  
    48  	if d.config.BucketName == "" {
    49  		errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("the `bucket_name` must be specified"))
    50  	}
    51  	if d.config.ChannelName == "" {
    52  		errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("the `channel_name` must be specified"))
    53  	}
    54  
    55  	if errs != nil && len(errs.Errors) > 0 {
    56  		return errs
    57  	}
    58  	return nil
    59  }
    60  
    61  // DatasourceOutput is essentially a copy of []*models.HashicorpCloudPacker20230101Version, but without
    62  // the build and ancestry details
    63  type DatasourceOutput struct {
    64  	// Name of the author who created this version.
    65  	AuthorID string `mapstructure:"author_id"`
    66  
    67  	// The name of the bucket that this version is associated with.
    68  	BucketName string `mapstructure:"bucket_name"`
    69  
    70  	// Current state of the version.
    71  	Status string `mapstructure:"status"`
    72  
    73  	// The date the version was created.
    74  	CreatedAt string `mapstructure:"created_at"`
    75  
    76  	// The fingerprint of the version; this is a  unique identifier set by the Packer build
    77  	// that created this version.
    78  	Fingerprint string `mapstructure:"fingerprint"`
    79  
    80  	// The version ID. This is a ULID, which is a unique identifier similar
    81  	// to a UUID. It is created by the HCP Packer Registry when a version is
    82  	// first created, and is unique to this version.
    83  	ID string `mapstructure:"id"`
    84  
    85  	// The version name is created by the HCP Packer Registry once a version is
    86  	// "complete". Incomplete or failed versions currently default to having a name "v0".
    87  	Name string `mapstructure:"name"`
    88  
    89  	// The date when this version was last updated.
    90  	UpdatedAt string `mapstructure:"updated_at"`
    91  
    92  	// The ID of the channel used to query this version.
    93  	ChannelID string `mapstructure:"channel_id"`
    94  }
    95  
    96  func (d *Datasource) OutputSpec() hcldec.ObjectSpec {
    97  	return (&DatasourceOutput{}).FlatMapstructure().HCL2Spec()
    98  }
    99  
   100  func (d *Datasource) Execute() (cty.Value, error) {
   101  	ctx := context.TODO()
   102  
   103  	cli, err := hcpapi.NewClient()
   104  	if err != nil {
   105  		return cty.NullVal(cty.EmptyObject), err
   106  	}
   107  	log.Printf(
   108  		"[INFO] Reading HCP Packer Version info from HCP Packer Registry (%s) "+
   109  			"[project_id=%s, organization_id=%s, channel=%s]",
   110  		d.config.BucketName, cli.ProjectID, cli.OrganizationID, d.config.ChannelName,
   111  	)
   112  
   113  	channel, err := cli.GetChannel(ctx, d.config.BucketName, d.config.ChannelName)
   114  	if err != nil {
   115  		return cty.NullVal(cty.EmptyObject), fmt.Errorf(
   116  			"error retrieving HCP Packer Version from HCP Packer Registry: %s",
   117  			err.Error(),
   118  		)
   119  	}
   120  	if channel.Version == nil {
   121  		return cty.NullVal(cty.EmptyObject), fmt.Errorf(
   122  			"there is no HCP Packer Version associated with the channel %s",
   123  			d.config.ChannelName,
   124  		)
   125  	}
   126  
   127  	version := channel.Version
   128  
   129  	if *version.Status == hcpPackerModels.HashicorpCloudPacker20230101VersionStatusVERSIONREVOKED {
   130  		return cty.NullVal(cty.EmptyObject), fmt.Errorf(
   131  			"the HCP Packer Version associated with the channel %s is revoked and can not be used on Packer builds",
   132  			d.config.ChannelName,
   133  		)
   134  	}
   135  
   136  	output := DatasourceOutput{
   137  		AuthorID:    version.AuthorID,
   138  		BucketName:  version.BucketName,
   139  		Status:      string(*version.Status),
   140  		CreatedAt:   version.CreatedAt.String(),
   141  		Fingerprint: version.Fingerprint,
   142  		ID:          version.ID,
   143  		Name:        version.Name,
   144  		UpdatedAt:   version.UpdatedAt.String(),
   145  		ChannelID:   channel.ID,
   146  	}
   147  
   148  	return hcl2helper.HCL2ValueFromConfig(output, d.OutputSpec()), nil
   149  }