2022-09-05 03:06:08 -04:00
/ *
Copyright ( c ) Edgeless Systems GmbH
SPDX - License - Identifier : AGPL - 3.0 - only
* /
2022-03-22 11:03:15 -04:00
package config
import (
2023-06-01 07:55:46 -04:00
"context"
2023-05-05 07:22:53 -04:00
"errors"
2022-08-02 06:24:55 -04:00
"reflect"
2022-03-22 11:03:15 -04:00
"testing"
2023-06-09 06:48:12 -04:00
"time"
2022-03-22 11:03:15 -04:00
2022-09-07 05:53:44 -04:00
"github.com/go-playground/locales/en"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
2022-03-22 11:03:15 -04:00
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
2022-06-30 09:24:36 -04:00
"go.uber.org/goleak"
2023-05-23 03:55:49 -04:00
"gopkg.in/yaml.v3"
2023-04-05 10:49:03 -04:00
2023-06-14 08:17:52 -04:00
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
2023-04-05 10:49:03 -04:00
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config/instancetypes"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file"
2023-06-05 03:13:02 -04:00
"github.com/edgelesssys/constellation/v2/internal/semver"
"github.com/edgelesssys/constellation/v2/internal/versions"
2022-03-22 11:03:15 -04:00
)
2022-06-30 09:24:36 -04:00
func TestMain ( m * testing . M ) {
2022-11-14 03:02:56 -05:00
goleak . VerifyTestMain ( m )
2022-06-30 09:24:36 -04:00
}
2022-03-22 11:03:15 -04:00
func TestDefaultConfig ( t * testing . T ) {
assert := assert . New ( t )
def := Default ( )
assert . NotNil ( def )
}
2023-06-09 06:48:12 -04:00
func TestDefaultConfigWritesLatestVersion ( t * testing . T ) {
conf := Default ( )
bt , err := yaml . Marshal ( conf )
require := require . New ( t )
require . NoError ( err )
var mp configMap
require . NoError ( yaml . Unmarshal ( bt , & mp ) )
assert := assert . New ( t )
assert . Equal ( "latest" , mp . getAzureSEVSNPVersion ( "microcodeVersion" ) )
assert . Equal ( "latest" , mp . getAzureSEVSNPVersion ( "teeVersion" ) )
assert . Equal ( "latest" , mp . getAzureSEVSNPVersion ( "snpVersion" ) )
assert . Equal ( "latest" , mp . getAzureSEVSNPVersion ( "bootloaderVersion" ) )
}
2023-05-25 12:43:44 -04:00
2023-06-14 08:17:52 -04:00
func TestNew ( t * testing . T ) {
2023-05-23 03:55:49 -04:00
testCases := map [ string ] struct {
2023-06-14 11:50:57 -04:00
config configMap
configName string
wantResult * Config
wantErr bool
wantedErrType error
2023-05-23 03:55:49 -04:00
} {
2023-06-14 08:17:52 -04:00
"Azure SEV-SNP: mix of Latest and uint as version value in file correctly sets latest versions values" : {
2023-05-25 12:43:44 -04:00
config : func ( ) configMap {
2023-06-14 08:17:52 -04:00
conf := Default ( ) // default configures latest version
modifyConfigForAzureToPassValidate ( conf )
2023-05-23 03:55:49 -04:00
m := getConfigAsMap ( conf , t )
2023-06-09 06:48:12 -04:00
m . setAzureSEVSNPVersion ( "microcodeVersion" , "Latest" ) // check uppercase also works
m . setAzureSEVSNPVersion ( "teeVersion" , 2 )
m . setAzureSEVSNPVersion ( "bootloaderVersion" , 1 )
2023-05-23 03:55:49 -04:00
return m
} ( ) ,
2023-06-09 06:48:12 -04:00
2023-05-23 03:55:49 -04:00
configName : constants . ConfigFilename ,
2023-06-09 06:48:12 -04:00
wantResult : func ( ) * Config {
conf := Default ( )
2023-06-14 08:17:52 -04:00
modifyConfigForAzureToPassValidate ( conf )
conf . Attestation . AzureSEVSNP . MicrocodeVersion = AttestationVersion {
Value : testCfg . Microcode ,
WantLatest : true ,
2023-06-09 06:48:12 -04:00
}
conf . Attestation . AzureSEVSNP . TEEVersion = AttestationVersion {
2023-06-14 08:17:52 -04:00
Value : 2 ,
WantLatest : false ,
}
conf . Attestation . AzureSEVSNP . BootloaderVersion = AttestationVersion {
Value : 1 ,
WantLatest : false ,
}
conf . Attestation . AzureSEVSNP . SNPVersion = AttestationVersion {
Value : testCfg . SNP ,
WantLatest : true ,
2023-06-09 06:48:12 -04:00
}
return conf
} ( ) ,
2023-05-23 03:55:49 -04:00
} ,
2023-06-14 08:17:52 -04:00
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
assert := assert . New ( t )
require := require . New ( t )
fileHandler := file . NewHandler ( afero . NewMemMapFs ( ) )
if tc . config != nil {
require . NoError ( fileHandler . WriteYAML ( tc . configName , tc . config , file . OptNone ) )
}
result , err := New ( fileHandler , tc . configName , stubAttestationFetcher { } , false )
if tc . wantErr {
assert . Error ( err )
return
}
assert . NoError ( err )
assert . Equal ( tc . wantResult , result )
} )
}
}
func modifyConfigForAzureToPassValidate ( c * Config ) {
c . RemoveProviderAndAttestationExcept ( cloudprovider . Azure )
c . Image = "v" + constants . VersionInfo ( )
c . Provider . Azure . SubscriptionID = "11111111-1111-1111-1111-111111111111"
c . Provider . Azure . TenantID = "11111111-1111-1111-1111-111111111111"
c . Provider . Azure . Location = "westus"
c . Provider . Azure . ResourceGroup = "test"
c . Provider . Azure . UserAssignedIdentity = "/subscriptions/11111111-1111-1111-1111-111111111111/resourcegroups/constellation-identity/providers/Microsoft.ManagedIdentity/userAssignedIdentities/constellation-identity"
c . Attestation . AzureSEVSNP . Measurements = measurements . M {
0 : measurements . WithAllBytes ( 0x00 , measurements . Enforce , measurements . PCRMeasurementLength ) ,
}
}
func TestReadConfigFile ( t * testing . T ) {
testCases := map [ string ] struct {
config configMap
configName string
wantResult * Config
wantErr bool
wantedErrType error
} {
2023-05-23 03:55:49 -04:00
"refuse invalid version value" : {
2023-05-25 12:43:44 -04:00
config : func ( ) configMap {
2023-05-23 03:55:49 -04:00
conf := Default ( )
m := getConfigAsMap ( conf , t )
2023-05-25 12:43:44 -04:00
m . setAzureSEVSNPVersion ( "microcodeVersion" , "1a" )
2023-05-23 03:55:49 -04:00
return m
} ( ) ,
configName : constants . ConfigFilename ,
wantErr : true ,
} ,
2023-06-14 11:50:57 -04:00
"error on entering app client id" : {
config : func ( ) configMap {
conf := Default ( )
m := getConfigAsMap ( conf , t )
m . setAzureProvider ( "appClientID" , "3ea4bdc1-1cc1-4237-ae78-0831eff3491e" )
return m
} ( ) ,
configName : constants . ConfigFilename ,
wantedErrType : UnsupportedAppRegistrationError { } ,
} ,
2023-05-23 03:55:49 -04:00
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
assert := assert . New ( t )
require := require . New ( t )
fileHandler := file . NewHandler ( afero . NewMemMapFs ( ) )
if tc . config != nil {
require . NoError ( fileHandler . WriteYAML ( tc . configName , tc . config , file . OptNone ) )
}
result , err := fromFile ( fileHandler , tc . configName )
2023-06-14 11:50:57 -04:00
if tc . wantedErrType != nil {
assert . ErrorIs ( err , tc . wantedErrType )
return
}
2023-05-23 03:55:49 -04:00
if tc . wantErr {
assert . Error ( err )
2023-06-14 11:50:57 -04:00
return
2023-05-23 03:55:49 -04:00
}
2023-06-14 11:50:57 -04:00
assert . NoError ( err )
assert . Equal ( tc . wantResult , result )
2023-05-23 03:55:49 -04:00
} )
}
}
2022-03-22 11:03:15 -04:00
func TestFromFile ( t * testing . T ) {
testCases := map [ string ] struct {
2022-05-16 12:54:25 -04:00
config * Config
configName string
wantResult * Config
wantErr bool
2022-03-22 11:03:15 -04:00
} {
2022-05-16 12:54:25 -04:00
"default config from default file" : {
config : Default ( ) ,
configName : constants . ConfigFilename ,
wantResult : Default ( ) ,
} ,
"default config from different path" : {
config : Default ( ) ,
configName : "other-config.yaml" ,
wantResult : Default ( ) ,
2022-03-22 11:03:15 -04:00
} ,
2022-05-16 12:54:25 -04:00
"default config when path empty" : {
config : nil ,
configName : "" ,
2022-08-08 05:04:17 -04:00
wantErr : true ,
2022-03-22 11:03:15 -04:00
} ,
2022-05-16 12:54:25 -04:00
"err when path not exist" : {
config : nil ,
configName : "wrong-name.yaml" ,
wantErr : true ,
} ,
"custom config from default file" : {
config : & Config {
2023-05-03 05:11:53 -04:00
Version : Version3 ,
2022-05-16 12:54:25 -04:00
} ,
configName : constants . ConfigFilename ,
wantResult : & Config {
2023-05-03 05:11:53 -04:00
Version : Version3 ,
2022-05-16 12:54:25 -04:00
} ,
} ,
"modify default config" : {
config : func ( ) * Config {
conf := Default ( )
conf . Provider . GCP . Region = "eu-north1"
conf . Provider . GCP . Zone = "eu-north1-a"
return conf
} ( ) ,
configName : constants . ConfigFilename ,
wantResult : func ( ) * Config {
conf := Default ( )
conf . Provider . GCP . Region = "eu-north1"
conf . Provider . GCP . Zone = "eu-north1-a"
return conf
} ( ) ,
2022-03-22 11:03:15 -04:00
} ,
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
assert := assert . New ( t )
require := require . New ( t )
fileHandler := file . NewHandler ( afero . NewMemMapFs ( ) )
2022-05-16 12:54:25 -04:00
if tc . config != nil {
require . NoError ( fileHandler . WriteYAML ( tc . configName , tc . config , file . OptNone ) )
}
2022-03-22 11:03:15 -04:00
2023-01-31 06:12:19 -05:00
result , err := fromFile ( fileHandler , tc . configName )
2022-03-22 11:03:15 -04:00
if tc . wantErr {
assert . Error ( err )
} else {
require . NoError ( err )
2022-05-16 12:54:25 -04:00
assert . Equal ( tc . wantResult , result )
2022-03-22 11:03:15 -04:00
}
} )
}
}
2022-05-18 05:39:14 -04:00
2022-05-23 09:01:39 -04:00
func TestValidate ( t * testing . T ) {
2023-06-09 09:41:02 -04:00
const defaultErrCount = 33 // expect this number of error messages by default because user-specific values are not set and multiple providers are defined by default
2023-05-26 05:45:03 -04:00
const azErrCount = 7
2023-02-07 09:19:59 -05:00
const gcpErrCount = 6
2023-05-04 05:59:31 -04:00
// TODO(AB#3132,3u13r): refactor config validation tests
// Note that the `cnf.Image = ""` is a hack to align `bazel test` with `go test` behavior
// since first does version stamping.
2022-05-23 09:01:39 -04:00
testCases := map [ string ] struct {
2023-02-07 09:19:59 -05:00
cnf * Config
wantErr bool
wantErrCount int
2022-05-23 09:01:39 -04:00
} {
2022-11-15 09:40:49 -05:00
"default config is not valid" : {
2023-05-04 05:59:31 -04:00
cnf : func ( ) * Config {
cnf := Default ( )
cnf . Image = ""
return cnf
} ( ) ,
2023-02-07 09:19:59 -05:00
wantErr : true ,
wantErrCount : defaultErrCount ,
2022-05-23 09:01:39 -04:00
} ,
2023-06-05 03:13:02 -04:00
"outdated k8s patch version is allowed" : {
cnf : func ( ) * Config {
cnf := Default ( )
cnf . Image = ""
ver , err := semver . New ( versions . SupportedK8sVersions ( ) [ 0 ] )
require . NoError ( t , err )
ver . Patch = ver . Patch - 1
cnf . KubernetesVersion = ver . String ( )
return cnf
} ( ) ,
wantErr : true ,
wantErrCount : defaultErrCount ,
} ,
2022-11-15 09:40:49 -05:00
"v0 is one error" : {
2022-05-23 09:01:39 -04:00
cnf : func ( ) * Config {
cnf := Default ( )
2023-05-04 05:59:31 -04:00
cnf . Image = ""
2022-05-23 09:01:39 -04:00
cnf . Version = "v0"
return cnf
} ( ) ,
2023-02-07 09:19:59 -05:00
wantErr : true ,
wantErrCount : defaultErrCount + 1 ,
2022-05-23 09:01:39 -04:00
} ,
2022-11-15 09:40:49 -05:00
"v0 and negative state disk are two errors" : {
2022-05-23 09:01:39 -04:00
cnf : func ( ) * Config {
cnf := Default ( )
2023-05-04 05:59:31 -04:00
cnf . Image = ""
2022-05-23 09:01:39 -04:00
cnf . Version = "v0"
cnf . StateDiskSizeGB = - 1
return cnf
} ( ) ,
2023-02-07 09:19:59 -05:00
wantErr : true ,
wantErrCount : defaultErrCount + 2 ,
2022-11-15 09:40:49 -05:00
} ,
"default Azure config is not valid" : {
cnf : func ( ) * Config {
cnf := Default ( )
2023-05-04 05:59:31 -04:00
cnf . Image = ""
2023-05-17 10:53:56 -04:00
cnf . RemoveProviderAndAttestationExcept ( cloudprovider . Azure )
2022-11-15 09:40:49 -05:00
return cnf
} ( ) ,
2023-02-07 09:19:59 -05:00
wantErr : true ,
wantErrCount : azErrCount ,
2022-11-15 09:40:49 -05:00
} ,
"Azure config with all required fields is valid" : {
cnf : func ( ) * Config {
cnf := Default ( )
2023-05-17 10:53:56 -04:00
cnf . RemoveProviderAndAttestationExcept ( cloudprovider . Azure )
2023-03-09 09:23:42 -05:00
cnf . Image = "v" + constants . VersionInfo ( )
2022-11-15 09:40:49 -05:00
az := cnf . Provider . Azure
az . SubscriptionID = "01234567-0123-0123-0123-0123456789ab"
az . TenantID = "01234567-0123-0123-0123-0123456789ab"
az . Location = "test-location"
az . UserAssignedIdentity = "test-identity"
az . ResourceGroup = "test-resource-group"
cnf . Provider = ProviderConfig { }
cnf . Provider . Azure = az
2023-05-03 05:11:53 -04:00
cnf . Attestation . AzureSEVSNP . Measurements = measurements . M {
2023-03-10 05:33:06 -05:00
0 : measurements . WithAllBytes ( 0x00 , measurements . Enforce , measurements . PCRMeasurementLength ) ,
2023-05-03 05:11:53 -04:00
}
2022-11-15 09:40:49 -05:00
return cnf
} ( ) ,
} ,
2023-05-04 05:59:31 -04:00
2022-11-15 09:40:49 -05:00
"default GCP config is not valid" : {
cnf : func ( ) * Config {
cnf := Default ( )
2023-05-17 10:53:56 -04:00
cnf . RemoveProviderAndAttestationExcept ( cloudprovider . GCP )
2023-05-04 05:59:31 -04:00
cnf . Image = ""
2022-11-15 09:40:49 -05:00
return cnf
} ( ) ,
2023-02-07 09:19:59 -05:00
wantErr : true ,
wantErrCount : gcpErrCount ,
2022-11-15 09:40:49 -05:00
} ,
2023-05-04 05:59:31 -04:00
2022-11-15 09:40:49 -05:00
"GCP config with all required fields is valid" : {
cnf : func ( ) * Config {
cnf := Default ( )
2023-05-17 10:53:56 -04:00
cnf . RemoveProviderAndAttestationExcept ( cloudprovider . GCP )
2023-03-09 09:23:42 -05:00
cnf . Image = "v" + constants . VersionInfo ( )
2022-11-15 09:40:49 -05:00
gcp := cnf . Provider . GCP
gcp . Region = "test-region"
gcp . Project = "test-project"
gcp . Zone = "test-zone"
gcp . ServiceAccountKeyPath = "test-key-path"
cnf . Provider = ProviderConfig { }
cnf . Provider . GCP = gcp
2023-05-03 05:11:53 -04:00
cnf . Attestation . GCPSEVES . Measurements = measurements . M {
2023-03-10 05:33:06 -05:00
0 : measurements . WithAllBytes ( 0x00 , measurements . Enforce , measurements . PCRMeasurementLength ) ,
2023-05-03 05:11:53 -04:00
}
2022-11-15 09:40:49 -05:00
return cnf
} ( ) ,
2022-05-23 09:01:39 -04:00
} ,
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
assert := assert . New ( t )
2023-02-07 09:19:59 -05:00
require := require . New ( t )
2022-05-23 09:01:39 -04:00
2023-01-31 05:45:31 -05:00
err := tc . cnf . Validate ( false )
2023-02-07 09:19:59 -05:00
2022-11-15 09:40:49 -05:00
if tc . wantErr {
assert . Error ( err )
2023-02-07 09:19:59 -05:00
var valErr * ValidationError
require . ErrorAs ( err , & valErr )
assert . Equal ( tc . wantErrCount , valErr . messagesCount ( ) )
2022-11-15 09:40:49 -05:00
return
}
assert . NoError ( err )
2022-05-23 09:01:39 -04:00
} )
}
}
func TestHasProvider ( t * testing . T ) {
assert := assert . New ( t )
assert . False ( ( & Config { } ) . HasProvider ( cloudprovider . Unknown ) )
assert . False ( ( & Config { } ) . HasProvider ( cloudprovider . Azure ) )
assert . False ( ( & Config { } ) . HasProvider ( cloudprovider . GCP ) )
assert . False ( ( & Config { } ) . HasProvider ( cloudprovider . QEMU ) )
assert . False ( Default ( ) . HasProvider ( cloudprovider . Unknown ) )
assert . True ( Default ( ) . HasProvider ( cloudprovider . Azure ) )
assert . True ( Default ( ) . HasProvider ( cloudprovider . GCP ) )
cnfWithAzure := Config { Provider : ProviderConfig { Azure : & AzureConfig { } } }
assert . False ( cnfWithAzure . HasProvider ( cloudprovider . Unknown ) )
assert . True ( cnfWithAzure . HasProvider ( cloudprovider . Azure ) )
assert . False ( cnfWithAzure . HasProvider ( cloudprovider . GCP ) )
}
2022-05-18 05:39:14 -04:00
func TestConfigRemoveProviderExcept ( t * testing . T ) {
testCases := map [ string ] struct {
removeExcept cloudprovider . Provider
2022-10-21 06:24:18 -04:00
wantAWS * AWSConfig
2022-05-18 05:39:14 -04:00
wantAzure * AzureConfig
wantGCP * GCPConfig
wantQEMU * QEMUConfig
} {
2022-10-21 06:24:18 -04:00
"except aws" : {
removeExcept : cloudprovider . AWS ,
wantAWS : Default ( ) . Provider . AWS ,
} ,
2022-05-18 05:39:14 -04:00
"except azure" : {
removeExcept : cloudprovider . Azure ,
wantAzure : Default ( ) . Provider . Azure ,
} ,
"except gcp" : {
removeExcept : cloudprovider . GCP ,
wantGCP : Default ( ) . Provider . GCP ,
} ,
"except qemu" : {
removeExcept : cloudprovider . QEMU ,
wantQEMU : Default ( ) . Provider . QEMU ,
} ,
"unknown provider" : {
removeExcept : cloudprovider . Unknown ,
2022-10-21 06:24:18 -04:00
wantAWS : Default ( ) . Provider . AWS ,
2022-05-18 05:39:14 -04:00
wantAzure : Default ( ) . Provider . Azure ,
wantGCP : Default ( ) . Provider . GCP ,
wantQEMU : Default ( ) . Provider . QEMU ,
} ,
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
assert := assert . New ( t )
conf := Default ( )
2023-05-17 10:53:56 -04:00
conf . RemoveProviderAndAttestationExcept ( tc . removeExcept )
2022-05-18 05:39:14 -04:00
2022-10-21 06:24:18 -04:00
assert . Equal ( tc . wantAWS , conf . Provider . AWS )
2022-05-18 05:39:14 -04:00
assert . Equal ( tc . wantAzure , conf . Provider . Azure )
assert . Equal ( tc . wantGCP , conf . Provider . GCP )
assert . Equal ( tc . wantQEMU , conf . Provider . QEMU )
} )
}
}
2022-05-18 12:10:57 -04:00
func TestConfigGeneratedDocsFresh ( t * testing . T ) {
assert := assert . New ( t )
2022-08-02 06:24:55 -04:00
updateMsg := "remember to re-generate config docs! 🔨"
assert . Len ( ConfigDoc . Fields , reflect . ValueOf ( Config { } ) . NumField ( ) , updateMsg )
assert . Len ( ProviderConfigDoc . Fields , reflect . ValueOf ( ProviderConfig { } ) . NumField ( ) , updateMsg )
2022-11-22 12:47:08 -05:00
assert . Len ( AWSConfigDoc . Fields , reflect . ValueOf ( AWSConfig { } ) . NumField ( ) , updateMsg )
2022-08-02 06:24:55 -04:00
assert . Len ( AzureConfigDoc . Fields , reflect . ValueOf ( AzureConfig { } ) . NumField ( ) , updateMsg )
assert . Len ( GCPConfigDoc . Fields , reflect . ValueOf ( GCPConfig { } ) . NumField ( ) , updateMsg )
assert . Len ( QEMUConfigDoc . Fields , reflect . ValueOf ( QEMUConfig { } ) . NumField ( ) , updateMsg )
2022-05-18 12:10:57 -04:00
}
2022-08-01 03:37:05 -04:00
func TestConfig_UpdateMeasurements ( t * testing . T ) {
assert := assert . New ( t )
2022-11-15 09:40:49 -05:00
newMeasurements := measurements . M {
2023-03-10 05:33:06 -05:00
1 : measurements . WithAllBytes ( 0x00 , measurements . Enforce , measurements . PCRMeasurementLength ) ,
2 : measurements . WithAllBytes ( 0x01 , measurements . Enforce , measurements . PCRMeasurementLength ) ,
3 : measurements . WithAllBytes ( 0x02 , measurements . Enforce , measurements . PCRMeasurementLength ) ,
2022-08-01 03:37:05 -04:00
}
2022-10-21 06:24:18 -04:00
{ // AWS
conf := Default ( )
2023-05-17 10:53:56 -04:00
conf . RemoveProviderAndAttestationExcept ( cloudprovider . AWS )
2023-06-09 09:41:02 -04:00
for k := range conf . Attestation . AWSSEVSNP . Measurements {
delete ( conf . Attestation . AWSSEVSNP . Measurements , k )
2022-10-21 06:24:18 -04:00
}
conf . UpdateMeasurements ( newMeasurements )
2023-06-09 09:41:02 -04:00
assert . Equal ( newMeasurements , conf . Attestation . AWSSEVSNP . Measurements )
2022-10-21 06:24:18 -04:00
}
2022-08-01 03:37:05 -04:00
{ // Azure
conf := Default ( )
2023-05-17 10:53:56 -04:00
conf . RemoveProviderAndAttestationExcept ( cloudprovider . Azure )
2023-05-03 05:11:53 -04:00
for k := range conf . Attestation . AzureSEVSNP . Measurements {
delete ( conf . Attestation . AzureSEVSNP . Measurements , k )
2022-08-01 03:37:05 -04:00
}
conf . UpdateMeasurements ( newMeasurements )
2023-05-03 05:11:53 -04:00
assert . Equal ( newMeasurements , conf . Attestation . AzureSEVSNP . Measurements )
2022-08-01 03:37:05 -04:00
}
{ // GCP
conf := Default ( )
2023-05-17 10:53:56 -04:00
conf . RemoveProviderAndAttestationExcept ( cloudprovider . GCP )
2023-05-03 05:11:53 -04:00
for k := range conf . Attestation . GCPSEVES . Measurements {
delete ( conf . Attestation . GCPSEVES . Measurements , k )
2022-08-01 03:37:05 -04:00
}
conf . UpdateMeasurements ( newMeasurements )
2023-05-03 05:11:53 -04:00
assert . Equal ( newMeasurements , conf . Attestation . GCPSEVES . Measurements )
2022-08-01 03:37:05 -04:00
}
{ // QEMU
conf := Default ( )
2023-05-17 10:53:56 -04:00
conf . RemoveProviderAndAttestationExcept ( cloudprovider . QEMU )
2023-05-03 05:11:53 -04:00
for k := range conf . Attestation . QEMUVTPM . Measurements {
delete ( conf . Attestation . QEMUVTPM . Measurements , k )
2022-08-01 03:37:05 -04:00
}
conf . UpdateMeasurements ( newMeasurements )
2023-05-03 05:11:53 -04:00
assert . Equal ( newMeasurements , conf . Attestation . QEMUVTPM . Measurements )
2022-08-01 03:37:05 -04:00
}
}
2022-08-16 09:53:54 -04:00
2022-11-22 12:47:08 -05:00
func TestConfig_IsReleaseImage ( t * testing . T ) {
2022-08-16 09:53:54 -04:00
testCases := map [ string ] struct {
conf * Config
want bool
} {
2022-11-22 12:47:08 -05:00
"release image v0.0.0" : {
2022-08-16 09:53:54 -04:00
conf : func ( ) * Config {
conf := Default ( )
2022-11-22 12:47:08 -05:00
conf . Image = "v0.0.0"
2022-08-16 09:53:54 -04:00
return conf
} ( ) ,
want : true ,
} ,
2022-11-22 12:47:08 -05:00
"branch image" : {
2022-08-16 09:53:54 -04:00
conf : func ( ) * Config {
conf := Default ( )
2022-11-22 12:47:08 -05:00
conf . Image = "feat-x-vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef"
2022-08-16 09:53:54 -04:00
return conf
} ( ) ,
want : false ,
} ,
2022-11-22 12:47:08 -05:00
"debug image" : {
2022-08-16 09:53:54 -04:00
conf : func ( ) * Config {
conf := Default ( )
2022-11-22 12:47:08 -05:00
conf . Image = "debug-vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef"
2022-08-16 09:53:54 -04:00
return conf
} ( ) ,
2022-11-22 12:47:08 -05:00
want : false ,
2022-08-16 09:53:54 -04:00
} ,
"empty config" : {
conf : & Config { } ,
want : false ,
} ,
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
assert := assert . New ( t )
2022-11-22 12:47:08 -05:00
assert . Equal ( tc . want , tc . conf . IsReleaseImage ( ) )
2022-08-16 09:53:54 -04:00
} )
}
}
2022-08-31 11:35:33 -04:00
func TestValidInstanceTypeForProvider ( t * testing . T ) {
testCases := map [ string ] struct {
provider cloudprovider . Provider
instanceTypes [ ] string
nonCVMsAllowed bool
expectedResult bool
} {
"empty all" : {
provider : cloudprovider . Unknown ,
instanceTypes : [ ] string { } ,
expectedResult : false ,
} ,
2022-10-21 06:24:18 -04:00
"empty aws" : {
provider : cloudprovider . AWS ,
instanceTypes : [ ] string { } ,
expectedResult : false ,
} ,
2022-08-31 11:35:33 -04:00
"empty azure only CVMs" : {
provider : cloudprovider . Azure ,
instanceTypes : [ ] string { } ,
expectedResult : false ,
} ,
"empty azure with non-CVMs" : {
provider : cloudprovider . Azure ,
instanceTypes : [ ] string { } ,
nonCVMsAllowed : true ,
expectedResult : false ,
} ,
"empty gcp" : {
provider : cloudprovider . GCP ,
instanceTypes : [ ] string { } ,
expectedResult : false ,
} ,
"azure only CVMs" : {
provider : cloudprovider . Azure ,
instanceTypes : instancetypes . AzureCVMInstanceTypes ,
expectedResult : true ,
} ,
"azure CVMs but CVMs disabled" : {
provider : cloudprovider . Azure ,
instanceTypes : instancetypes . AzureCVMInstanceTypes ,
nonCVMsAllowed : true ,
expectedResult : false ,
} ,
"azure trusted launch VMs with CVMs enabled" : {
provider : cloudprovider . Azure ,
instanceTypes : instancetypes . AzureTrustedLaunchInstanceTypes ,
expectedResult : false ,
} ,
2022-10-21 06:24:18 -04:00
"azure trusted launch VMs with CVMs disabled" : {
2022-08-31 11:35:33 -04:00
provider : cloudprovider . Azure ,
instanceTypes : instancetypes . AzureTrustedLaunchInstanceTypes ,
nonCVMsAllowed : true ,
expectedResult : true ,
} ,
"gcp" : {
provider : cloudprovider . GCP ,
instanceTypes : instancetypes . GCPInstanceTypes ,
expectedResult : true ,
} ,
"put gcp when azure is set" : {
provider : cloudprovider . Azure ,
instanceTypes : instancetypes . GCPInstanceTypes ,
expectedResult : false ,
} ,
"put gcp when azure is set with CVMs disabled" : {
provider : cloudprovider . Azure ,
instanceTypes : instancetypes . GCPInstanceTypes ,
nonCVMsAllowed : true ,
expectedResult : false ,
} ,
"put azure when gcp is set" : {
provider : cloudprovider . GCP ,
instanceTypes : instancetypes . AzureCVMInstanceTypes ,
expectedResult : false ,
} ,
"put azure when gcp is set with CVMs disabled" : {
provider : cloudprovider . GCP ,
instanceTypes : instancetypes . AzureTrustedLaunchInstanceTypes ,
nonCVMsAllowed : true ,
expectedResult : false ,
} ,
2022-10-21 06:24:18 -04:00
// Testing every possible instance type for AWS is not feasible, so we just test a few based on known supported / unsupported families
// Also serves as a test for checkIfInstanceInValidAWSFamilys
"aws two valid instances" : {
provider : cloudprovider . AWS ,
instanceTypes : [ ] string { "c5.xlarge" , "c5a.2xlarge" , "c5a.16xlarge" , "u-12tb1.112xlarge" } ,
expectedResult : true ,
} ,
"aws one valid instance one with too little vCPUs" : {
provider : cloudprovider . AWS ,
instanceTypes : [ ] string { "c5.medium" } ,
expectedResult : false ,
} ,
"aws graviton sub-family unsupported" : {
provider : cloudprovider . AWS ,
instanceTypes : [ ] string { "m6g.xlarge" , "r6g.2xlarge" , "x2gd.xlarge" , "g5g.8xlarge" } ,
expectedResult : false ,
} ,
"aws combined two valid instances as one string" : {
provider : cloudprovider . AWS ,
instanceTypes : [ ] string { "c5.xlarge, c5a.2xlarge" } ,
expectedResult : false ,
} ,
2022-08-31 11:35:33 -04:00
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
assert := assert . New ( t )
for _ , instanceType := range tc . instanceTypes {
assert . Equal ( tc . expectedResult , validInstanceTypeForProvider ( instanceType , tc . nonCVMsAllowed , tc . provider ) , instanceType )
}
} )
}
}
2022-09-05 10:53:37 -04:00
func TestIsDebugCluster ( t * testing . T ) {
testCases := map [ string ] struct {
config * Config
prepareConfig func ( * Config )
expectedResult bool
} {
"empty config" : {
config : & Config { } ,
expectedResult : false ,
} ,
"default config" : {
config : Default ( ) ,
expectedResult : false ,
} ,
"enabled" : {
config : Default ( ) ,
prepareConfig : func ( conf * Config ) {
* conf . DebugCluster = true
} ,
expectedResult : true ,
} ,
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
assert := assert . New ( t )
if tc . prepareConfig != nil {
tc . prepareConfig ( tc . config )
}
assert . Equal ( tc . expectedResult , tc . config . IsDebugCluster ( ) )
} )
}
}
2022-09-07 05:53:44 -04:00
func TestValidateProvider ( t * testing . T ) {
testCases := map [ string ] struct {
provider ProviderConfig
wantErr bool
expectedErrorTag string
} {
"empty, should trigger no provider error" : {
provider : ProviderConfig { } ,
wantErr : true ,
expectedErrorTag : "no_provider" ,
} ,
"azure only, should be okay" : {
provider : ProviderConfig {
Azure : & AzureConfig { } ,
} ,
wantErr : false ,
} ,
"gcp only, should be okay" : {
provider : ProviderConfig {
GCP : & GCPConfig { } ,
} ,
wantErr : false ,
} ,
"qemu only, should be okay" : {
provider : ProviderConfig {
QEMU : & QEMUConfig { } ,
} ,
wantErr : false ,
} ,
"azure and gcp, should trigger multiple provider error" : {
provider : ProviderConfig {
Azure : & AzureConfig { } ,
GCP : & GCPConfig { } ,
} ,
wantErr : true ,
expectedErrorTag : "more_than_one_provider" ,
} ,
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
assert := assert . New ( t )
require := require . New ( t )
v := validator . New ( )
trans := ut . New ( en . New ( ) ) . GetFallback ( )
conf := Default ( )
conf . Provider = tc . provider
v . RegisterStructValidation ( validateProvider , ProviderConfig { } )
err := v . StructPartial ( tc . provider )
// Register provider validation error types.
// Make sure the tags and expected strings below are in sync with the actual implementation.
require . NoError ( v . RegisterTranslation ( "no_provider" , trans , registerNoProviderError , translateNoProviderError ) )
require . NoError ( v . RegisterTranslation ( "more_than_one_provider" , trans , registerMoreThanOneProviderError , conf . translateMoreThanOneProviderError ) )
// Continue if no error is expected.
if ! tc . wantErr {
assert . NoError ( err )
return
}
// Validate if the error was identified correctly.
require . NotNil ( err )
assert . Error ( err )
assert . Contains ( err . Error ( ) , tc . expectedErrorTag )
// Check if error translation works correctly.
validationErr := err . ( validator . ValidationErrors )
translatedErr := validationErr . Translate ( trans )
// The translator does not seem to export a list of available translations or for a specific field.
// So we need to hardcode expected strings. Needs to be in sync with implementation.
switch tc . expectedErrorTag {
case "no_provider" :
assert . Contains ( translatedErr [ "ProviderConfig.Provider" ] , "No provider has been defined" )
case "more_than_one_provider" :
assert . Contains ( translatedErr [ "ProviderConfig.Provider" ] , "Only one provider can be defined" )
}
} )
}
}
2023-01-24 16:20:10 -05:00
func TestConfigVersionCompatibility ( t * testing . T ) {
2023-05-03 05:11:53 -04:00
t . Skip ( ) // TODO(daniel-weisse): re-enable and re-write for config v3
2023-01-24 16:20:10 -05:00
testCases := map [ string ] struct {
config string
expectedConfig * Config
} {
"config v2 gcp" : {
config : "testdata/configGCPV2.yaml" ,
expectedConfig : & Config {
Version : "v2" ,
Image : "v2.5.0" ,
StateDiskSizeGB : 16 ,
KubernetesVersion : "1.23" ,
DebugCluster : toPtr ( false ) ,
Provider : ProviderConfig {
GCP : & GCPConfig {
Project : "project-12345" ,
Region : "europe-west3" ,
Zone : "europe-west3-b" ,
ServiceAccountKeyPath : "serviceAccountKey.json" ,
InstanceType : "n2d-standard-4" ,
StateDiskType : "pd-ssd" ,
DeployCSIDriver : toPtr ( true ) ,
} ,
} ,
} ,
} ,
"config v2 aws" : {
config : "testdata/configAWSV2.yaml" ,
expectedConfig : & Config {
Version : "v2" ,
Image : "v2.5.0" ,
StateDiskSizeGB : 16 ,
KubernetesVersion : "1.23" ,
DebugCluster : toPtr ( false ) ,
Provider : ProviderConfig {
AWS : & AWSConfig {
Region : "us-east-2" ,
Zone : "us-east-2a" ,
InstanceType : "c5.xlarge" ,
StateDiskType : "gp2" ,
IAMProfileControlPlane : "control_plane_instance_profile" ,
IAMProfileWorkerNodes : "node_instance_profile" ,
} ,
} ,
} ,
} ,
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
assert := assert . New ( t )
fileHandler := file . NewHandler ( afero . NewOsFs ( ) )
2023-01-31 06:12:19 -05:00
config , err := fromFile ( fileHandler , tc . config )
2023-01-24 16:20:10 -05:00
assert . NoError ( err )
assert . Equal ( tc . expectedConfig , config )
} )
}
}
2023-05-25 12:43:44 -04:00
2023-06-14 11:50:57 -04:00
func TestIsAppClientIDError ( t * testing . T ) {
testCases := map [ string ] struct {
err error
expected bool
} {
"yaml.Error with appClientID error" : {
err : & yaml . TypeError {
Errors : [ ] string {
"invalid value for appClientID" ,
"another error" ,
} ,
} ,
expected : true ,
} ,
"yaml.Error without appClientID error" : {
err : & yaml . TypeError {
Errors : [ ] string {
"invalid value for something else" ,
"another error" ,
} ,
} ,
expected : false ,
} ,
"other error" : {
err : errors . New ( "appClientID but other error type" ) ,
expected : false ,
} ,
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
assert := assert . New ( t )
assert . Equal ( tc . expected , isAppClientIDError ( tc . err ) )
} )
}
}
2023-05-25 12:43:44 -04:00
// configMap is used to un-/marshal the config as an unstructured map.
type configMap map [ string ] interface { }
func ( c configMap ) setAzureSEVSNPVersion ( versionType string , value interface { } ) {
c [ "attestation" ] . ( configMap ) [ "azureSEVSNP" ] . ( configMap ) [ versionType ] = value
}
2023-06-14 11:50:57 -04:00
func ( c configMap ) setAzureProvider ( azureProviderField string , value interface { } ) {
c [ "provider" ] . ( configMap ) [ "azure" ] . ( configMap ) [ azureProviderField ] = value
}
2023-06-09 06:48:12 -04:00
func ( c configMap ) getAzureSEVSNPVersion ( versionType string ) interface { } {
return c [ "attestation" ] . ( configMap ) [ "azureSEVSNP" ] . ( configMap ) [ versionType ]
}
2023-05-25 12:43:44 -04:00
// getConfigAsMap returns a map of the config.
func getConfigAsMap ( conf * Config , t * testing . T ) ( res configMap ) {
bytes , err := yaml . Marshal ( & conf )
if err != nil {
t . Fatal ( err )
}
if err := yaml . Unmarshal ( bytes , & res ) ; err != nil {
t . Fatal ( err )
}
return
}
2023-06-09 06:48:12 -04:00
type stubAttestationFetcher struct { }
2023-05-25 12:43:44 -04:00
2023-06-14 08:17:52 -04:00
func ( f stubAttestationFetcher ) FetchAzureSEVSNPVersionList ( _ context . Context , _ attestationconfigapi . AzureSEVSNPVersionList ) ( attestationconfigapi . AzureSEVSNPVersionList , error ) {
return attestationconfigapi . AzureSEVSNPVersionList (
2023-06-01 07:55:46 -04:00
[ ] string { } ,
) , nil
}
2023-05-25 12:43:44 -04:00
2023-06-14 08:17:52 -04:00
func ( f stubAttestationFetcher ) FetchAzureSEVSNPVersion ( _ context . Context , _ attestationconfigapi . AzureSEVSNPVersionAPI ) ( attestationconfigapi . AzureSEVSNPVersionAPI , error ) {
return attestationconfigapi . AzureSEVSNPVersionAPI {
2023-06-01 07:55:46 -04:00
AzureSEVSNPVersion : testCfg ,
} , nil
2023-05-25 12:43:44 -04:00
}
2023-06-14 08:17:52 -04:00
func ( f stubAttestationFetcher ) FetchAzureSEVSNPVersionLatest ( _ context . Context , _ time . Time ) ( attestationconfigapi . AzureSEVSNPVersionAPI , error ) {
return attestationconfigapi . AzureSEVSNPVersionAPI {
2023-06-02 03:19:23 -04:00
AzureSEVSNPVersion : testCfg ,
} , nil
2023-06-01 07:55:46 -04:00
}
2023-06-14 08:17:52 -04:00
var testCfg = attestationconfigapi . AzureSEVSNPVersion {
2023-06-01 07:55:46 -04:00
Microcode : 93 ,
TEE : 0 ,
SNP : 6 ,
Bootloader : 2 ,
2023-05-25 12:43:44 -04:00
}