2022-11-22 12:47:08 -05:00
|
|
|
/*
|
|
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
2023-05-23 03:17:27 -04:00
|
|
|
package imagefetcher
|
2022-11-22 12:47:08 -05:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-01-03 03:52:06 -05:00
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
2022-11-22 12:47:08 -05:00
|
|
|
"net/http"
|
|
|
|
"testing"
|
|
|
|
|
2023-06-07 10:16:32 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
|
2023-06-09 09:41:02 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
2022-11-22 12:47:08 -05:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
2023-01-03 03:52:06 -05:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
2022-11-22 12:47:08 -05:00
|
|
|
"github.com/spf13/afero"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"go.uber.org/goleak"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestMain(m *testing.M) {
|
|
|
|
goleak.VerifyTestMain(m)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetReference(t *testing.T) {
|
|
|
|
testCases := map[string]struct {
|
2023-01-03 03:52:06 -05:00
|
|
|
info versionsapi.ImageInfo
|
|
|
|
provider cloudprovider.Provider
|
|
|
|
variant string
|
2023-05-23 03:17:27 -04:00
|
|
|
filter filter
|
2022-11-22 12:47:08 -05:00
|
|
|
wantReference string
|
|
|
|
wantErr bool
|
|
|
|
}{
|
2023-05-23 03:17:27 -04:00
|
|
|
"reference exists with filter": {
|
|
|
|
info: versionsapi.ImageInfo{
|
|
|
|
List: []versionsapi.ImageInfoEntry{
|
|
|
|
{CSP: "aws", AttestationVariant: "aws-nitro-tpm", Reference: "someReference"},
|
|
|
|
{CSP: "aws", AttestationVariant: "aws-nitro-tpm", Reference: "someOtherReference", Region: "someRegion"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
provider: cloudprovider.AWS,
|
|
|
|
variant: "aws-nitro-tpm",
|
|
|
|
filter: func(entry versionsapi.ImageInfoEntry) bool {
|
|
|
|
return entry.Region == "someRegion"
|
|
|
|
},
|
|
|
|
wantReference: "someOtherReference",
|
|
|
|
},
|
2023-01-03 03:52:06 -05:00
|
|
|
"reference exists aws": {
|
2023-05-23 03:17:27 -04:00
|
|
|
info: versionsapi.ImageInfo{
|
|
|
|
List: []versionsapi.ImageInfoEntry{
|
|
|
|
{CSP: "aws", AttestationVariant: "aws-nitro-tpm", Reference: "someReference"},
|
|
|
|
},
|
|
|
|
},
|
2023-01-03 03:52:06 -05:00
|
|
|
provider: cloudprovider.AWS,
|
2023-05-23 03:17:27 -04:00
|
|
|
variant: "aws-nitro-tpm",
|
2023-01-03 03:52:06 -05:00
|
|
|
wantReference: "someReference",
|
|
|
|
},
|
|
|
|
"reference exists azure": {
|
2023-05-23 03:17:27 -04:00
|
|
|
info: versionsapi.ImageInfo{
|
|
|
|
List: []versionsapi.ImageInfoEntry{
|
|
|
|
{CSP: "azure", AttestationVariant: "azure-sev-snp", Reference: "someReference"},
|
|
|
|
},
|
|
|
|
},
|
2023-01-03 03:52:06 -05:00
|
|
|
provider: cloudprovider.Azure,
|
2023-05-23 03:17:27 -04:00
|
|
|
variant: "azure-sev-snp",
|
2023-01-03 03:52:06 -05:00
|
|
|
wantReference: "someReference",
|
|
|
|
},
|
|
|
|
"reference exists gcp": {
|
2023-05-23 03:17:27 -04:00
|
|
|
info: versionsapi.ImageInfo{
|
|
|
|
List: []versionsapi.ImageInfoEntry{
|
|
|
|
{CSP: "gcp", AttestationVariant: "gcp-sev-es", Reference: "someReference"},
|
|
|
|
},
|
|
|
|
},
|
2023-01-03 03:52:06 -05:00
|
|
|
provider: cloudprovider.GCP,
|
2023-05-23 03:17:27 -04:00
|
|
|
variant: "gcp-sev-es",
|
2023-01-03 03:52:06 -05:00
|
|
|
wantReference: "someReference",
|
|
|
|
},
|
2023-02-27 12:19:52 -05:00
|
|
|
"reference exists openstack": {
|
2023-05-23 03:17:27 -04:00
|
|
|
info: versionsapi.ImageInfo{
|
|
|
|
List: []versionsapi.ImageInfoEntry{
|
|
|
|
{CSP: "openstack", AttestationVariant: "qemu-vtpm", Reference: "someReference"},
|
|
|
|
},
|
|
|
|
},
|
2023-02-27 12:19:52 -05:00
|
|
|
provider: cloudprovider.OpenStack,
|
2023-05-23 03:17:27 -04:00
|
|
|
variant: "qemu-vtpm",
|
2023-02-27 12:19:52 -05:00
|
|
|
wantReference: "someReference",
|
|
|
|
},
|
2023-01-03 03:52:06 -05:00
|
|
|
"reference exists qemu": {
|
2023-05-23 03:17:27 -04:00
|
|
|
info: versionsapi.ImageInfo{
|
|
|
|
List: []versionsapi.ImageInfoEntry{
|
|
|
|
{CSP: "qemu", AttestationVariant: "qemu-vtpm", Reference: "someReference"},
|
|
|
|
},
|
|
|
|
},
|
2023-01-03 03:52:06 -05:00
|
|
|
provider: cloudprovider.QEMU,
|
2023-05-23 03:17:27 -04:00
|
|
|
variant: "qemu-vtpm",
|
2022-11-22 12:47:08 -05:00
|
|
|
wantReference: "someReference",
|
|
|
|
},
|
|
|
|
"csp does not exist": {
|
2023-05-23 03:17:27 -04:00
|
|
|
info: versionsapi.ImageInfo{List: []versionsapi.ImageInfoEntry{}},
|
2023-01-03 03:52:06 -05:00
|
|
|
provider: cloudprovider.Unknown,
|
|
|
|
variant: "someVariant",
|
|
|
|
wantErr: true,
|
2022-11-22 12:47:08 -05:00
|
|
|
},
|
|
|
|
"variant does not exist": {
|
2023-05-23 03:17:27 -04:00
|
|
|
info: versionsapi.ImageInfo{
|
|
|
|
List: []versionsapi.ImageInfoEntry{
|
|
|
|
{CSP: "aws", AttestationVariant: "dummy", Reference: "someReference"},
|
|
|
|
},
|
|
|
|
},
|
2023-01-03 03:52:06 -05:00
|
|
|
provider: cloudprovider.AWS,
|
2023-05-23 03:17:27 -04:00
|
|
|
variant: "aws-nitro-tpm",
|
2023-01-03 03:52:06 -05:00
|
|
|
wantErr: true,
|
2022-11-22 12:47:08 -05:00
|
|
|
},
|
2023-01-03 03:52:06 -05:00
|
|
|
"info is empty": {
|
|
|
|
info: versionsapi.ImageInfo{},
|
|
|
|
provider: cloudprovider.AWS,
|
|
|
|
variant: "someVariant",
|
|
|
|
wantErr: true,
|
2022-12-01 05:51:33 -05:00
|
|
|
},
|
2022-11-22 12:47:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
for name, tc := range testCases {
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
assert := assert.New(t)
|
|
|
|
require := require.New(t)
|
|
|
|
|
2023-05-23 03:17:27 -04:00
|
|
|
var filters []filter
|
|
|
|
if tc.filter != nil {
|
|
|
|
filters = []filter{tc.filter}
|
2022-11-22 12:47:08 -05:00
|
|
|
}
|
2023-05-23 03:17:27 -04:00
|
|
|
reference, err := getReferenceFromImageInfo(tc.provider, tc.variant, tc.info, filters...)
|
2022-11-22 12:47:08 -05:00
|
|
|
|
|
|
|
if tc.wantErr {
|
|
|
|
assert.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
require.NoError(err)
|
2023-05-23 03:17:27 -04:00
|
|
|
assert.Equal(tc.wantReference, reference)
|
2022-11-22 12:47:08 -05:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFetchReference(t *testing.T) {
|
2022-12-09 05:51:38 -05:00
|
|
|
img := "ref/abc/stream/nightly/v1.2.3"
|
2023-01-03 03:52:06 -05:00
|
|
|
newImgInfo := func() versionsapi.ImageInfo {
|
|
|
|
return versionsapi.ImageInfo{
|
|
|
|
Ref: "abc",
|
|
|
|
Stream: "nightly",
|
|
|
|
Version: "v1.2.3",
|
2023-05-23 03:17:27 -04:00
|
|
|
List: []versionsapi.ImageInfoEntry{
|
|
|
|
{
|
|
|
|
CSP: "qemu",
|
|
|
|
AttestationVariant: "dummy",
|
|
|
|
Reference: "someReference",
|
|
|
|
},
|
|
|
|
},
|
2022-11-22 12:47:08 -05:00
|
|
|
}
|
2023-01-03 03:52:06 -05:00
|
|
|
}
|
|
|
|
imgInfoPath := imageInfoFilename(newImgInfo())
|
2022-11-22 12:47:08 -05:00
|
|
|
|
|
|
|
testCases := map[string]struct {
|
2023-05-23 03:17:27 -04:00
|
|
|
provider cloudprovider.Provider
|
|
|
|
image string
|
2023-01-03 03:52:06 -05:00
|
|
|
imageInfoFetcher versionsAPIImageInfoFetcher
|
|
|
|
localFile []byte
|
|
|
|
wantReference string
|
|
|
|
wantErr bool
|
2022-11-22 12:47:08 -05:00
|
|
|
}{
|
|
|
|
"reference fetched remotely": {
|
2023-05-23 03:17:27 -04:00
|
|
|
provider: cloudprovider.QEMU,
|
|
|
|
image: img,
|
2023-01-03 03:52:06 -05:00
|
|
|
imageInfoFetcher: &stubVersionsAPIImageFetcher{
|
|
|
|
fetchImageInfoInfo: newImgInfo(),
|
|
|
|
},
|
2022-11-22 12:47:08 -05:00
|
|
|
wantReference: "someReference",
|
|
|
|
},
|
2023-01-03 03:52:06 -05:00
|
|
|
"reference fetched remotely fails": {
|
2023-05-23 03:17:27 -04:00
|
|
|
provider: cloudprovider.QEMU,
|
|
|
|
image: img,
|
2023-01-03 03:52:06 -05:00
|
|
|
imageInfoFetcher: &stubVersionsAPIImageFetcher{
|
|
|
|
fetchImageInfoErr: errors.New("failed"),
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
2022-11-22 12:47:08 -05:00
|
|
|
"reference fetched locally": {
|
2023-05-23 03:17:27 -04:00
|
|
|
provider: cloudprovider.QEMU,
|
|
|
|
image: img,
|
2023-01-03 03:52:06 -05:00
|
|
|
localFile: func() []byte {
|
|
|
|
info := newImgInfo()
|
2023-05-23 03:17:27 -04:00
|
|
|
info.List[0].Reference = "localOverrideReference"
|
2023-01-03 03:52:06 -05:00
|
|
|
file, err := json.Marshal(info)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return file
|
|
|
|
}(),
|
2022-11-22 12:47:08 -05:00
|
|
|
wantReference: "localOverrideReference",
|
|
|
|
},
|
2023-01-03 03:52:06 -05:00
|
|
|
"local file first": {
|
2023-05-23 03:17:27 -04:00
|
|
|
provider: cloudprovider.QEMU,
|
|
|
|
image: img,
|
2023-01-03 03:52:06 -05:00
|
|
|
imageInfoFetcher: &stubVersionsAPIImageFetcher{
|
|
|
|
fetchImageInfoInfo: newImgInfo(),
|
|
|
|
},
|
|
|
|
localFile: func() []byte {
|
|
|
|
info := newImgInfo()
|
2023-05-23 03:17:27 -04:00
|
|
|
info.List[0].Reference = "localOverrideReference"
|
2023-01-03 03:52:06 -05:00
|
|
|
file, err := json.Marshal(info)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return file
|
|
|
|
}(),
|
|
|
|
wantReference: "localOverrideReference",
|
2022-11-22 12:47:08 -05:00
|
|
|
},
|
2023-01-03 03:52:06 -05:00
|
|
|
"local file is invalid": {
|
2023-05-23 03:17:27 -04:00
|
|
|
provider: cloudprovider.QEMU,
|
|
|
|
image: img,
|
2023-01-03 03:52:06 -05:00
|
|
|
localFile: []byte("invalid"),
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
"local file has invalid image info": {
|
2023-05-23 03:17:27 -04:00
|
|
|
provider: cloudprovider.QEMU,
|
|
|
|
image: img,
|
2023-01-03 03:52:06 -05:00
|
|
|
localFile: func() []byte {
|
|
|
|
info := newImgInfo()
|
|
|
|
info.Ref = ""
|
|
|
|
file, err := json.Marshal(info)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return file
|
|
|
|
}(),
|
2022-11-22 12:47:08 -05:00
|
|
|
wantErr: true,
|
|
|
|
},
|
2023-01-03 03:52:06 -05:00
|
|
|
"image version does not exist": {
|
2023-05-23 03:17:27 -04:00
|
|
|
provider: cloudprovider.QEMU,
|
|
|
|
image: "nonExistingImageName",
|
|
|
|
wantErr: true,
|
2022-11-22 12:47:08 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, tc := range testCases {
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
assert := assert.New(t)
|
|
|
|
require := require.New(t)
|
|
|
|
|
2023-01-03 03:52:06 -05:00
|
|
|
fs := afero.NewMemMapFs()
|
|
|
|
af := &afero.Afero{Fs: fs}
|
|
|
|
if tc.localFile != nil {
|
|
|
|
fh := file.NewHandler(af)
|
|
|
|
require.NoError(fh.Write(imgInfoPath, tc.localFile))
|
|
|
|
}
|
|
|
|
|
2022-11-22 12:47:08 -05:00
|
|
|
fetcher := &Fetcher{
|
2023-01-03 03:52:06 -05:00
|
|
|
fetcher: tc.imageInfoFetcher,
|
|
|
|
fs: af,
|
2022-11-22 12:47:08 -05:00
|
|
|
}
|
2023-01-03 03:52:06 -05:00
|
|
|
|
2023-05-23 03:17:27 -04:00
|
|
|
reference, err := fetcher.FetchReference(context.Background(), tc.provider, variant.Dummy{}, tc.image, "someRegion")
|
2023-01-03 03:52:06 -05:00
|
|
|
|
2022-11-22 12:47:08 -05:00
|
|
|
if tc.wantErr {
|
|
|
|
assert.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
require.NoError(err)
|
|
|
|
assert.Equal(tc.wantReference, reference)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-03 03:52:06 -05:00
|
|
|
type stubVersionsAPIImageFetcher struct {
|
|
|
|
fetchImageInfoInfo versionsapi.ImageInfo
|
|
|
|
fetchImageInfoErr error
|
|
|
|
}
|
|
|
|
|
2023-03-20 06:03:36 -04:00
|
|
|
func (f *stubVersionsAPIImageFetcher) FetchImageInfo(_ context.Context, _ versionsapi.ImageInfo) (
|
2023-01-03 03:52:06 -05:00
|
|
|
versionsapi.ImageInfo, error,
|
|
|
|
) {
|
|
|
|
return f.fetchImageInfoInfo, f.fetchImageInfoErr
|
|
|
|
}
|
|
|
|
|
2022-11-22 12:47:08 -05:00
|
|
|
func must(t *testing.T, err error) {
|
|
|
|
t.Helper()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// roundTripFunc .
|
|
|
|
type roundTripFunc func(req *http.Request) *http.Response
|
|
|
|
|
|
|
|
// RoundTrip .
|
|
|
|
func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
|
return f(req), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// newTestClient returns *http.Client with Transport replaced to avoid making real calls.
|
|
|
|
func newTestClient(fn roundTripFunc) *http.Client {
|
|
|
|
return &http.Client{
|
|
|
|
Transport: fn,
|
|
|
|
}
|
|
|
|
}
|