AB#2117 cli: validate config (#170)

* AB#2117 cli: validate config

* update hack/go.mod
This commit is contained in:
Thomas Tendyck 2022-05-23 15:01:39 +02:00 committed by GitHub
parent 45bf9f15fb
commit 2ba3c153de
12 changed files with 312 additions and 58 deletions

View File

@ -9,7 +9,6 @@ import (
"github.com/edgelesssys/constellation/cli/cloud/cloudcmd"
"github.com/edgelesssys/constellation/cli/cloudprovider"
"github.com/edgelesssys/constellation/cli/gcp"
"github.com/edgelesssys/constellation/internal/config"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero"
@ -68,7 +67,7 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
return err
}
config, err := config.FromFile(fileHandler, flags.configPath)
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath, provider)
if err != nil {
return err
}

View File

@ -75,7 +75,17 @@ func initialize(ctx context.Context, cmd *cobra.Command, protCl protoClient, ser
return err
}
config, err := config.FromFile(fileHandler, flags.configPath)
var stat state.ConstellationState
err = fileHandler.ReadJSON(constants.StateFilename, &stat)
if errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("nothing to initialize: %w", err)
} else if err != nil {
return err
}
provider := cloudprovider.FromString(stat.CloudProvider)
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath, provider)
if err != nil {
return err
}
@ -88,17 +98,7 @@ func initialize(ctx context.Context, cmd *cobra.Command, protCl protoClient, ser
})
}
protoSSHUserKeys := ssh.ToProtoSlice(sshUsers)
var stat state.ConstellationState
err = fileHandler.ReadJSON(constants.StateFilename, &stat)
if errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("nothing to initialize: %w", err)
} else if err != nil {
return err
}
validators, err := cloudcmd.NewValidators(cloudprovider.FromString(stat.CloudProvider), config)
validators, err := cloudcmd.NewValidators(provider, config)
if err != nil {
return err
}
@ -141,7 +141,7 @@ func initialize(ctx context.Context, cmd *cobra.Command, protCl protoClient, ser
coordinatorPrivIPs: coordinators.PrivateIPs()[1:],
autoscalingNodeGroups: autoscalingNodeGroups,
cloudServiceAccountURI: serviceAccount,
sshUserKeys: protoSSHUserKeys,
sshUserKeys: ssh.ToProtoSlice(sshUsers),
}
result, err := activate(ctx, cmd, protCl, input, validators.V())
if err != nil {

43
cli/cmd/readconfig.go Normal file
View File

@ -0,0 +1,43 @@
package cmd
import (
"errors"
"fmt"
"io"
"github.com/edgelesssys/constellation/cli/cloudprovider"
"github.com/edgelesssys/constellation/internal/config"
"github.com/edgelesssys/constellation/internal/file"
)
func readConfig(out io.Writer, fileHandler file.Handler, name string, provider cloudprovider.Provider) (*config.Config, error) {
cnf, err := config.FromFile(fileHandler, name)
if err != nil {
return nil, err
}
if err := validateConfig(out, cnf, provider); err != nil {
return nil, err
}
return cnf, nil
}
func validateConfig(out io.Writer, cnf *config.Config, provider cloudprovider.Provider) error {
msgs, err := cnf.Validate()
if err != nil {
return err
}
if len(msgs) > 0 {
fmt.Fprintln(out, "Invalid fields in config file:")
for _, m := range msgs {
fmt.Fprintln(out, "\t"+m)
}
return errors.New("invalid configuration")
}
if provider != cloudprovider.Unknown && !cnf.HasProvider(provider) {
return fmt.Errorf("configuration doesn't contain provider: %v", provider)
}
return nil
}

View File

@ -0,0 +1,78 @@
package cmd
import (
"bytes"
"testing"
"github.com/edgelesssys/constellation/cli/cloudprovider"
"github.com/edgelesssys/constellation/internal/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestValidateConfig(t *testing.T) {
testCases := map[string]struct {
cnf *config.Config
provider cloudprovider.Provider
wantOutput bool
wantErr bool
}{
"default config is valid": {
cnf: config.Default(),
},
"config with an error": {
cnf: func() *config.Config {
cnf := config.Default()
cnf.Version = "v0"
return cnf
}(),
wantOutput: true,
wantErr: true,
},
"config without provider is ok if no provider required": {
cnf: func() *config.Config {
cnf := config.Default()
cnf.Provider = config.ProviderConfig{}
return cnf
}(),
},
"config with only required provider": {
cnf: func() *config.Config {
cnf := config.Default()
az := cnf.Provider.Azure
cnf.Provider = config.ProviderConfig{}
cnf.Provider.Azure = az
return cnf
}(),
provider: cloudprovider.Azure,
},
"config without required provider": {
cnf: func() *config.Config {
cnf := config.Default()
cnf.Provider.Azure = nil
return cnf
}(),
provider: cloudprovider.Azure,
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
out := &bytes.Buffer{}
err := validateConfig(out, tc.cnf, tc.provider)
if tc.wantErr {
assert.Error(err)
return
}
require.NoError(err)
assert.Equal(tc.wantOutput, out.Len() > 0)
})
}
}

