Skip to content

Commit 6b55f55

Browse files
author
Wilken Rivera
committed
Add registryimage pkg
In order to assist in the creation of HCP Packer Registry Image metadata for various Artifacts. A new package is being added to assist in the creation of a registryimage.Image from an existing Artifact. The FromArtifact function provides a simple approach of extracting the required bits from an Artifact needed by the HCP Packer registry. The default values, which can be overwritten by the use of a ArtifactOverrideFunc , were selected to provide some standard defaults that are set to be available for most plugins were bundled with Packer prior to v1.7.0. When those defaults are not viable the package provider override funcs that can be used by the consumer for setting the appropriate values. For those plugins that generate more than one registryimage.Image per Artifact one can construct a []registryimage.Image and return that information as the response of Packer core calling artifact.State(registryimage.ArtifactStateURI).
1 parent 39969ac commit 6b55f55

2 files changed

Lines changed: 187 additions & 0 deletions

File tree

packer/registryimage/image.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package registryimage
2+
3+
import (
4+
"errors"
5+
6+
"github.com/hashicorp/packer-plugin-sdk/packer"
7+
)
8+
9+
// ArtifactStateURI represents the key used by Packer when querying an packersdk.Artifact
10+
// for Image metadata that a particular component would like to have stored on the HCP Packer Registry.
11+
const ArtifactStateURI = "par.artifact.metadata"
12+
13+
// ArtifactOverrideFunc represents a transformation func that can be applied to a
14+
// non-nil *Image. See With* functions for examples.
15+
type ArtifactOverrideFunc func(*Image) error
16+
17+
// Image represents the metadata for some Artifact in the HCP Packer Registry.
18+
type Image struct {
19+
ImageID string
20+
ProviderName, ProviderRegion string
21+
Metadata map[string]string
22+
}
23+
24+
// FromArtifact returns a *Image that can be used by Packer core for publishing to the HCP Packer Registry.
25+
// By default FromArtifact will use the a.BuilderID as the Image Provider, and the a.Id() as the ImageID that
26+
// should be tracked within the HCP Packer Registry. No Region is selected by default as region varies per build.
27+
// The use of one or more ArtifactOverrideFunc can be used to override any of the defaults used.
28+
func FromArtifact(a packer.Artifact, opts ...ArtifactOverrideFunc) *Image {
29+
if a == nil {
30+
return nil
31+
}
32+
33+
img := &Image{
34+
ProviderName: a.BuilderId(),
35+
ImageID: a.Id(),
36+
Metadata: make(map[string]string),
37+
}
38+
39+
// Let's grab some state data
40+
for _, opt := range opts {
41+
err := opt(img)
42+
if err != nil {
43+
return nil
44+
}
45+
}
46+
47+
return img
48+
}
49+
50+
// WithProvider takes a name, and returns a ArtifactOverrideFunc that can be
51+
// used to override the set name for an existing Image.
52+
func WithProvider(name string) func(*Image) error {
53+
return func(img *Image) error {
54+
if img == nil {
55+
return errors.New("no go on empty image")
56+
}
57+
58+
img.ProviderName = name
59+
return nil
60+
}
61+
}
62+
63+
// WithID takes a id, and returns a ArtifactOverrideFunc that can be
64+
// used to override the set id for an existing Image.
65+
func WithID(id string) func(*Image) error {
66+
return func(img *Image) error {
67+
if img == nil {
68+
return errors.New("no go on empty image")
69+
}
70+
71+
img.ImageID = id
72+
return nil
73+
}
74+
}
75+
76+
// WithRegion takes a region, and returns a ArtifactOverrideFunc that can be
77+
// used to override the set region for an existing Image.
78+
func WithRegion(region string) func(*Image) error {
79+
return func(img *Image) error {
80+
if img == nil {
81+
return errors.New("no go on empty image")
82+
}
83+
84+
img.ProviderRegion = region
85+
return nil
86+
}
87+
}
88+
89+
// SetMetadata takes metadata, and returns a ArtifactOverrideFunc that can be
90+
// used to set metadata for an existing Image. The incoming metadata `md`
91+
// will be filtered only for keys whose values are of type string.
92+
// If you wish to override this behavior you may create your own ArtifactOverrideFunc
93+
// for manipulating and setting Image metadata.
94+
func SetMetadata(md map[string]interface{}) func(*Image) error {
95+
return func(img *Image) error {
96+
for k, v := range md {
97+
v, ok := v.(string)
98+
if !ok {
99+
continue
100+
}
101+
img.Metadata[k] = v
102+
}
103+
104+
return nil
105+
}
106+
}

