-
Notifications
You must be signed in to change notification settings - Fork 59
Expand file tree
/
Copy pathimage.go
More file actions
168 lines (143 loc) · 5.63 KB
/
image.go
File metadata and controls
168 lines (143 loc) · 5.63 KB
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
/* Package image allows for the management of image metadata that can be stored in a HCP Packer registry.
*/
package image
import (
"errors"
"fmt"
"reflect"
"github.com/hashicorp/packer-plugin-sdk/packer"
)
// ArtifactStateURI represents the key used by Packer when querying a packersdk.Artifact
// for Image metadata that a particular component would like to have stored on the HCP Packer Registry.
const ArtifactStateURI = "par.artifact.metadata"
// ArtifactOverrideFunc represents a transformation func that can be applied to a
// non-nil *Image. See WithID, WithRegion functions for examples.
type ArtifactOverrideFunc func(*Image) error
// Image represents the metadata for some Artifact in the HCP Packer Registry.
type Image struct {
// ImageID is a unique reference identifier stored on the HCP Packer registry
// that can be used to get back the built artifact of a builder or post-processor.
ImageID string
// ProviderName represents the name of the top level cloud or service where the built artifact resides.
// For example "aws, azure, docker, gcp, and vsphere".
ProviderName string
// ProviderRegion represents the location of the built artifact.
// For cloud providers region usually maps to a cloud region or zone, but for things like the file builder,
// S3 bucket or vsphere cluster region can represent a path on the upstream datastore, or cluster.
ProviderRegion string
// Labels represents additional details about an image that a builder or post-processor may with to provide for a given build.
// Any additional metadata will be made available as build labels within a HCP Packer registry iteration.
Labels map[string]string
}
// Validate checks that the Image i contains a non-empty ImageID and ProviderName.
func (i *Image) Validate() error {
if i.ImageID == "" {
return errors.New("error registry image does not contain a valid ImageId")
}
if i.ProviderName == "" {
return errors.New("error registry image does not contain a valid ProviderName")
}
return nil
}
// String returns string representation of Image
func (i *Image) String() string {
return fmt.Sprintf("provider:%s, image:%s, region:%s", i.ProviderName, i.ImageID, i.ProviderRegion)
}
// FromMappedData calls f sequentially for each key and value present in mappedData to create a []*Image
// as the final return value. If there is an error in calling f, FromMappedData will stop processing mapped items
// and result in a nil slice, with the said error.
//
// FromMappedData will make its best attempt to convert the input map into map[interface{}]interface{} before
// calling f(k,v). The func f is responsible for type asserting the expected type for the key and value before
// trying to create an Image from it.
func FromMappedData(mappedData interface{}, f func(key, value interface{}) (*Image, error)) ([]*Image, error) {
mapValue := reflect.ValueOf(mappedData)
if mapValue.Kind() != reflect.Map {
return nil, errors.New("error the incoming mappedData does not appear to be a map; found type to be" + mapValue.Kind().String())
}
keys := mapValue.MapKeys()
var images []*Image
for _, k := range keys {
v := mapValue.MapIndex(k)
i, err := f(k.Interface(), v.Interface())
if err != nil {
return nil, err
}
images = append(images, i)
}
return images, nil
}
// FromArtifact returns an *Image that can be used by Packer core for publishing to the HCP Packer Registry.
// By default FromArtifact will use the a.BuilderID() as the ProviderName, and the a.Id() as the ImageID that
// should be tracked within the HCP Packer Registry. No Region is selected by default as region varies per builder.
// The use of one or more ArtifactOverrideFunc can be used to override any of the defaults used.
func FromArtifact(a packer.Artifact, opts ...ArtifactOverrideFunc) (*Image, error) {
if a == nil {
return nil, errors.New("unable to create Image from nil artifact")
}
img := Image{
ProviderName: a.BuilderId(),
ImageID: a.Id(),
Labels: make(map[string]string),
}
for _, opt := range opts {
err := opt(&img)
if err != nil {
return nil, err
}
}
return &img, nil
}
// WithProvider takes a name, and returns a ArtifactOverrideFunc that can be
// used to override the ProviderName for an existing Image.
func WithProvider(name string) func(*Image) error {
return func(img *Image) error {
if img == nil {
return errors.New("no go on empty image")
}
img.ProviderName = name
return nil
}
}
// WithID takes a id, and returns a ArtifactOverrideFunc that can be
// used to override the ImageId for an existing Image.
func WithID(id string) func(*Image) error {
return func(img *Image) error {
if img == nil {
return errors.New("no go on empty image")
}
img.ImageID = id
return nil
}
}
// WithRegion takes a region, and returns a ArtifactOverrideFunc that can be
// used to override the ProviderRegion for an existing Image.
func WithRegion(region string) func(*Image) error {
return func(img *Image) error {
if img == nil {
return errors.New("no go on empty image")
}
img.ProviderRegion = region
return nil
}
}
// SetLabels takes metadata, and returns a ArtifactOverrideFunc that can be
// used to set metadata for an existing Image. The incoming metadata `md`
// will be filtered only for keys whose values are of type string.
// If you wish to override this behavior you may create your own ArtifactOverrideFunc
// for manipulating and setting Image metadata.
func SetLabels(md map[string]interface{}) func(*Image) error {
return func(img *Image) error {
if img.Labels == nil {
img.Labels = make(map[string]string)
}
for k, v := range md {
v, ok := v.(string)
if !ok {
continue
}
img.Labels[k] = v
}
return nil
}
}