View File

@ -11,7 +11,6 @@ import (
"github.com/edgelesssys/constellation/cli/cloudprovider"
"github.com/edgelesssys/constellation/cli/proto"
"github.com/edgelesssys/constellation/coordinator/util"
"github.com/edgelesssys/constellation/internal/config"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/state"
@ -51,17 +50,19 @@ func recover(ctx context.Context, cmd *cobra.Command, fileHandler file.Handler,
return err
}
config, err := config.FromFile(fileHandler, flags.configPath)
if err != nil {
return err
}
var stat state.ConstellationState
if err := fileHandler.ReadJSON(constants.StateFilename, &stat); err != nil {
return err
}
validators, err := cloudcmd.NewValidators(cloudprovider.FromString(stat.CloudProvider), config)
provider := cloudprovider.FromString(stat.CloudProvider)
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath, provider)
if err != nil {
return err
}
validators, err := cloudcmd.NewValidators(provider, config)
if err != nil {
return err
}

View File

@ -8,7 +8,6 @@ import (
"github.com/edgelesssys/constellation/cli/cloud/cloudcmd"
"github.com/edgelesssys/constellation/cli/cloudprovider"
"github.com/edgelesssys/constellation/cli/proto"
"github.com/edgelesssys/constellation/internal/config"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero"
@ -49,7 +48,7 @@ func verify(ctx context.Context, cmd *cobra.Command, provider cloudprovider.Prov
return err
}
config, err := config.FromFile(fileHandler, flags.configPath)
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath, provider)
if err != nil {
return err
}

7
go.mod
View File

