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 }