diff --git a/internal/osbuild/skopeo_source.go b/internal/osbuild/skopeo_source.go new file mode 100644 index 000000000..fde02287c --- /dev/null +++ b/internal/osbuild/skopeo_source.go @@ -0,0 +1,73 @@ +package osbuild + +import ( + "fmt" + "regexp" +) + +var skopeoDigestPattern = regexp.MustCompile(`sha256:[0-9a-f]{64}`) + +type SkopeoSource struct { + Items map[string]SkopeoSourceItem `json:"items"` +} + +func (SkopeoSource) isSource() {} + +type SkopeopSourceImage struct { + Name string `json:"name"` + Digest string `json:"digest"` + TLSVerify *bool `json:"tls-verify,omitempty"` +} + +type SkopeoSourceItem struct { + Image SkopeopSourceImage `json:"image"` +} + +// NewSkopeoSourceItem creates a new source item for name and digest +func NewSkopeoSourceItem(name, digest string, tlsVerify *bool) SkopeoSourceItem { + item := SkopeoSourceItem{ + Image: SkopeopSourceImage{ + Name: name, + Digest: digest, + TLSVerify: tlsVerify, + }, + } + + return item +} + +func (item SkopeoSourceItem) validate() error { + + if item.Image.Name == "" { + return fmt.Errorf("source item has empty name") + } + + if !skopeoDigestPattern.MatchString(item.Image.Digest) { + return fmt.Errorf("source item has invalid digest") + } + + return nil +} + +// NewSkopeoSource creates a new and empty SkopeoSource +func NewSkopeoSource() *SkopeoSource { + return &SkopeoSource{ + Items: make(map[string]SkopeoSourceItem), + } +} + +// AddItem adds a source item to the source; will panic +// if any of the supplied options are invalid or missing +func (source *SkopeoSource) AddItem(name, digest, image string, tlsVerify *bool) { + item := NewSkopeoSourceItem(name, digest, tlsVerify) + + if err := item.validate(); err != nil { + panic(err) + } + + if !skopeoDigestPattern.MatchString(image) { + panic("item has invalid image id") + } + + source.Items[image] = item +} diff --git a/internal/osbuild/skopeo_source_test.go b/internal/osbuild/skopeo_source_test.go new file mode 100644 index 000000000..5ca113c59 --- /dev/null +++ b/internal/osbuild/skopeo_source_test.go @@ -0,0 +1,58 @@ +package osbuild + +import ( + "testing" + + "github.com/osbuild/osbuild-composer/internal/common" + "github.com/stretchr/testify/assert" +) + +func TestNewSkopeoSource(t *testing.T) { + testDigest := "sha256:f29b6cd42a94a574583439addcd6694e6224f0e4b32044c9e3aee4c4856c2a50" + imageID := "sha256:c2ecf25cf190e76b12b07436ad5140d4ba53d8a136d498705e57a006837a720f" + + source := NewSkopeoSource() + + source.AddItem("name", testDigest, imageID, common.BoolToPtr(false)) + assert.Len(t, source.Items, 1) + + item, ok := source.Items[imageID] + assert.True(t, ok) + assert.Equal(t, item.Image.Name, "name") + assert.Equal(t, item.Image.Digest, testDigest) + assert.Equal(t, item.Image.TLSVerify, common.BoolToPtr(false)) + + testDigest = "sha256:d49eebefb6c7ce5505594bef652bd4adc36f413861bd44209d9b9486310b1264" + imageID = "sha256:d2ab8fea7f08a22f03b30c13c6ea443121f25e87202a7496e93736efa6fe345a" + + source.AddItem("name2", testDigest, imageID, nil) + assert.Len(t, source.Items, 2) + item, ok = source.Items[imageID] + assert.True(t, ok) + assert.Nil(t, item.Image.TLSVerify) + + // empty name + assert.Panics(t, func() { + source.AddItem("", testDigest, imageID, nil) + }) + + // empty digest + assert.Panics(t, func() { + source.AddItem("name", "", imageID, nil) + }) + + // empty image id + assert.Panics(t, func() { + source.AddItem("name", testDigest, "", nil) + }) + + // invalid digest + assert.Panics(t, func() { + source.AddItem("name", "foo", imageID, nil) + }) + + // invalid image id + assert.Panics(t, func() { + source.AddItem("name", testDigest, "sha256:foo", nil) + }) +}