constellation/internal/imagefetcher/imagefetcher_test.go

304 lines
7.8 KiB
Go
Raw Normal View History

/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
2023-05-23 07:17:27 +00:00
package imagefetcher
import (
"context"
"encoding/json"
"errors"
"net/http"
"testing"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/file"
"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, goleak.IgnoreAnyFunction("github.com/bazelbuild/rules_go/go/tools/bzltestutil.RegisterTimeoutHandler.func1"))
}
func TestGetReference(t *testing.T) {
testCases := map[string]struct {
info versionsapi.ImageInfo
provider cloudprovider.Provider
variant string
2023-05-23 07:17:27 +00:00
filter filter
wantReference string
wantErr bool
}{
2023-05-23 07:17:27 +00: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",
},
"reference exists aws": {
2023-05-23 07:17:27 +00:00
info: versionsapi.ImageInfo{
List: []versionsapi.ImageInfoEntry{
{CSP: "aws", AttestationVariant: "aws-nitro-tpm", Reference: "someReference"},
},
},
provider: cloudprovider.AWS,
2023-05-23 07:17:27 +00:00
variant: "aws-nitro-tpm",
wantReference: "someReference",
},
"reference exists azure": {
2023-05-23 07:17:27 +00:00
info: versionsapi.ImageInfo{
List: []versionsapi.ImageInfoEntry{
{CSP: "azure", AttestationVariant: "azure-sev-snp", Reference: "someReference"},
},
},
provider: cloudprovider.Azure,
2023-05-23 07:17:27 +00:00
variant: "azure-sev-snp",
wantReference: "someReference",
},
"reference exists gcp": {
2023-05-23 07:17:27 +00:00
info: versionsapi.ImageInfo{
List: []versionsapi.ImageInfoEntry{
{CSP: "gcp", AttestationVariant: "gcp-sev-es", Reference: "someReference"},
},
},
provider: cloudprovider.GCP,
2023-05-23 07:17:27 +00:00
variant: "gcp-sev-es",
wantReference: "someReference",
},
"reference exists openstack": {
2023-05-23 07:17:27 +00:00
info: versionsapi.ImageInfo{
List: []versionsapi.ImageInfoEntry{
{CSP: "openstack", AttestationVariant: "qemu-vtpm", Reference: "someReference"},
},
},
provider: cloudprovider.OpenStack,
2023-05-23 07:17:27 +00:00
variant: "qemu-vtpm",
wantReference: "someReference",
},
"reference exists qemu": {
2023-05-23 07:17:27 +00:00
info: versionsapi.ImageInfo{
List: []versionsapi.ImageInfoEntry{
{CSP: "qemu", AttestationVariant: "qemu-vtpm", Reference: "someReference"},
},
},
provider: cloudprovider.QEMU,
2023-05-23 07:17:27 +00:00
variant: "qemu-vtpm",
wantReference: "someReference",
},
"csp does not exist": {
2023-05-23 07:17:27 +00:00
info: versionsapi.ImageInfo{List: []versionsapi.ImageInfoEntry{}},
provider: cloudprovider.Unknown,
variant: "someVariant",
wantErr: true,
},
"variant does not exist": {
2023-05-23 07:17:27 +00:00
info: versionsapi.ImageInfo{
List: []versionsapi.ImageInfoEntry{
{CSP: "aws", AttestationVariant: "dummy", Reference: "someReference"},
},
},
provider: cloudprovider.AWS,
2023-05-23 07:17:27 +00:00
variant: "aws-nitro-tpm",
wantErr: true,
},
"info is empty": {
info: versionsapi.ImageInfo{},
provider: cloudprovider.AWS,
variant: "someVariant",
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
2023-05-23 07:17:27 +00:00
var filters []filter
if tc.filter != nil {
filters = []filter{tc.filter}
}
2023-05-23 07:17:27 +00:00
reference, err := getReferenceFromImageInfo(tc.provider, tc.variant, tc.info, filters...)
if tc.wantErr {
assert.Error(err)
return
}
require.NoError(err)
2023-05-23 07:17:27 +00:00
assert.Equal(tc.wantReference, reference)
})
}
}
func TestFetchReference(t *testing.T) {
img := "ref/abc/stream/nightly/v1.2.3"
newImgInfo := func() versionsapi.ImageInfo {
return versionsapi.ImageInfo{
Ref: "abc",
Stream: "nightly",
Version: "v1.2.3",
2023-05-23 07:17:27 +00:00
List: []versionsapi.ImageInfoEntry{
{
CSP: "qemu",
AttestationVariant: "dummy",
Reference: "someReference",
},
},
}
}
imgInfoPath := imageInfoFilename(newImgInfo())
testCases := map[string]struct {
2023-05-23 07:17:27 +00:00
provider cloudprovider.Provider
image string
imageInfoFetcher versionsAPIImageInfoFetcher
localFile []byte
wantReference string
wantErr bool
}{
"reference fetched remotely": {
2023-05-23 07:17:27 +00:00
provider: cloudprovider.QEMU,
image: img,
imageInfoFetcher: &stubVersionsAPIImageFetcher{
fetchImageInfoInfo: newImgInfo(),
},
wantReference: "someReference",
},
"reference fetched remotely fails": {
2023-05-23 07:17:27 +00:00
provider: cloudprovider.QEMU,
image: img,
imageInfoFetcher: &stubVersionsAPIImageFetcher{
fetchImageInfoErr: errors.New("failed"),
},
wantErr: true,
},
"reference fetched locally": {
2023-05-23 07:17:27 +00:00
provider: cloudprovider.QEMU,
image: img,
localFile: func() []byte {
info := newImgInfo()
2023-05-23 07:17:27 +00:00
info.List[0].Reference = "localOverrideReference"
file, err := json.Marshal(info)
require.NoError(t, err)
return file
}(),
wantReference: "localOverrideReference",
},
"local file first": {
2023-05-23 07:17:27 +00:00
provider: cloudprovider.QEMU,
image: img,
imageInfoFetcher: &stubVersionsAPIImageFetcher{
fetchImageInfoInfo: newImgInfo(),
},
localFile: func() []byte {
info := newImgInfo()
2023-05-23 07:17:27 +00:00
info.List[0].Reference = "localOverrideReference"
file, err := json.Marshal(info)
require.NoError(t, err)
return file
}(),
wantReference: "localOverrideReference",
},
"local file is invalid": {
2023-05-23 07:17:27 +00:00
provider: cloudprovider.QEMU,
image: img,
localFile: []byte("invalid"),
wantErr: true,
},
"local file has invalid image info": {
2023-05-23 07:17:27 +00:00
provider: cloudprovider.QEMU,
image: img,
localFile: func() []byte {
info := newImgInfo()
info.Ref = ""
file, err := json.Marshal(info)
require.NoError(t, err)
return file
}(),
wantErr: true,
},
"image version does not exist": {
2023-05-23 07:17:27 +00:00
provider: cloudprovider.QEMU,
image: "nonExistingImageName",
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
fs := afero.NewMemMapFs()
af := &afero.Afero{Fs: fs}
if tc.localFile != nil {
fh := file.NewHandler(af)
require.NoError(fh.Write(imgInfoPath, tc.localFile))
}
fetcher := &Fetcher{
fetcher: tc.imageInfoFetcher,
fs: af,
}
terraform: Azure Marketplace image support (#2651) * terraform: add Azure marketplace variable Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * config: add Azure marketplace variable Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * cli: use Terraform variables from config Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * terraform: pass down marketplace variable * image: pad Azure images to 1GiB * terraform: add version attribute to marketplace image * semver: allow versions to be exported without prefix * cli: boolean var to use marketplace images * config: remove dive key * dev-docs: add instructions on how to use marketplace images * terraform: fix unit test * terraform: only fetch image for non-marketplace images * mpimage: refactor image selection Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * [remove] increase minor version for image build Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * terraform: ignore changes to source_image_reference on upgrade * operator: add support for parsing Azure marketplace images Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * upgrade: fix imagefetcher call * docs: add info about azure marketplace * image: ensure more than 1GiB in size * image: test to pad to 2GiB * version: change back to v2.14.0-pre * image: GPT-conformant image size padding * [remove] increase version * mpimage: inline prefix func Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * ci: add marketplace image e2e test Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * [remove] register workflow * ci: fix workflow name * ci: only allow azure test * cli: add marketplace image input to interface * cli: fix argument passing * version: roll back to v2.14.0 * ci: add force-flag support * Update docs/docs/overview/license.md * Update dev-docs/workflows/marketplace-images.md Co-authored-by: Moritz Eckert <m1gh7ym0@gmail.com> --------- Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> Co-authored-by: Moritz Eckert <m1gh7ym0@gmail.com> Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com>
2023-12-08 13:40:31 +00:00
reference, err := fetcher.FetchReference(context.Background(), tc.provider, variant.Dummy{},
tc.image, "someRegion", false)
if tc.wantErr {
assert.Error(err)
return
}
require.NoError(err)
assert.Equal(tc.wantReference, reference)
})
}
}
type stubVersionsAPIImageFetcher struct {
fetchImageInfoInfo versionsapi.ImageInfo
fetchImageInfoErr error
}
func (f *stubVersionsAPIImageFetcher) FetchImageInfo(_ context.Context, _ versionsapi.ImageInfo) (
versionsapi.ImageInfo, error,
) {
return f.fetchImageInfoInfo, f.fetchImageInfoErr
}
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,
}
}