github.com/hashicorp/packer@v1.14.3/datasource/hcp-packer-iteration/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_iteration
     7  
     8  import (
     9  	"context"
    10  	"fmt"
    11  	"log"
    12  	"time"
    13  
    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 name of the bucket your image is in.
    31  	Bucket string `mapstructure:"bucket_name" required:"true"`
    32  	// The name of the channel to use when retrieving your image
    33  	Channel string `mapstructure:"channel" required:"true"`
    34  	// TODO: Version          string `mapstructure:"version"`
    35  	// TODO: Fingerprint          string `mapstructure:"fingerprint"`
    36  	// TODO: Label          string `mapstructure:"label"`
    37  }
    38  
    39  func (d *Datasource) ConfigSpec() hcldec.ObjectSpec {
    40  	return d.config.FlatMapstructure().HCL2Spec()
    41  }
    42  
    43  func (d *Datasource) Configure(raws ...interface{}) error {
    44  	err := config.Decode(&d.config, nil, raws...)
    45  	if err != nil {
    46  		return err
    47  	}
    48  
    49  	var errs *packersdk.MultiError
    50  
    51  	if d.config.Bucket == "" {
    52  		errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The `bucket_name` must be specified"))
    53  	}
    54  	if d.config.Channel == "" {
    55  		errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("`channel` is currently a required field."))
    56  	}
    57  
    58  	if errs != nil && len(errs.Errors) > 0 {
    59  		return errs
    60  	}
    61  	return nil
    62  }
    63  
    64  // DatasourceOutput is essentially a copy of []*models.HashicorpCloudPackerIteration, but without the
    65  // []Builds or ancestor id.
    66  type DatasourceOutput struct {
    67  	// who created the iteration
    68  	AuthorID string `mapstructure:"author_id"`
    69  	// Name of the bucket that the iteration was retrieved from
    70  	BucketName string `mapstructure:"bucket_name"`
    71  	// If true, this iteration is considered "ready to use" and will be
    72  	// returned even if the include_incomplete flag is "false" in the
    73  	// list iterations request. Note that if you are retrieving an iteration
    74  	// using a channel, this will always be "true"; channels cannot be assigned
    75  	// to incomplete iterations.
    76  	Complete bool `mapstructure:"complete"`
    77  	// The date the iteration was created.
    78  	CreatedAt string `mapstructure:"created_at"`
    79  	// The fingerprint of the build; this could be a git sha or other unique
    80  	// identifier as set by the Packer build that created this iteration.
    81  	Fingerprint string `mapstructure:"fingerprint"`
    82  	// The iteration ID. This is a ULID, which is a unique identifier similar
    83  	// to a UUID. It is created by the HCP Packer Registry when an iteration is
    84  	// first created, and is unique to this iteration.
    85  	ID string `mapstructure:"id"`
    86  	// The version number assigned to an iteration. This number is an integer,
    87  	// and is created by the HCP Packer Registry once an iteration is
    88  	// marked "complete". If a new iteration is marked "complete", the version
    89  	// that HCP Packer assigns to it will always be the highest previous
    90  	// iteration version plus one.
    91  	IncrementalVersion int32 `mapstructure:"incremental_version"`
    92  	// The date when this iteration was last updated.
    93  	UpdatedAt string `mapstructure:"updated_at"`
    94  	// The ID of the channel used to query the image iteration.
    95  	ChannelID string `mapstructure:"channel_id"`
    96  }
    97  
    98  func (d *Datasource) OutputSpec() hcldec.ObjectSpec {
    99  	return (&DatasourceOutput{}).FlatMapstructure().HCL2Spec()
   100  }
   101  
   102  func (d *Datasource) Execute() (cty.Value, error) {
   103  	log.Printf("[WARN] Deprecation: `hcp-packer-iteration` datasource has been deprecated. " +
   104  		"Please use `hcp-packer-version` datasource instead.")
   105  	ctx := context.TODO()
   106  
   107  	cli, err := hcpapi.NewDeprecatedClient()
   108  	if err != nil {
   109  		return cty.NullVal(cty.EmptyObject), err
   110  	}
   111  	// Load channel.
   112  	log.Printf("[INFO] Reading iteration info from HCP Packer registry (%s) [project_id=%s, organization_id=%s, channel=%s]",
   113  		d.config.Bucket, cli.ProjectID, cli.OrganizationID, d.config.Channel)
   114  
   115  	channel, err := cli.GetChannel(ctx, d.config.Bucket, d.config.Channel)
   116  	if err != nil {
   117  		return cty.NullVal(cty.EmptyObject), fmt.Errorf("error retrieving "+
   118  			"iteration from HCP Packer registry: %s", err.Error())
   119  	}
   120  	if channel.Iteration == nil {
   121  		return cty.NullVal(cty.EmptyObject), fmt.Errorf("there is no iteration associated with the channel %s",
   122  			d.config.Channel)
   123  	}
   124  
   125  	iteration := channel.Iteration
   126  
   127  	revokeAt := time.Time(iteration.RevokeAt)
   128  	if !revokeAt.IsZero() && revokeAt.Before(time.Now().UTC()) {
   129  		// If RevokeAt is not a zero date and is before NOW, it means this iteration is revoked and should not be used
   130  		// to build new images.
   131  		return cty.NullVal(cty.EmptyObject), fmt.Errorf("the iteration associated with the channel %s is revoked and can not be used on Packer builds",
   132  			d.config.Channel)
   133  	}
   134  
   135  	output := DatasourceOutput{
   136  		AuthorID:           iteration.AuthorID,
   137  		BucketName:         iteration.BucketSlug,
   138  		Complete:           iteration.Complete,
   139  		CreatedAt:          iteration.CreatedAt.String(),
   140  		Fingerprint:        iteration.Fingerprint,
   141  		ID:                 iteration.ID,
   142  		IncrementalVersion: iteration.IncrementalVersion,
   143  		UpdatedAt:          iteration.UpdatedAt.String(),
   144  		ChannelID:          channel.ID,
   145  	}
   146  
   147  	return hcl2helper.HCL2ValueFromConfig(output, d.OutputSpec()), nil
   148  }