@ -64,6 +64,9 @@ require (
github.com/coreos/go-systemd/v22 v22.3.2
github.com/docker/docker v20.10.13+incompatible
github.com/docker/go-connections v0.4.0
github.com/go-playground/locales v0.14.0
github.com/go-playground/universal-translator v0.18.0
github.com/go-playground/validator/v10 v10.11.0
github.com/google/go-tpm v0.3.3
github.com/google/go-tpm-tools v0.3.5
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
@ -105,8 +108,6 @@ require (
sigs.k8s.io/yaml v1.3.0
)
require github.com/hashicorp/errwrap v1.1.0 // indirect
require (
cloud.google.com/go v0.100.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v0.9.1 // indirect
@ -161,6 +162,7 @@ require (
github.com/google/go-cmp v0.5.7 // indirect
github.com/google/go-tspi v0.3.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/icholy/replace v0.5.0
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
@ -169,6 +171,7 @@ require (
github.com/josharian/native v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mdlayher/genetlink v1.2.0 // indirect

17
go.sum
View File

@ -649,8 +649,16 @@ github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/
github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU=
github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
@ -990,8 +998,9 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -1000,6 +1009,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
@ -1230,6 +1241,7 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -1303,6 +1315,9 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM=
github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=

View File

@ -97,6 +97,9 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.11.0 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
@ -114,6 +117,7 @@ require (
github.com/josharian/native v1.0.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mdlayher/genetlink v1.2.0 // indirect
github.com/mdlayher/netlink v1.6.0 // indirect
github.com/mdlayher/socket v0.2.1 // indirect

View File

@ -374,8 +374,16 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
@ -602,6 +610,7 @@ github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/icholy/replace v0.5.0 h1:Nx80zYQVlowdba+3Y6dvHDnmxaGtBrDlf2wYn9GyIXQ=
github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
@ -654,8 +663,9 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -664,6 +674,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
@ -781,6 +793,7 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@ -833,6 +846,9 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@ -1632,6 +1648,7 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.24.0 h1:J0hann2hfxWr1hinZIDefw7Q96wmCBx6SSB8IY0MdDg=
k8s.io/apimachinery v0.24.0 h1:ydFCyC/DjCvFCHK5OPMKBlxayQytB8pxy8YQInd5UyQ=
k8s.io/apiserver v0.24.0 h1:GR7kGsjOMfilRvlG3Stxv/3uz/ryvJ/aZXc5pqdsNV0=
k8s.io/client-go v0.24.0 h1:lbE4aB1gTHvYFSwm6eD3OF14NhFDKCejlnsGYlSJe5U=
k8s.io/cluster-bootstrap v0.24.0 h1:MTs2x3Vfcl/PWvB5bfX7gzTFRyi4ZSbNSQgGJTCb6Sw=
k8s.io/component-base v0.24.0 h1:h5jieHZQoHrY/lHG+HyrSbJeyfuitheBvqvKwKHVC0g=

View File

@ -11,6 +11,10 @@ import (
"github.com/edgelesssys/constellation/cli/cloudprovider"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/go-playground/locales/en"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
en_translations "github.com/go-playground/validator/v10/translations/en"
)
const (
@ -21,21 +25,21 @@ const (
type Config struct {
// description: |
// Schema version of this configuration file.
Version string `yaml:"version"`
Version string `yaml:"version" validate:"eq=v1"`
// description: |
// Minimum number of nodes in autoscaling group.
// worker nodes.
AutoscalingNodeGroupsMin int `yaml:"autoscalingNodeGroupsMin"`
AutoscalingNodeGroupsMin int `yaml:"autoscalingNodeGroupsMin" validate:"min=0"`
// description: |
// Maximum number of nodes in autoscaling group.
// worker nodes.
AutoscalingNodeGroupsMax int `yaml:"autoscalingNodeGroupsMax"`
AutoscalingNodeGroupsMax int `yaml:"autoscalingNodeGroupsMax" validate:"gtefield=AutoscalingNodeGroupsMin"`
// description: |
// Size (in GB) of data disk used for nodes.
StateDiskSizeGB int `yaml:"stateDisksizeGB"`
StateDiskSizeGB int `yaml:"stateDisksizeGB" validate:"min=0"`
// description: |
// Ingress firewall rules for node network.
IngressFirewall Firewall `yaml:"ingressFirewall,omitempty"`
IngressFirewall Firewall `yaml:"ingressFirewall,omitempty" validate:"dive"`
// description: |
// Egress firewall rules for node network.
// examples:
@ -49,46 +53,46 @@ type Config struct {
// ToPort: 443,
// },
// }'
EgressFirewall Firewall `yaml:"egressFirewall,omitempty"`
EgressFirewall Firewall `yaml:"egressFirewall,omitempty" validate:"dive"`
// description: |
// Supported cloud providers & their specific configurations.
Provider ProviderConfig `yaml:"provider"`
Provider ProviderConfig `yaml:"provider" validate:"dive"`
// description: |
// Create SSH users on Constellation nodes.
// examples:
// - value: '[]UserKey{ { Username: "Alice", PublicKey: "ssh-rsa AAAAB3NzaC...5QXHKW1rufgtJeSeJ8= alice@domain.com" } }'
SSHUsers []UserKey `yaml:"sshUsers,omitempty"`
SSHUsers []UserKey `yaml:"sshUsers,omitempty" validate:"dive"`
}
// UserKey describes a user that should be created with corresponding public SSH key.
type UserKey struct {
// description: |
// Username of new SSH user.
Username string `yaml:"username"`
Username string `yaml:"username" validate:"required"`
// description: |
// Public key of new SSH user.
PublicKey string `yaml:"publicKey"`
PublicKey string `yaml:"publicKey" validate:"required"`
}
type FirewallRule struct {
// description: |
// Name of rule.
Name string `yaml:"name"`
Name string `yaml:"name" validate:"required"`
// description: |
// Description for rule.
Description string `yaml:"description"`
// description: |
// Protocol, such as 'udp' or 'tcp'.
Protocol string `yaml:"protocol"`
Protocol string `yaml:"protocol" validate:"required"`
// description: |
// CIDR range for which this rule is applied.
IPRange string `yaml:"iprange"`
IPRange string `yaml:"iprange" validate:"required"`
// description: |
// Port of start port of a range.
FromPort int `yaml:"fromport"`
FromPort int `yaml:"fromport" validate:"min=0,max=65535"`
// description: |
// End port of a range, or 0 if a single port is given by FromPort.
ToPort int `yaml:"toport"`
ToPort int `yaml:"toport" validate:"omitempty,gtefield=FromPort,max=65535"`
}
type Firewall []FirewallRule
@ -99,51 +103,51 @@ type Firewall []FirewallRule
type ProviderConfig struct {
// description: |
// Configuration for Azure as provider.
Azure *AzureConfig `yaml:"azureConfig,omitempty"`
Azure *AzureConfig `yaml:"azureConfig,omitempty" validate:"omitempty,dive"`
// description: |
// Configuration for Google Cloud as provider.
GCP *GCPConfig `yaml:"gcpConfig,omitempty"`
GCP *GCPConfig `yaml:"gcpConfig,omitempty" validate:"omitempty,dive"`
// description: |
// Configuration for QEMU as provider.
QEMU *QEMUConfig `yaml:"qemuConfig,omitempty"`
QEMU *QEMUConfig `yaml:"qemuConfig,omitempty" validate:"omitempty,dive"`
}
// AzureConfig are Azure specific configuration values used by the CLI.
type AzureConfig struct {
// description: |
// Subscription ID of the used Azure account. See: https://docs.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription
SubscriptionID string `yaml:"subscription"`
SubscriptionID string `yaml:"subscription" validate:"uuid"`
// description: |
// Tenant ID of the used Azure account. See: https://docs.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-ad-tenant
TenantID string `yaml:"tenant"`
TenantID string `yaml:"tenant" validate:"uuid"`
// description: |
// Azure datacenter region to be used. See: https://docs.microsoft.com/en-us/azure/availability-zones/az-overview#azure-regions-with-availability-zones
Location string `yaml:"location"`
Location string `yaml:"location" validate:"required"`
// description: |
// Machine image used to create Constellation nodes.
Image string `yaml:"image"`
Image string `yaml:"image" validate:"required"`
// description: |
// Expected confidential VM measurements.
Measurements Measurements `yaml:"measurements"`
// description: |
// Authorize spawned VMs to access Azure API. See: https://constellation-docs.edgeless.systems/6c320851-bdd2-41d5-bf10-e27427398692/#/getting-started/install?id=azure
UserAssignedIdentity string `yaml:"userassignedIdentity"`
UserAssignedIdentity string `yaml:"userassignedIdentity" validate:"required"`
}
// GCPConfig are GCP specific configuration values used by the CLI.
type GCPConfig struct {
// description: |
// GCP project. See: https://support.google.com/googleapi/answer/7014113?hl=en
Project string `yaml:"project"`
Project string `yaml:"project" validate:"required"`
// description: |
// GCP datacenter region. See: https://cloud.google.com/compute/docs/regions-zones#available
Region string `yaml:"region"`
Region string `yaml:"region" validate:"required"`
// description: |
// GCP datacenter zone. See: https://cloud.google.com/compute/docs/regions-zones#available
Zone string `yaml:"zone"`
Zone string `yaml:"zone" validate:"required"`
// description: |
// Machine image used to create Constellation nodes.
Image string `yaml:"image"`
Image string `yaml:"image" validate:"required"`
// description: |
// Roles added to service account.
ServiceAccountRoles []string `yaml:"serviceAccountRoles"`
@ -226,6 +230,45 @@ func Default() *Config {
}
}
// Validate checks the config values and returns validation error messages.
// The function only returns an error if the validation itself fails.
func (c *Config) Validate() ([]string, error) {
trans := ut.New(en.New()).GetFallback()
validate := validator.New()
if err := en_translations.RegisterDefaultTranslations(validate, trans); err != nil {
return nil, err
}
err := validate.Struct(c)
if err == nil {
return nil, nil
}
var errs validator.ValidationErrors
if !errors.As(err, &errs) {
return nil, err
}
var msgs []string
for _, e := range errs {
msgs = append(msgs, e.Translate(trans))
}
return msgs, nil
}
// HasProvider checks whether the config contains the provider.
func (c *Config) HasProvider(provider cloudprovider.Provider) bool {
switch provider {
case cloudprovider.Azure:
return c.Provider.Azure != nil
case cloudprovider.GCP:
return c.Provider.GCP != nil
case cloudprovider.QEMU:
return c.Provider.QEMU != nil
}
return false
}
// RemoveProviderExcept removes all provider specific configurations, i.e.,
// sets them to nil, except the one specified.
// If an unknown provider is passed, the same configuration is returned.
@ -259,8 +302,5 @@ func FromFile(fileHandler file.Handler, name string) (*Config, error) {
}
return nil, fmt.Errorf("could not load config from file %s: %w", name, err)
}
if emptyConf.Version != Version1 {
return nil, fmt.Errorf("config version (%s) is not supported - only version %s is supported", emptyConf.Version, Version1)
}
return &emptyConf, nil
}

View File

@ -141,6 +141,61 @@ func TestFromFileStrictErrors(t *testing.T) {
}
}
func TestValidate(t *testing.T) {
testCases := map[string]struct {
cnf *Config
wantMsgCount int
}{
"default config is valid": {
cnf: Default(),
wantMsgCount: 0,
},
"config with 1 error": {
cnf: func() *Config {
cnf := Default()
cnf.Version = "v0"
return cnf
}(),
wantMsgCount: 1,
},
"config with 2 errors": {
cnf: func() *Config {
cnf := Default()
cnf.Version = "v0"
cnf.StateDiskSizeGB = -1
return cnf
}(),
wantMsgCount: 2,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
msgs, err := tc.cnf.Validate()
require.NoError(err)
assert.Len(msgs, tc.wantMsgCount)
})
}
}
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))
}
func TestConfigRemoveProviderExcept(t *testing.T) {
testCases := map[string]struct {
removeExcept cloudprovider.Provider