-
Notifications
You must be signed in to change notification settings - Fork 3.3k
/
data.go
231 lines (205 loc) · 8.94 KB
/
data.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
//go:generate packer-sdc struct-markdown
//go:generate packer-sdc mapstructure-to-hcl2 -type DatasourceOutput,Config
package hcp_packer_image
import (
"context"
"errors"
"fmt"
"log"
"time"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/hcl/v2/hcldec"
hcpPackerDeprecatedModels "github.com/hashicorp/hcp-sdk-go/clients/cloud-packer-service/stable/2021-04-30/models"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/hcl2helper"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/config"
hcpapi "github.com/hashicorp/packer/internal/hcp/api"
)
type Datasource struct {
config Config
}
type Config struct {
common.PackerConfig `mapstructure:",squash"`
// The name of the bucket your image is in.
Bucket string `mapstructure:"bucket_name" required:"true"`
// The name of the channel to use when retrieving your image.
// Either this or `iteration_id` MUST be set.
// Mutually exclusive with `iteration_id`.
// If using several images from a single iteration, you may prefer
// sourcing an iteration first, and referencing it for subsequent uses,
// as every `hcp-packer-image` with the channel set will generate a
// potentially billable HCP Packer request, but if several
// `hcp-packer-image`s use a shared `hcp-packer-iteration` that will
// only generate one potentially billable request.
Channel string `mapstructure:"channel" required:"true"`
// The ID of the iteration to use when retrieving your image
// Either this or `channel` MUST be set.
// Mutually exclusive with `channel`
IterationID string `mapstructure:"iteration_id" required:"true"`
// The name of the cloud provider that your image is for. For example,
// "aws" or "gce".
CloudProvider string `mapstructure:"cloud_provider" required:"true"`
// The name of the cloud region your image is in. For example "us-east-1".
Region string `mapstructure:"region" required:"true"`
// The specific Packer builder used to create the image.
// For example, "amazon-ebs.example"
ComponentType string `mapstructure:"component_type" required:"false"`
// TODO: Version string `mapstructure:"version"`
// TODO: Fingerprint string `mapstructure:"fingerprint"`
// TODO: Label string `mapstructure:"label"`
}
func (d *Datasource) ConfigSpec() hcldec.ObjectSpec {
return d.config.FlatMapstructure().HCL2Spec()
}
func (d *Datasource) Configure(raws ...interface{}) error {
err := config.Decode(&d.config, nil, raws...)
if err != nil {
return err
}
var errs *packersdk.MultiError
if d.config.Bucket == "" {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The `bucket_name` must be specified"))
}
// Ensure either channel or iteration_id are set, and not both at the same time
if d.config.Channel == "" &&
d.config.IterationID == "" {
errs = packersdk.MultiErrorAppend(errs, errors.New(
"The `iteration_id` or `channel` must be specified."))
}
if d.config.Channel != "" &&
d.config.IterationID != "" {
errs = packersdk.MultiErrorAppend(errs, errors.New(
"`iteration_id` and `channel` cannot both be specified."))
}
if d.config.Region == "" {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("`region` is "+
"currently a required field."))
}
if d.config.CloudProvider == "" {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("`cloud_provider` is "+
"currently a required field."))
}
if errs != nil && len(errs.Errors) > 0 {
return errs
}
return nil
}
// DatasourceOutput Information from []*hcpPackerDeprecatedModels.HashicorpCloudPackerImage with some information
// from the parent []*hcpPackerDeprecatedModels.HashicorpCloudPackerBuild included where it seemed
// like it might be relevant. Need to copy so we can generate
type DatasourceOutput struct {
// The name of the cloud provider that the image exists in. For example,
// "aws", "azure", or "gce".
CloudProvider string `mapstructure:"cloud_provider"`
// The specific Packer builder or post-processor used to create the image.
ComponentType string `mapstructure:"component_type"`
// The date and time at which the image was created.
CreatedAt string `mapstructure:"created_at"`
// The ID of the build that created the image. This is a ULID, which is a
// unique identifier similar to a UUID. It is created by the HCP Packer
// Registry when an build is first created, and is unique to this build.
BuildID string `mapstructure:"build_id"`
// The iteration ID. This is a ULID, which is a unique identifier similar
// to a UUID. It is created by the HCP Packer Registry when an iteration is
// first created, and is unique to this iteration.
IterationID string `mapstructure:"iteration_id"`
// The ID of the channel used to query the image iteration. This value will be empty if the `iteration_id` was used
// directly instead of a channel.
ChannelID string `mapstructure:"channel_id"`
// The UUID associated with the Packer run that created this image.
PackerRunUUID string `mapstructure:"packer_run_uuid"`
// ID or URL of the remote cloud image as given by a build.
ID string `mapstructure:"id"`
// The cloud region as given by `packer build`. eg. "ap-east-1".
// For locally managed clouds, this may map instead to a cluster, server
// or datastore.
Region string `mapstructure:"region"`
// The key:value metadata labels associated with this build.
Labels map[string]string `mapstructure:"labels"`
}
func (d *Datasource) OutputSpec() hcldec.ObjectSpec {
return (&DatasourceOutput{}).FlatMapstructure().HCL2Spec()
}
func (d *Datasource) Execute() (cty.Value, error) {
log.Printf("[WARN] Deprecation: `hcp-packer-image` datasource has been deprecated. " +
"Please use `hcp-packer-artifact` datasource instead.")
ctx := context.TODO()
cli, err := hcpapi.NewDeprecatedClient()
if err != nil {
return cty.NullVal(cty.EmptyObject), err
}
var iteration *hcpPackerDeprecatedModels.HashicorpCloudPackerIteration
var channelID string
if d.config.IterationID != "" {
log.Printf("[INFO] Reading info from HCP Packer registry (%s) [project_id=%s, organization_id=%s, iteration_id=%s]",
d.config.Bucket, cli.ProjectID, cli.OrganizationID, d.config.IterationID)
iter, err := cli.GetIteration(ctx, d.config.Bucket, hcpapi.GetIteration_byID(d.config.IterationID))
if err != nil {
return cty.NullVal(cty.EmptyObject), fmt.Errorf(
"error retrieving image iteration from HCP Packer registry: %s",
err)
}
iteration = iter
} else {
log.Printf("[INFO] Reading info from HCP Packer registry (%s) [project_id=%s, organization_id=%s, channel=%s]",
d.config.Bucket, cli.ProjectID, cli.OrganizationID, d.config.Channel)
channel, err := cli.GetChannel(ctx, d.config.Bucket, d.config.Channel)
if err != nil {
return cty.NullVal(cty.EmptyObject), fmt.Errorf("error retrieving "+
"channel from HCP Packer registry: %s", err.Error())
}
if channel.Iteration == nil {
return cty.NullVal(cty.EmptyObject), fmt.Errorf("there is no iteration associated with the channel %s",
d.config.Channel)
}
channelID = channel.ID
iteration = channel.Iteration
}
revokeAt := time.Time(iteration.RevokeAt)
if !revokeAt.IsZero() && revokeAt.Before(time.Now().UTC()) {
// If RevokeAt is not a zero date and is before NOW, it means this iteration is revoked and should not be used
// to build new images.
return cty.NullVal(cty.EmptyObject), fmt.Errorf("the iteration %s is revoked and can not be used on Packer builds",
iteration.ID)
}
var output DatasourceOutput
cloudAndRegions := map[string][]string{}
for _, build := range iteration.Builds {
if build.CloudProvider != d.config.CloudProvider {
continue
}
for _, image := range build.Images {
cloudAndRegions[build.CloudProvider] = append(cloudAndRegions[build.CloudProvider], image.Region)
if image.Region == d.config.Region && filterBuildByComponentType(build, d.config.ComponentType) {
// This is the desired image.
output = DatasourceOutput{
CloudProvider: build.CloudProvider,
ComponentType: build.ComponentType,
CreatedAt: image.CreatedAt.String(),
BuildID: build.ID,
IterationID: build.IterationID,
ChannelID: channelID,
PackerRunUUID: build.PackerRunUUID,
ID: image.ImageID,
Region: image.Region,
Labels: build.Labels,
}
return hcl2helper.HCL2ValueFromConfig(output, d.OutputSpec()), nil
}
}
}
return cty.NullVal(cty.EmptyObject), fmt.Errorf("could not find a build result matching "+
"[region=%q, cloud_provider=%q, component_type=%q]. Available: %v ",
d.config.Region, d.config.CloudProvider, d.config.ComponentType, cloudAndRegions)
}
func filterBuildByComponentType(build *hcpPackerDeprecatedModels.HashicorpCloudPackerBuild, componentType string) bool {
// optional field is not specified, passthrough
if componentType == "" {
return true
}
// if specified, only the matched image metadata is returned by this effect
return build.ComponentType == componentType
}