packer/registryimage/image_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package registryimage
2+
3+
import (
4+
"testing"
5+
6+
"github.com/hashicorp/packer-plugin-sdk/packer"
7+
)
8+
9+
func TestFromArtifact_defaults(t *testing.T) {
10+
artifact := new(packer.MockArtifact)
11+
12+
img := FromArtifact(artifact)
13+
14+
if img.ImageID != artifact.Id() {
15+
t.Errorf("expected resulting registryimage.Image to have a valid ImageID, but it got %q", img.ImageID)
16+
}
17+
18+
if img.ProviderName != artifact.BuilderId() {
19+
t.Errorf("expected resulting registryimage.Image to have a valid ProviderName, but it got %q", img.ProviderName)
20+
}
21+
22+
// By default no ProviderRegion is specified when generating from an Artifact
23+
if img.ProviderRegion != "" {
24+
t.Errorf("expected resulting registryimage.Image to have no ProviderRegion, but it got %q", img.ProviderRegion)
25+
}
26+
}
27+
28+
func TestFromArtifact_WithRegion(t *testing.T) {
29+
region := "TheGreatBeyond"
30+
31+
artifact := new(packer.MockArtifact)
32+
img := FromArtifact(artifact, WithRegion(region))
33+
34+
if img.ProviderRegion != region {
35+
t.Errorf("expected resulting registryimage.Image to have ProviderRegion of %q, but it got %q", region, img.ProviderRegion)
36+
}
37+
}
38+
39+
func TestFromArtifact_WithImageID(t *testing.T) {
40+
id := "some-image-id"
41+
42+
artifact := new(packer.MockArtifact)
43+
img := FromArtifact(artifact, WithID(id))
44+
45+
if img.ImageID != id {
46+
t.Errorf("expected resulting registryimage.Image to have ImageID of %q, but it got %q", id, img.ImageID)
47+
}
48+
}
49+
50+
func TestFromArtifact_WithProvider(t *testing.T) {
51+
provider := "Provider"
52+
53+
artifact := new(packer.MockArtifact)
54+
img := FromArtifact(artifact, WithProvider(provider))
55+
56+
if img.ProviderName != provider {
57+
t.Errorf("expected resulting registryimage.Image to have ProviderName of %q, but it got %q", provider, img.ProviderName)
58+
}
59+
}
60+
61+
func TestFromArtifact_SetMetadata(t *testing.T) {
62+
63+
artifact := new(packer.MockArtifact)
64+
artifact.StateValues = map[string]interface{}{
65+
"cloud": "foo",
66+
"non-string-value": 7,
67+
"slice-of-strings": []string{"foo", "bar"},
68+
}
69+
70+
img := FromArtifact(artifact, SetMetadata(artifact.StateValues))
71+
72+
if len(img.Metadata) == 0 {
73+
t.Errorf("expected resulting registryimage.Image to have some Metadata %q, but it got %q", artifact.StateValues["cloud"], img.Metadata)
74+
}
75+
76+
// Only values of string should be stored as metadata
77+
if len(img.Metadata) > 1 {
78+
t.Errorf("expected resulting registryimage.Image to have some Metadata only for string values %q, but it got %q", artifact.StateValues["cloud"], img.Metadata)
79+
}
80+
81+
}

0 commit comments

Comments
 (0)