diff --git a/cmd/edenConfig.go b/cmd/edenConfig.go index 88014793b..b1c05b3c9 100644 --- a/cmd/edenConfig.go +++ b/cmd/edenConfig.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "os" "github.com/lf-edge/eden/pkg/defaults" "github.com/lf-edge/eden/pkg/openevec" @@ -10,7 +11,11 @@ import ( ) func newConfigCmd(configName, verbosity *string) *cobra.Command { - cfg := &openevec.EdenSetupArgs{} + currentPath, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + cfg := openevec.GetDefaultConfig(currentPath) var configCmd = &cobra.Command{ Use: "config", Short: "work with config", @@ -73,13 +78,13 @@ func newConfigAddCmd(cfg *openevec.EdenSetupArgs) *cobra.Command { configAddCmd.Flags().StringVarP(&cfg.Eve.QemuFileToSave, "qemu-config", "", defaults.DefaultQemuFileToSave, "file to save config") configAddCmd.Flags().IntVarP(&cfg.Eve.QemuCpus, "cpus", "", defaults.DefaultCpus, "cpus") configAddCmd.Flags().IntVarP(&cfg.Eve.QemuMemory, "memory", "", defaults.DefaultMemory, "memory (MB)") - configAddCmd.Flags().StringSliceVarP(&cfg.Eve.QemuFirmware, "eve-firmware", "", nil, "firmware path") - configAddCmd.Flags().StringVarP(&cfg.Eve.QemuConfigPath, "config-part", "", "", "path for config drive") - configAddCmd.Flags().StringVarP(&cfg.Eve.QemuDTBPath, "dtb-part", "", "", "path for device tree drive (for arm)") + configAddCmd.Flags().StringSliceVarP(&cfg.Eve.QemuFirmware, "eve-firmware", "", cfg.Eve.QemuFirmware, "firmware path") + configAddCmd.Flags().StringVarP(&cfg.Eve.QemuConfigPath, "config-part", "", cfg.Eve.QemuConfigPath, "path for config drive") + configAddCmd.Flags().StringVarP(&cfg.Eve.QemuDTBPath, "dtb-part", "", cfg.Eve.QemuDTBPath, "path for device tree drive (for arm)") configAddCmd.Flags().StringToStringVarP(&cfg.Eve.HostFwd, "eve-hostfwd", "", defaults.DefaultQemuHostFwd, "port forward map") configAddCmd.Flags().StringVar(&cfg.Eve.Ssid, "ssid", "", "set ssid of wifi for rpi") - configAddCmd.Flags().StringVar(&cfg.Eve.Arch, "arch", "", "arch of EVE (amd64 or arm64)") - configAddCmd.Flags().StringVar(&cfg.Eve.ModelFile, "devmodel-file", "", "File to use for overwrite of model defaults") + configAddCmd.Flags().StringVar(&cfg.Eve.Arch, "arch", cfg.Eve.Arch, "arch of EVE (amd64 or arm64)") + configAddCmd.Flags().StringVar(&cfg.Eve.ModelFile, "devmodel-file", cfg.Eve.ModelFile, "File to use for overwrite of model defaults") configAddCmd.Flags().BoolVarP(&force, "force", "", false, "force overwrite config file") return configAddCmd diff --git a/cmd/edenSetup.go b/cmd/edenSetup.go index acb13fb2f..88e89f3e3 100644 --- a/cmd/edenSetup.go +++ b/cmd/edenSetup.go @@ -23,9 +23,9 @@ func newSetupCmd(configName, verbosity *string) *cobra.Command { Long: `Setup harness.`, PersistentPreRunE: preRunViperLoadFunction(cfg, configName, verbosity), Run: func(cmd *cobra.Command, args []string) { - if err := openevec.ConfigCheck(*configName); err != nil { - log.Fatalf("Config check failed %s", err) - } + // if err := openevec.ConfigCheck(*configName); err != nil { + // log.Fatalf("Config check failed %s", err) + // } if err := openEVEC.SetupEden(*configName, configDir, softSerial, zedControlURL, ipxeOverride, grubOptions, netboot, installer); err != nil { log.Fatalf("Setup eden failed: %s", err) diff --git a/pkg/defaults/templates.go b/pkg/defaults/templates.go index 4929ca16d..eb797f15b 100644 --- a/pkg/defaults/templates.go +++ b/pkg/defaults/templates.go @@ -1,6 +1,6 @@ package defaults -//DefaultEdenTemplate is configuration template for Eden +// DefaultEdenTemplate is configuration template for Eden const DefaultEdenTemplate = `#config is generated by eden adam: #tag on adam container to pull @@ -317,7 +317,7 @@ sdn: network-model: '{{parse "sdn.network-model"}}' ` -//DefaultQemuTemplate is configuration template for qemu +// DefaultQemuTemplate is configuration template for qemu const DefaultQemuTemplate = `#qemu config file generated by eden {{- if .Firmware }} {{ $firmwareLength := len .Firmware }}{{ if eq $firmwareLength 1 }} @@ -428,3 +428,152 @@ const ParallelsDiskTemplate = ` ` + +// DefaultEdenTemplate is configuration template for Eden +const DefaultActivateShTemplate = `# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +if [ "${BASH_SOURCE-}" = "$0" ]; then + echo "You must source this script: \$ source $0" >&2 + exit 33 +fi + +eden_deactivate () { + # reset old environment variables + # ! [ -z ${VAR+_} ] returns true if VAR is declared at all + if ! [ -z "${_OLD_EDEN_PATH:+_}" ] ; then + PATH="$_OLD_EDEN_PATH" + export PATH + unset _OLD_EDEN_PATH + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "${BASH-}" ] || [ -n "${ZSH_VERSION-}" ] ; then + hash -r 2>/dev/null + fi + + if ! [ -z "${_OLD_EDEN_PS1+_}" ] ; then + PS1="$_OLD_EDEN_PS1" + export PS1 + unset _OLD_EDEN_PS1 + fi + + unset EDEN_HOME + if [ ! "${1-}" = "nondestructive" ] ; then + # Self destruct! + unset -f eden_deactivate + unset -f eden_config + unset -f eden-config + unset -f eden+config + fi +} + +eden_config () { + if [ $# -eq 0 ] + then + echo Usage: eden_config config + return + fi + + eden config set $1 + PS1="EDEN-$(eden config get)_${_OLD_EDEN_PS1-}" +} + +eden+config () { + if [ $# -eq 0 ] + then + echo Usage: eden+config config + return + fi + + cd $(eden config get --key eden.root)/.. + eden config add $1 + cd - +} + +eden-config () { + if [ $# -eq 0 ] + then + echo Usage: eden-config config + return + fi + + eden config delete $1 + eden_config default +} + +# unset irrelevant variables +eden_deactivate nondestructive + +EDEN_HOME={{.Eden.Root}} +EDEN_BIN={{.Eden.BinDir}} +export EDEN_HOME + +_OLD_EDEN_PATH="$PATH" +PATH="$EDEN_BIN:$PATH" +export PATH + +if [ -z "${EDEN_HOME_DISABLE_PROMPT-}" ] ; then + _OLD_EDEN_PS1="${PS1-}" + PS1="EDEN-$(eden config get)_${PS1-}" + export PS1 +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "${BASH-}" ] || [ -n "${ZSH_VERSION-}" ] ; then + hash -r 2>/dev/null +fi +` + +const DefaultActivateCshTemplate = `# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. + +set newline='\ +' + +alias eden_deactivate 'test $?_OLD_EDEN_PATH != 0 && setenv PATH "$_OLD_EDEN_PATH:q" && unset _OLD_EDEN_PATH; rehash; test $?_OLD_EDEN_PROMPT != 0 && set prompt="$_OLD_EDEN_PROMPT:q" && unset _OLD_EDEN_PROMPT; unsetenv EDEN_HOME; test "\!:*" != "nondestructive" && unalias eden_deactivate && unalias eden_config && unalias eden+config && unalias eden-config' + +alias eden_config 'eden config set \!:1 && set prompt="EDEN-$(eden config get)_$_OLD_EDEN_PROMPT:q"' + +alias eden+config 'cd $(eden config get --key eden.root)/..; eden config add \!:1; cd -' +alias eden-config 'eden config delete \!:1; eden_config default' + +# Unset irrelevant variables. +eden_deactivate nondestructive + +setenv EDEN_HOME "{{.Eden.Root}}" + +set _OLD_EDEN_PATH="$PATH:q" +setenv PATH "{{.Eden.BinDir}}:$PATH:q" + +if ( $?EDEN_DISABLE_PROMPT ) then + if ( $EDEN_DISABLE_PROMPT == "" ) then + set do_prompt = "1" + else + set do_prompt = "0" + endif +else + set do_prompt = "1" +endif + +if ( $do_prompt == "1" ) then + # Could be in a non-interactive environment, + # in which case, $prompt is undefined and we wouldn't + # care about the prompt anyway. + if ( $?prompt ) then + set _OLD_EDEN_PROMPT="$prompt:q" + if ( "$prompt:q" =~ *"$newline:q"* ) then + : + else + set prompt = "eden-$(eden config get)_$prompt:q" + endif + endif +endif + +unset do_prompt + +rehash` diff --git a/pkg/eve/state.go b/pkg/eve/state.go index ba856b0f3..7f72f63f7 100644 --- a/pkg/eve/state.go +++ b/pkg/eve/state.go @@ -5,7 +5,7 @@ import ( "github.com/lf-edge/eden/pkg/controller/einfo" "github.com/lf-edge/eden/pkg/controller/emetric" "github.com/lf-edge/eden/pkg/device" - "github.com/lf-edge/eden/pkg/projects" + "github.com/lf-edge/eden/pkg/testcontext" "github.com/lf-edge/eve-api/go/info" "github.com/lf-edge/eve-api/go/metrics" log "github.com/sirupsen/logrus" @@ -22,13 +22,13 @@ type State struct { applications map[string]*AppInstState networks map[string]*NetInstState volumes map[string]*VolInstState - infoAndMetrics *projects.State + infoAndMetrics *testcontext.State device *device.Ctx } // Init State object with controller and device func Init(ctrl controller.Cloud, dev *device.Ctx) (ctx *State) { - ctx = &State{device: dev, infoAndMetrics: projects.InitState(dev)} + ctx = &State{device: dev, infoAndMetrics: testcontext.InitState(dev)} ctx.applications = make(map[string]*AppInstState) ctx.networks = make(map[string]*NetInstState) if err := ctx.initApplications(ctrl, dev); err != nil { @@ -44,7 +44,7 @@ func Init(ctrl controller.Cloud, dev *device.Ctx) (ctx *State) { } // InfoAndMetrics returns last info and metric objects -func (ctx *State) InfoAndMetrics() *projects.State { +func (ctx *State) InfoAndMetrics() *testcontext.State { return ctx.infoAndMetrics } diff --git a/pkg/evetestkit/utils.go b/pkg/evetestkit/utils.go index 64a530f10..fe378e78c 100644 --- a/pkg/evetestkit/utils.go +++ b/pkg/evetestkit/utils.go @@ -6,6 +6,7 @@ import ( "net" "os" "path" + "path/filepath" "strings" "testing" "time" @@ -13,11 +14,13 @@ import ( "github.com/docker/docker/pkg/namesgenerator" "github.com/dustin/go-humanize" "github.com/lf-edge/eden/pkg/controller" + "github.com/lf-edge/eden/pkg/controller/adam" "github.com/lf-edge/eden/pkg/defaults" "github.com/lf-edge/eden/pkg/device" + "github.com/lf-edge/eden/pkg/edensdn" "github.com/lf-edge/eden/pkg/eve" "github.com/lf-edge/eden/pkg/openevec" - "github.com/lf-edge/eden/pkg/projects" + "github.com/lf-edge/eden/pkg/testcontext" "github.com/lf-edge/eden/pkg/tests" "github.com/lf-edge/eden/pkg/utils" "github.com/tmc/scp" @@ -87,7 +90,7 @@ type EveNode struct { controller *openevec.OpenEVEC edgenode *device.Ctx cfg *openevec.EdenSetupArgs - tc *projects.TestContext + tc *testcontext.TestContext apps []appInstanceConfig ip string t *testing.T @@ -130,7 +133,7 @@ func getOpenEVEC() (*openevec.OpenEVEC, *openevec.EdenSetupArgs, error) { return openevec.CreateOpenEVEC(cfg), cfg, nil } -func createEveNode(node *device.Ctx, tc *projects.TestContext) (*EveNode, error) { +func createEveNode(node *device.Ctx, tc *testcontext.TestContext) (*EveNode, error) { evec, cfg, err := getOpenEVEC() if err != nil { return nil, fmt.Errorf("can't create OpenEVEC: %w", err) @@ -663,7 +666,7 @@ func GetRandomAppName(prefix string) string { func InitilizeTest(projectName string, options ...TestOption) (*EveNode, error) { var edgenode *device.Ctx tests.TestArgsParse() - tc := projects.NewTestContext() + tc := testcontext.NewTestContext() // Registering our own project namespace with controller for easy cleanup tc.InitProject(fmt.Sprintf("%s_%s", projectName, time.Now())) @@ -728,3 +731,121 @@ func InitilizeTest(projectName string, options ...TestOption) (*EveNode, error) return rnode, nil } + +func NewTestContextFromConfig(cfg *openevec.EdenSetupArgs) (*testcontext.TestContext, error) { + var ( + err error + sdnClient *edensdn.SdnClient + withSdn bool + ) + + devModel := cfg.Eve.DevModel + eveRemote := cfg.Eve.Remote + withSdn = !cfg.Sdn.Disable && + devModel == defaults.DefaultQemuModel && + !eveRemote + if withSdn { + sdnSSHPort := cfg.Sdn.SSHPort + sdnMgmtPort := cfg.Sdn.MgmtPort + sdnSourceDir := filepath.Join(cfg.Eden.Root, strings.TrimSpace(cfg.Sdn.SourceDir)) + sdnSSHKeyPath := filepath.Join(sdnSourceDir, "vm/cert/ssh/id_rsa") + sdnClient = &edensdn.SdnClient{ + SSHPort: uint16(sdnSSHPort), + SSHKeyPath: sdnSSHKeyPath, + MgmtPort: uint16(sdnMgmtPort), + } + } + + vars, err := openevec.InitVarsFromConfig(cfg) + if err != nil { + return nil, err + } + ctx := &controller.CloudCtx{Controller: &adam.Ctx{}} + ctx.SetVars(vars) + if err := ctx.InitWithVars(vars); err != nil { + return nil, err + } + ctx.GetAllNodes() + tstCtx := &testcontext.TestContext{ + Cloud: ctx, + Tests: map[*device.Ctx]*testing.T{}, + SdnClient: sdnClient, + WithSdn: withSdn, + } + tstCtx.ProcBus = testcontext.InitBus(tstCtx) + return tstCtx, nil + +} + +func InitilizeTestFromConfig(projectName string, cfg *openevec.EdenSetupArgs, options ...TestOption) (*EveNode, error) { + var edgenode *device.Ctx + tc, err := NewTestContextFromConfig(cfg) + if err != nil { + return nil, err + } + + // Registering our own project namespace with controller for easy cleanup + tc.InitProject(fmt.Sprintf("%s_%s", projectName, time.Now())) + + // Create representation of EVE instances (based on the names + // or UUIDs that were passed in) in the context. This is the first place + // where we're using zcli-like API: + for _, node := range tc.GetNodeDescriptions() { + edgeNode := node.GetEdgeNode(tc) + if edgeNode == nil { + // Couldn't find existing edgeNode record in the controller. + // Need to create it from scratch now: + // this is modeled after: zcli edge-node create + // --project= --model= [--title=] + // ([--edge-node-certificate=<certificate>] | + // [--onboarding-certificate=<certificate>] | + // [(--onboarding-key=<key> --serial=<serial-number>)]) + // [--network=<network>...] + // + // XXX: not sure if struct (giving us optional fields) would be better + edgeNode = tc.NewEdgeNode(tc.WithNodeDescription(node), tc.WithCurrentProject()) + } else { + // make sure to move EdgeNode to the project we created, again + // this is modeled after zcli edge-node update <name> [--title=<title>] + // [--lisp-mode=experimental|default] [--project=<project>] + // [--clear-onboarding-certs] [--config=<key:value>...] [--network=<network>...] + edgeNode.SetProject(projectName) + } + + edgenode = edgeNode + tc.ConfigSync(edgeNode) + + // finally we need to make sure that the edgeNode is in a state that we need + // it to be, before the test can run -- this could be multiple checks on its + // status, but for example: + if edgeNode.GetState() == device.NotOnboarded { + return nil, fmt.Errorf("node is not onboarded now") + } + + // this is a good node -- lets add it to the test context + tc.AddNode(edgeNode) + } + + tc.StartTrackingState(false) + + // apply options + for _, option := range options { + option() + } + + rnode := &EveNode{ + controller: openevec.CreateOpenEVEC(cfg), + edgenode: edgenode, + tc: tc, + apps: []appInstanceConfig{}, + cfg: cfg, + } + + // get the IP address of the EVE node + err = rnode.discoverEveIP() + if err != nil { + return nil, fmt.Errorf("can't get the IP address of the EVE node: %w", err) + } + + return rnode, nil +} diff --git a/pkg/openevec/changers.go b/pkg/openevec/changers.go index 9bca3ac98..bc4b85728 100644 --- a/pkg/openevec/changers.go +++ b/pkg/openevec/changers.go @@ -7,8 +7,9 @@ import ( "os" "github.com/lf-edge/eden/pkg/controller" + "github.com/lf-edge/eden/pkg/defaults" "github.com/lf-edge/eden/pkg/device" - "github.com/lf-edge/eden/pkg/projects" + "github.com/lf-edge/eden/pkg/utils" "github.com/lf-edge/eve-api/go/config" log "github.com/sirupsen/logrus" ) @@ -28,7 +29,7 @@ func changerByControllerMode(controllerMode string) (configChanger, error) { if controllerMode == "" { return &adamChanger{}, nil } - modeType, modeURL, err := projects.GetControllerMode(controllerMode) + modeType, modeURL, err := utils.GetControllerMode(controllerMode, defaults.DefaultControllerModePattern) if err != nil { return nil, err } diff --git a/pkg/openevec/config.go b/pkg/openevec/config.go index 5a8f0194b..5fe0a9134 100644 --- a/pkg/openevec/config.go +++ b/pkg/openevec/config.go @@ -2,6 +2,7 @@ package openevec import ( "fmt" + "io" "os" "path" "path/filepath" @@ -21,6 +22,12 @@ type EServerConfig struct { Tag string `mapstructure:"tag" cobraflag:"eserver-tag"` IP string `mapstructure:"ip"` Images ImagesConfig `mapstructure:"images"` + EVEIP string `mapstructure:"eve-ip"` +} + +type EClientConfig struct { + Tag string `mapstructure:"tag"` + Image string `mapstructure:"image"` } type ImagesConfig struct { @@ -37,10 +44,12 @@ type EdenConfig struct { EdenBin string `mapstructure:"eden-bin"` TestBin string `mapstructure:"test-bin"` TestScenario string `mapstructure:"test-scenario"` + Tests string `mapstructure:"tests" resolvepath:""` EServer EServerConfig `mapstructure:"eserver"` - Images ImagesConfig `mapstructure:"images"` + EClient EClientConfig `mapstructure:"eclient"` + Images ImagesConfig `mapstructure:"images"` } type RedisConfig struct { @@ -73,6 +82,7 @@ type AdamConfig struct { CertsEVEIP string `mapstructure:"eve-ip" cobraflag:"eve-ip"` APIv1 bool `mapstructure:"v1" cobrafalg:"force"` Force bool `mapstructure:"force" cobraflag:"force"` + CA string `mapstructure:"ca"` Redis RedisConfig `mapstructure:"redis"` Remote RemoteConfig `mapstructure:"remote"` @@ -94,7 +104,7 @@ type EveConfig struct { QemuConfig QemuConfig `mapstructure:"qemu"` QemuFirmware []string `mapstructure:"firmware" cobraflag:"eve-firmware"` - QemuConfigPath string `mapstructure:"config-part" cobraflag:"config-path" resolvepath:""` + QemuConfigPath string `mapstructure:"config-part" cobraflag:"config-path"` QemuDTBPath string `mapstructure:"dtb-part" cobraflag:"dtb-part" resolvepath:""` QemuOS string `mapstructure:"os" cobraflag:"eve-os"` ImageFile string `mapstructure:"image-file" cobraflag:"image-file" resolvepath:""` @@ -113,7 +123,6 @@ type EveConfig struct { QemuMemory int `mapstructure:"ram" cobraflag:"memory"` ImageSizeMB int `mapstructure:"disk" cobraflag:"image-size"` DevModel string `mapstructure:"devmodel" cobraflag:"devmodel"` - DevModelFile string `mapstructure:"devmodelfile"` Ssid string `mapstructure:"ssid" cobraflag:"ssid"` Password string `mapstructure:"password" cobraflag:"password"` Serial string `mapstructure:"serial" cobraflag:"eve-serial"` @@ -152,7 +161,7 @@ type GcpConfig struct { } type SdnConfig struct { - ImageFile string `mapstructure:"image-file" cobraflag:"sdn-image-file" resolvepath:""` + ImageFile string `mapstructure:"image-file" cobraflag:"sdn-image-file"` SourceDir string `mapstructure:"source-dir" cobraflag:"sdn-source-dir" resolvepath:""` RAM int `mapstructure:"ram" cobraflag:"sdn-ram"` CPU int `mapstructure:"cpu" cobraflag:"sdn-cpu"` @@ -179,6 +188,7 @@ type EdenSetupArgs struct { ConfigFile string ConfigName string + EdenDir string } // PodConfig store configuration for Pod deployment @@ -284,7 +294,7 @@ func LoadConfig(configFile string) (*EdenSetupArgs, error) { return nil, fmt.Errorf("unable to decode into config struct, %w", err) } - resolvePath(reflect.ValueOf(cfg).Elem()) + resolvePath(cfg.Eden.Root, reflect.ValueOf(cfg).Elem()) if configFile == "" { configFile, _ = utils.DefaultConfigPath() @@ -300,17 +310,17 @@ func LoadConfig(configFile string) (*EdenSetupArgs, error) { return cfg, nil } -func resolvePath(v reflect.Value) { +func resolvePath(path string, v reflect.Value) { for i := 0; i < v.NumField(); i++ { f := v.Field(i) if _, ok := v.Type().Field(i).Tag.Lookup("resolvepath"); ok { if f.IsValid() && f.CanSet() && f.Kind() == reflect.String { val := f.Interface().(string) - f.SetString(utils.ResolveAbsPath(val)) + f.SetString(utils.ResolveAbsPathWithRoot(path, val)) } } if f.Kind() == reflect.Struct { - resolvePath(f) + resolvePath(path, f) } } } @@ -357,3 +367,78 @@ func ConfigCheck(configName string) error { } return nil } + +func getValStrRepr(v reflect.Value) string { + if v.Kind() == reflect.String { + return fmt.Sprintf("'%v'", v.Interface()) + } else { + return fmt.Sprintf("%v", v.Interface()) + } +} + +func WriteConfig(dst reflect.Value, writer io.Writer, nestLevel int) { + for i := 0; i < dst.NumField(); i++ { + if structTag := dst.Type().Field(i).Tag.Get("mapstructure"); structTag != "" { + io.WriteString(writer, strings.Repeat(" ", nestLevel)) + switch dst.Field(i).Kind() { + case reflect.Struct: + io.WriteString(writer, structTag+":\n") + WriteConfig(dst.Field(i), writer, nestLevel+1) + case reflect.Map: + io.WriteString(writer, structTag+":\n") + iter := dst.Field(i).MapRange() + for iter.Next() { + k := iter.Key() + v := iter.Value() + io.WriteString(writer, strings.Repeat(" ", nestLevel+1)) + // We assume that map cannot have structure as value + io.WriteString(writer, fmt.Sprintf("%v: %s\n", k.Interface(), getValStrRepr(v))) + } + case reflect.Slice: + io.WriteString(writer, structTag+":\n") + for j := 0; j < dst.Field(i).Len(); j++ { + io.WriteString(writer, strings.Repeat(" ", nestLevel+1)) + elem := dst.Field(i).Index(j) + io.WriteString(writer, fmt.Sprintf("- %v\n", getValStrRepr(elem))) + } + case reflect.String: // we need to wrap string in quotes + io.WriteString(writer, fmt.Sprintf("%s: '%v'\n", structTag, dst.Field(i))) + default: + io.WriteString(writer, fmt.Sprintf("%s: %v\n", structTag, dst.Field(i))) + } + } + } +} + +func PrintDifferences(a, b interface{}, parentField string) { + valA := reflect.ValueOf(a) + valB := reflect.ValueOf(b) + + if valA.Kind() != reflect.Struct || valB.Kind() != reflect.Struct { + if valA.Interface() != valB.Interface() { + fmt.Printf("Field %s differs: %v vs %v\n", parentField, valA.Interface(), valB.Interface()) + } + return + } + + typeA := valA.Type() + + for i := 0; i < valA.NumField(); i++ { + fieldName := typeA.Field(i).Name + fieldValA := valA.Field(i) + fieldValB := valB.Field(i) + + fullFieldName := fieldName + if parentField != "" { + fullFieldName = parentField + "." + fieldName + } + + if fieldValA.Kind() == reflect.Struct { + PrintDifferences(fieldValA.Interface(), fieldValB.Interface(), fullFieldName) + } else { + if fieldValA.Interface() != fieldValB.Interface() { + fmt.Printf("Field %s differs: %v vs %v \n", fullFieldName, fieldValA.Interface(), fieldValB.Interface()) + } + } + } +} diff --git a/pkg/openevec/config_test.go b/pkg/openevec/config_test.go new file mode 100644 index 000000000..16baa76bc --- /dev/null +++ b/pkg/openevec/config_test.go @@ -0,0 +1,97 @@ +package openevec_test + +import ( + "bytes" + "fmt" + "reflect" + "testing" + + "github.com/lf-edge/eden/pkg/openevec" + . "github.com/onsi/gomega" + "github.com/spf13/viper" +) + +type NestedConfig struct { + NumField int `mapstructure:"numfield"` +} + +type ServerConfig struct { + Field string `mapstructure:"field"` + Access int `mapstructure:"access"` + HostFwd map[string]string `mapstructure:"hostfwd"` + + NestedField NestedConfig `mapstructure:"nested"` +} + +type Config struct { + Names []string `mapstructure:"names"` + IsSpecial bool `mapstructure:"special"` + + Server ServerConfig `mapstructure:"server"` +} + +func TestViperSerializeFromWriteConfig(t *testing.T) { + t.Parallel() + g := NewGomegaWithT(t) + + cfg := Config{ + Names: []string{"test1", "test2"}, + IsSpecial: false, + + Server: ServerConfig{ + Field: "ServerField", + Access: 42, + + HostFwd: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + + NestedField: NestedConfig{ + NumField: 21, + }, + }, + } + + var buf bytes.Buffer + openevec.WriteConfig(reflect.ValueOf(cfg), &buf, 0) + + v := viper.New() + v.SetConfigType("yaml") + err := v.ReadConfig(&buf) + if err != nil { + t.Errorf("error reading config: %v", err) + return + } + + // Unmarshal the configuration into the Config struct. + gotCfg := &Config{} + err = v.Unmarshal(&gotCfg) + + g.Expect(*gotCfg).To(BeEquivalentTo(cfg)) +} + +func TestConfigSliceType(t *testing.T) { + t.Parallel() + g := NewGomegaWithT(t) + + cfg := Config{ + Names: []string{"test1", "test2"}, + } + + var buf bytes.Buffer + openevec.WriteConfig(reflect.ValueOf(cfg), &buf, 0) + + v := viper.New() + v.SetConfigType("yaml") + err := v.ReadConfig(&buf) + if err != nil { + fmt.Println("error reading config:", err) + return + } + + gotCfg := &Config{} + err = v.Unmarshal(&gotCfg) + + g.Expect(reflect.TypeOf(cfg.Names[0]).Kind()).To(BeEquivalentTo(reflect.String)) +} diff --git a/pkg/openevec/defaults.go b/pkg/openevec/defaults.go index 928841da8..063b1138c 100644 --- a/pkg/openevec/defaults.go +++ b/pkg/openevec/defaults.go @@ -1,97 +1,221 @@ package openevec import ( - "log" - "os" + "fmt" "path/filepath" "runtime" + "strconv" + "strings" + "github.com/dustin/go-humanize" "github.com/lf-edge/eden/pkg/defaults" "github.com/lf-edge/eden/pkg/utils" + uuid "github.com/satori/go.uuid" ) -func GetDefaultConfig() *EdenSetupArgs { +func GetDefaultConfig(currentPath string) *EdenSetupArgs { + ip, err := utils.GetIPForDockerAccess() + if err != nil { + return nil + } + + edenDir, err := utils.DefaultEdenDir() + if err != nil { + return nil + } - currentPath, err := os.Getwd() + id, err := uuid.NewV4() if err != nil { - log.Fatal(err) + return nil + } + + imageDist := filepath.Join(currentPath, defaults.DefaultDist, fmt.Sprintf("%s-%s", defaults.DefaultContext, defaults.DefaultImageDist)) + certsDist := filepath.Join(currentPath, defaults.DefaultDist, fmt.Sprintf("%s-%s", defaults.DefaultContext, defaults.DefaultCertsDist)) + + firmware := []string{filepath.Join(imageDist, "eve", "OVMF.fd")} + if runtime.GOARCH == "amd64" { + firmware = []string{ + filepath.Join(imageDist, "eve", "firmware", "OVMF_CODE.fd"), + filepath.Join(imageDist, "eve", "firmware", "OVMF_VARS.fd")} } defaultEdenConfig := &EdenSetupArgs{ Eden: EdenConfig{ - Download: true, - BinDir: filepath.Join(currentPath, defaults.DefaultDist, defaults.DefaultBinDist), - SSHKey: filepath.Join(currentPath, defaults.DefaultCertsDist, "id_rsa"), + Root: filepath.Join(currentPath, defaults.DefaultDist), + Tests: filepath.Join(currentPath, defaults.DefaultDist, "tests"), + Download: true, + BinDir: defaults.DefaultBinDist, + SSHKey: filepath.Join(currentPath, defaults.DefaultDist, fmt.Sprintf("%s-%s", defaults.DefaultContext, defaults.DefaultSSHKey)), + CertsDir: certsDist, + TestBin: defaults.DefaultTestProg, + EdenBin: "eden", + TestScenario: defaults.DefaultTestScenario, + + Images: ImagesConfig{ + EServerImageDist: defaults.DefaultEserverDist, + }, EServer: EServerConfig{ + IP: ip, + EVEIP: defaults.DefaultDomain, + Port: defaults.DefaultEserverPort, - Force: false, + Force: true, Tag: defaults.DefaultEServerTag, }, + + EClient: EClientConfig{ + Tag: defaults.DefaultEClientTag, + Image: defaults.DefaultEClientContainerRef, + }, }, Adam: AdamConfig{ - Tag: defaults.DefaultAdamTag, - Port: defaults.DefaultAdamPort, - CertsIP: defaults.DefaultIP, - CertsEVEIP: defaults.DefaultEVEIP, + Tag: defaults.DefaultAdamTag, + Port: defaults.DefaultAdamPort, + Dist: defaults.DefaultAdamDist, + CertsDomain: defaults.DefaultDomain, + CertsIP: ip, + CertsEVEIP: ip, + Force: true, + CA: filepath.Join(fmt.Sprintf("%s-%s", defaults.DefaultContext, defaults.DefaultCertsDist), "root-certificate.pem"), + APIv1: false, Redis: RedisConfig{ - Tag: defaults.DefaultRedisTag, - Port: defaults.DefaultRedisPort, + RemoteURL: fmt.Sprintf("%s:%d", defaults.DefaultRedisContainerName, defaults.DefaultRedisPort), + Tag: defaults.DefaultRedisTag, + Port: defaults.DefaultRedisPort, + Eden: fmt.Sprintf("%s:%d", ip, defaults.DefaultRedisPort), + }, + + Remote: RemoteConfig{ + Enabled: true, + Redis: true, + }, + + Caching: CachingConfig{ + Enabled: false, + Redis: false, + Prefix: "cache", }, }, Eve: EveConfig{ - QemuConfig: QemuConfig{ - MonitorPort: defaults.DefaultQemuMonitorPort, - NetDevSocketPort: defaults.DefaultQemuNetdevSocketPort, - }, - CertsUUID: defaults.DefaultUUID, - Dist: filepath.Join(currentPath, defaults.DefaultDist, defaults.DefaultEVEDist), - Repo: defaults.DefaultEveRepo, - Registry: defaults.DefaultEveRegistry, - Tag: defaults.DefaultEVETag, - UefiTag: defaults.DefaultEVETag, - HV: defaults.DefaultEVEHV, - Arch: runtime.GOARCH, - HostFwd: defaults.DefaultQemuHostFwd, - QemuFileToSave: defaults.DefaultQemuFileToSave, + Name: strings.ToLower(defaults.DefaultContext), + DevModel: defaults.DefaultQemuModel, + ModelFile: "", + Arch: runtime.GOARCH, + QemuOS: runtime.GOOS, + Accel: true, + HV: defaults.DefaultEVEHV, + CertsUUID: id.String(), + Cert: filepath.Join(certsDist, "onboard.cert.pem"), + DeviceCert: filepath.Join(certsDist, "device.cert.pem"), + QemuFirmware: firmware, + Dist: fmt.Sprintf("%s-%s", defaults.DefaultContext, defaults.DefaultEVEDist), + Repo: defaults.DefaultEveRepo, + Registry: defaults.DefaultEveRegistry, + Tag: defaults.DefaultEVETag, + UefiTag: defaults.DefaultEVETag, + HostFwd: map[string]string{ + strconv.Itoa(defaults.DefaultSSHPort): "22", + "5911": "5901", + "5912": "5902", + "8027": "8027", + "8028": "8028"}, + QemuFileToSave: filepath.Join(edenDir, fmt.Sprintf("%s-%s", defaults.DefaultContext, defaults.DefaultQemuFileToSave)), QemuCpus: defaults.DefaultCpus, QemuMemory: defaults.DefaultMemory, ImageSizeMB: defaults.DefaultEVEImageSize, - DevModel: defaults.DefaultQemuModel, Serial: defaults.DefaultEVESerial, - Pid: filepath.Join(currentPath, defaults.DefaultDist), - Log: filepath.Join(currentPath, defaults.DefaultDist), + Pid: filepath.Join(currentPath, defaults.DefaultDist, fmt.Sprintf("%s-eve.pid", strings.ToLower(defaults.DefaultContext))), + Log: filepath.Join(currentPath, defaults.DefaultDist, fmt.Sprintf("%s-eve.log", strings.ToLower(defaults.DefaultContext))), TelnetPort: defaults.DefaultTelnetPort, TPM: defaults.DefaultTPMEnabled, + ImageFile: filepath.Join(imageDist, "eve", "live.img"), + QemuDTBPath: "", + QemuConfigPath: certsDist, + Remote: defaults.DefaultEVERemote, + RemoteAddr: defaults.DefaultEVEHost, + LogLevel: defaults.DefaultEveLogLevel, + AdamLogLevel: defaults.DefaultAdamLogLevel, + Ssid: "", + Disks: defaults.DefaultAdditionalDisks, + BootstrapFile: "", + UsbNetConfFile: "", + Platform: "none", + + CustomInstaller: CustomInstallerConfig{ + Path: "", + Format: "", + }, + + QemuConfig: QemuConfig{ + MonitorPort: defaults.DefaultQemuMonitorPort, + NetDevSocketPort: defaults.DefaultQemuNetdevSocketPort, + }, }, Redis: RedisConfig{ Tag: defaults.DefaultRedisTag, Port: defaults.DefaultRedisPort, + Dist: defaults.DefaultRedisDist, }, Registry: RegistryConfig{ Tag: defaults.DefaultRegistryTag, Port: defaults.DefaultRegistryPort, + IP: ip, + Dist: defaults.DefaultRegistryDist, }, Sdn: SdnConfig{ RAM: defaults.DefaultSdnMemory, CPU: defaults.DefaultSdnCpus, ConsoleLogFile: filepath.Join(currentPath, defaults.DefaultDist, "sdn-console.log"), - Disable: false, + Disable: true, TelnetPort: defaults.DefaultSdnTelnetPort, MgmtPort: defaults.DefaultSdnMgmtPort, PidFile: filepath.Join(currentPath, defaults.DefaultDist, "sdn.pid"), SSHPort: defaults.DefaultSdnSSHPort, + SourceDir: filepath.Join(currentPath, "sdn"), + ConfigDir: filepath.Join(edenDir, fmt.Sprintf("%s-sdn", "default")), + ImageFile: filepath.Join(imageDist, "eden", "sdn-efi.qcow2"), + LinuxkitBin: filepath.Join(currentPath, defaults.DefaultBuildtoolsDir, "linuxkit"), + NetModelFile: "", + }, + + Gcp: GcpConfig{ + Key: "", + }, + + Packet: PacketConfig{ + Key: "", }, ConfigName: defaults.DefaultContext, ConfigFile: utils.GetConfig(defaults.DefaultContext), + EdenDir: edenDir, } return defaultEdenConfig } + +func GetDefaultPodConfig() *PodConfig { + dpc := &PodConfig{ + AppMemory: humanize.Bytes(defaults.DefaultAppMem * 1024), + DiskSize: humanize.Bytes(0), + VolumeType: "qcow2", + AppCpus: defaults.DefaultAppCPU, + ACLOnlyHost: false, + NoHyper: false, + Registry: "remote", + DirectLoad: true, + SftpLoad: false, + VolumeSize: humanize.IBytes(defaults.DefaultVolumeSize), + OpenStackMetadata: false, + PinCpus: false, + } + + return dpc +} diff --git a/pkg/openevec/eden.go b/pkg/openevec/eden.go index b9a1d9965..c9a18a436 100644 --- a/pkg/openevec/eden.go +++ b/pkg/openevec/eden.go @@ -7,7 +7,6 @@ import ( "fmt" "html/template" "io" - "io/ioutil" "os" "os/exec" "path" @@ -337,14 +336,16 @@ func setupEdenScripts(cfg EdenSetupArgs) error { fmt.Printf("Directory %s access error: %s\n", cfgDir, err) } else { - shPath := cfg.Eden.Root + "/scripts/shell/" - activateShFile, err := os.Create(cfgDir + "activate.sh") defer activateShFile.Close() if err != nil { return err } - if err = ParseTemplateFile(shPath+"activate.sh.tmpl", cfg, activateShFile); err != nil { + tmpl, err := template.New("").Parse(defaults.DefaultActivateShTemplate) + if err != nil { + return err + } + if err = tmpl.Execute(activateShFile, cfg); err != nil { return err } @@ -353,7 +354,11 @@ func setupEdenScripts(cfg EdenSetupArgs) error { if err != nil { return err } - if err = ParseTemplateFile(shPath+"activate.csh.tmpl", cfg, activateCshFile); err != nil { + tmpl, err = template.New("").Parse(defaults.DefaultActivateCshTemplate) + if err != nil { + return err + } + if err = tmpl.Execute(activateShFile, cfg); err != nil { return err } @@ -366,29 +371,24 @@ func setupEdenScripts(cfg EdenSetupArgs) error { } func setupConfigDir(cfg EdenSetupArgs, eveConfigDir, softSerial, zedControlURL string, grubOptions []string) error { - if _, err := os.Stat(filepath.Join(cfg.Eden.CertsDir, "root-certificate.pem")); os.IsNotExist(err) { - wifiPSK := "" - if cfg.Eve.Ssid != "" { - fmt.Printf("Enter password for wifi %s: ", cfg.Eve.Ssid) - pass, _ := term.ReadPassword(0) - wifiPSK = strings.ToLower(hex.EncodeToString(pbkdf2.Key(pass, []byte(cfg.Eve.Ssid), 4096, 32, sha1.New))) - fmt.Println() - } - if zedControlURL == "" { - if err := eden.GenerateEveCerts(cfg.Eden.CertsDir, cfg.Adam.CertsDomain, cfg.Adam.CertsIP, cfg.Adam.CertsEVEIP, cfg.Eve.CertsUUID, - cfg.Eve.DevModel, cfg.Eve.Ssid, cfg.Eve.Arch, wifiPSK, grubOptions, cfg.Adam.APIv1); err != nil { - return fmt.Errorf("cannot GenerateEveCerts: %w", err) - } - log.Info("GenerateEveCerts done") - } else { - if err := eden.PutEveCerts(cfg.Eden.CertsDir, cfg.Eve.DevModel, cfg.Eve.Ssid, cfg.Eve.Arch, wifiPSK); err != nil { - return fmt.Errorf("cannot GenerateEveCerts: %w", err) - } - log.Info("GenerateEveCerts done") + wifiPSK := "" + if cfg.Eve.Ssid != "" { + fmt.Printf("Enter password for wifi %s: ", cfg.Eve.Ssid) + pass, _ := term.ReadPassword(0) + wifiPSK = strings.ToLower(hex.EncodeToString(pbkdf2.Key(pass, []byte(cfg.Eve.Ssid), 4096, 32, sha1.New))) + fmt.Println() + } + if zedControlURL == "" { + if err := eden.GenerateEveCerts(cfg.Eden.CertsDir, cfg.Adam.CertsDomain, cfg.Adam.CertsIP, cfg.Adam.CertsEVEIP, cfg.Eve.CertsUUID, + cfg.Eve.DevModel, cfg.Eve.Ssid, cfg.Eve.Arch, wifiPSK, grubOptions, cfg.Adam.APIv1); err != nil { + return fmt.Errorf("cannot GenerateEveCerts: %w", err) } + log.Info("GenerateEveCerts done") } else { + if err := eden.PutEveCerts(cfg.Eden.CertsDir, cfg.Eve.DevModel, cfg.Eve.Ssid, cfg.Eve.Arch, wifiPSK); err != nil { + return fmt.Errorf("cannot GenerateEveCerts: %w", err) + } log.Info("GenerateEveCerts done") - log.Infof("Certs already exists in certs dir: %s", cfg.Eden.CertsDir) } if zedControlURL == "" { err := eden.GenerateEVEConfig(cfg.Eve.DevModel, cfg.Eden.CertsDir, cfg.Adam.CertsDomain, cfg.Adam.CertsEVEIP, @@ -829,7 +829,7 @@ func (openEVEC *OpenEVEC) EdenImport(tarFile string, rewriteRoot bool) error { // ParseTemplateFile fills EdenSetupArgs variable into template stored in file and writes result to io.Writer func ParseTemplateFile(path string, cfg EdenSetupArgs, w io.Writer) error { - t, err := ioutil.ReadFile(path) + t, err := os.ReadFile(path) if err != nil { return err } diff --git a/pkg/openevec/edenConfig.go b/pkg/openevec/edenConfig.go index a6fac3167..35bb49b73 100644 --- a/pkg/openevec/edenConfig.go +++ b/pkg/openevec/edenConfig.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "reflect" "github.com/lf-edge/eden/pkg/defaults" "github.com/lf-edge/eden/pkg/models" @@ -75,15 +76,26 @@ func ConfigAdd(cfg *EdenSetupArgs, currentContext, contextFile string, force boo log.Debugf("current config already exists: %s", cfg.ConfigFile) } } - if _, err = os.Stat(cfg.ConfigFile); os.IsNotExist(err) { - if err = utils.GenerateConfigFile(cfg.ConfigFile); err != nil { - return fmt.Errorf("fail in generate yaml: %w", err) - } - log.Infof("Config file generated: %s", cfg.ConfigFile) - } - if err := ReloadConfigDetails(cfg); err != nil { - return err - } + // if _, err = os.Stat(cfg.ConfigFile); os.IsNotExist(err) { + + // dir := filepath.Dir(cfg.ConfigFile) + // if err = os.MkdirAll(dir, os.ModePerm); err != nil { + // return fmt.Errorf("Error creating folders %v", err) + // } + + // file, err := os.Create(cfg.ConfigFile) + // if err != nil { + // return fmt.Errorf("Error creating file 111 %v", err) + // } + // defer file.Close() + + // WriteConfig(reflect.ValueOf(*cfg), file, 0) + + // log.Infof("Config file generated: %s", cfg.ConfigFile) + // } + // if err := ReloadConfigDetails(cfg); err != nil { + // return err + // } context, err := utils.ContextLoad() if err != nil { @@ -112,9 +124,9 @@ func ConfigAdd(cfg *EdenSetupArgs, currentContext, contextFile string, force boo } } context.SetContext(context.Current) - if err := ReloadConfigDetails(cfg); err != nil { - return err - } + // if err := ReloadConfigDetails(cfg); err != nil { + // return err + // } // we prepare viper config here from EdenSetupArgs // to feed into GenerateConfigFileFromViper @@ -138,9 +150,22 @@ func ConfigAdd(cfg *EdenSetupArgs, currentContext, contextFile string, force boo viper.Set(k, v) } - if err = utils.GenerateConfigFileFromViper(); err != nil { - return fmt.Errorf("error writing config: %w", err) + dir := filepath.Dir(cfg.ConfigFile) + if err = os.MkdirAll(dir, os.ModePerm); err != nil { + return fmt.Errorf("Error creating folders %v", err) } + + file, err := os.Create(cfg.ConfigFile) + if err != nil { + return fmt.Errorf("Error creating file 111 %v", err) + } + defer file.Close() + + WriteConfig(reflect.ValueOf(*cfg), file, 0) + + // if err = utils.GenerateConfigFileFromViper(); err != nil { + // return fmt.Errorf("error writing config: %w", err) + // } context.SetContext(currentContextName) return nil diff --git a/pkg/openevec/test.go b/pkg/openevec/test.go index 62958e6ec..224dc7a09 100644 --- a/pkg/openevec/test.go +++ b/pkg/openevec/test.go @@ -44,7 +44,7 @@ func InitVarsFromConfig(cfg *EdenSetupArgs) (*utils.ConfigVars, error) { cv.AdamIP = cfg.Adam.CertsIP cv.AdamPort = strconv.Itoa(cfg.Adam.Port) cv.AdamDomain = cfg.Adam.CertsDomain - cv.AdamDir = utils.ResolveAbsPath(cfg.Adam.Dist) + cv.AdamDir = utils.ResolveAbsPathWithRoot(cfg.Eden.Root, cfg.Adam.Dist) cv.AdamCA = caCertPath cv.AdamRedisURLEden = cfg.Adam.Redis.RemoteURL cv.AdamRemote = cfg.Adam.Remote.Enabled @@ -53,17 +53,17 @@ func InitVarsFromConfig(cfg *EdenSetupArgs) (*utils.ConfigVars, error) { cv.AdamCachingPrefix = cfg.Adam.Caching.Prefix cv.AdamCachingRedis = cfg.Adam.Caching.Redis - cv.SSHKey = utils.ResolveAbsPath(cfg.Eden.SSHKey) + cv.SSHKey = cfg.Eden.SSHKey cv.EdenBinDir = cfg.Eden.BinDir cv.EdenProg = cfg.Eden.EdenBin cv.TestProg = cfg.Eden.TestBin cv.TestScenario = cfg.Eden.TestScenario - cv.EServerImageDist = utils.ResolveAbsPath(cfg.Eden.Images.EServerImageDist) + cv.EServerImageDist = utils.ResolveAbsPathWithRoot(cfg.Eden.Root, cfg.Eden.Images.EServerImageDist) cv.EServerPort = strconv.Itoa(cfg.Eden.EServer.Port) cv.EServerIP = cfg.Eden.EServer.IP - cv.EveCert = utils.ResolveAbsPath(cfg.Eve.Cert) - cv.EveDeviceCert = utils.ResolveAbsPath(cfg.Eve.DeviceCert) + cv.EveCert = utils.ResolveAbsPathWithRoot(cfg.Eden.Root, cfg.Eve.Cert) + cv.EveDeviceCert = utils.ResolveAbsPathWithRoot(cfg.Eden.Root, cfg.Eve.DeviceCert) cv.EveSerial = cfg.Eve.Serial cv.EveDist = cfg.Eve.Dist cv.EveQemuConfig = cfg.Eve.QemuFileToSave @@ -71,7 +71,7 @@ func InitVarsFromConfig(cfg *EdenSetupArgs) (*utils.ConfigVars, error) { cv.EveSSID = cfg.Eve.Ssid cv.EveHV = cfg.Eve.HV cv.DevModel = cfg.Eve.DevModel - cv.DevModelFIle = cfg.Eve.DevModelFile + cv.DevModelFIle = cfg.Eve.ModelFile cv.EveName = cfg.Eve.Name cv.EveUUID = cfg.Eve.CertsUUID cv.AdamLogLevel = cfg.Eve.AdamLogLevel diff --git a/pkg/projects/project.go b/pkg/projects/project.go deleted file mode 100644 index 5c64bf04f..000000000 --- a/pkg/projects/project.go +++ /dev/null @@ -1,6 +0,0 @@ -package projects - -//Project structure for test set -type Project struct { - name string -} diff --git a/pkg/projects/edgeNodeDescription.go b/pkg/testcontext/edgeNodeDescription.go similarity index 73% rename from pkg/projects/edgeNodeDescription.go rename to pkg/testcontext/edgeNodeDescription.go index 8fb49fd14..d78d9b74f 100644 --- a/pkg/projects/edgeNodeDescription.go +++ b/pkg/testcontext/edgeNodeDescription.go @@ -1,15 +1,15 @@ -package projects +package testcontext import "github.com/lf-edge/eden/pkg/device" -//EdgeNodeDescription must be defined in config file +// EdgeNodeDescription must be defined in config file type EdgeNodeDescription struct { Key string Serial string Model string } -//GetEdgeNode returns EdgeNode for provided EdgeNodeDescription based on onboarding key (if exists) or name +// GetEdgeNode returns EdgeNode for provided EdgeNodeDescription based on onboarding key (if exists) or name func (nodeDescription *EdgeNodeDescription) GetEdgeNode(tc *TestContext) *device.Ctx { ctrl := tc.GetController() if nodeDescription.Key != "" { @@ -26,10 +26,10 @@ func (nodeDescription *EdgeNodeDescription) GetEdgeNode(tc *TestContext) *device return nil } -//EdgeNodeOption is type to use for creation of device.Ctx +// EdgeNodeOption is type to use for creation of device.Ctx type EdgeNodeOption func(description *device.Ctx) -//WithNodeDescription sets device info +// WithNodeDescription sets device info func (tc *TestContext) WithNodeDescription(nodeDescription *EdgeNodeDescription) EdgeNodeOption { return func(d *device.Ctx) { d.SetDevModel(nodeDescription.Model) @@ -38,14 +38,14 @@ func (tc *TestContext) WithNodeDescription(nodeDescription *EdgeNodeDescription) } } -//WithCurrentProject sets project info +// WithCurrentProject sets project info func (tc *TestContext) WithCurrentProject() EdgeNodeOption { return func(d *device.Ctx) { d.SetProject(tc.project.name) } } -//WithDeviceModel sets device model info +// WithDeviceModel sets device model info func (tc *TestContext) WithDeviceModel(devModel string) EdgeNodeOption { return func(d *device.Ctx) { d.SetDevModel(devModel) diff --git a/pkg/projects/functions.go b/pkg/testcontext/functions.go similarity index 99% rename from pkg/projects/functions.go rename to pkg/testcontext/functions.go index 3e540de04..bc0601055 100644 --- a/pkg/projects/functions.go +++ b/pkg/testcontext/functions.go @@ -1,4 +1,4 @@ -package projects +package testcontext import ( "fmt" diff --git a/pkg/projects/state.go b/pkg/testcontext/state.go similarity index 99% rename from pkg/projects/state.go rename to pkg/testcontext/state.go index 4b9d8a065..7c8e4f97b 100644 --- a/pkg/projects/state.go +++ b/pkg/testcontext/state.go @@ -1,4 +1,4 @@ -package projects +package testcontext import ( "reflect" diff --git a/pkg/projects/testContext.go b/pkg/testcontext/testContext.go similarity index 73% rename from pkg/projects/testContext.go rename to pkg/testcontext/testContext.go index 93e235ff6..85510568e 100644 --- a/pkg/projects/testContext.go +++ b/pkg/testcontext/testContext.go @@ -1,4 +1,4 @@ -package projects +package testcontext import ( "fmt" @@ -21,37 +21,26 @@ import ( "github.com/spf13/viper" ) -//GetControllerMode parse url with controller -func GetControllerMode(controllerMode string) (modeType, modeURL string, err error) { - params := utils.GetParams(controllerMode, defaults.DefaultControllerModePattern) - if len(params) == 0 { - return "", "", fmt.Errorf("cannot parse mode (not [file|proto|adam|zedcloud]://<URL>): %s", controllerMode) - } - ok := false - if modeType, ok = params["Type"]; !ok { - return "", "", fmt.Errorf("cannot parse modeType (not [file|proto|adam|zedcloud]://<URL>): %s", controllerMode) - } - if modeURL, ok = params["URL"]; !ok { - return "", "", fmt.Errorf("cannot parse modeURL (not [file|proto|adam|zedcloud]://<URL>): %s", controllerMode) - } - return +// Project structure for test set +type Project struct { + name string } -//TestContext is main structure for running tests +// TestContext is main structure for running tests type TestContext struct { - cloud controller.Cloud + Cloud controller.Cloud project *Project nodes []*device.Ctx - sdnClient *edensdn.SdnClient - withSdn bool - procBus *processingBus - tests map[*device.Ctx]*testing.T + SdnClient *edensdn.SdnClient + WithSdn bool + ProcBus *processingBus + Tests map[*device.Ctx]*testing.T states map[*device.Ctx]*State stopTime time.Time addTime time.Duration } -//NewTestContext creates new TestContext +// NewTestContext creates new TestContext func NewTestContext() *TestContext { var ( err error @@ -68,7 +57,7 @@ func NewTestContext() *TestContext { log.Fatalf("LoadConfigFile %s", err) } if viperLoaded { - modeType, modeURL, err := GetControllerMode(viper.GetString("test.controller")) + modeType, modeURL, err := utils.GetControllerMode(viper.GetString("test.controller"), defaults.DefaultControllerModePattern) if err != nil { log.Debug(err) } @@ -118,16 +107,16 @@ func NewTestContext() *TestContext { } ctx.GetAllNodes() tstCtx := &TestContext{ - cloud: ctx, - tests: map[*device.Ctx]*testing.T{}, - sdnClient: sdnClient, - withSdn: withSdn, + Cloud: ctx, + Tests: map[*device.Ctx]*testing.T{}, + SdnClient: sdnClient, + WithSdn: withSdn, } - tstCtx.procBus = initBus(tstCtx) + tstCtx.ProcBus = InitBus(tstCtx) return tstCtx } -//GetNodeDescriptions returns list of nodes from config +// GetNodeDescriptions returns list of nodes from config func (tc *TestContext) GetNodeDescriptions() (nodes []*EdgeNodeDescription) { if eveList := viper.GetStringMap("test.eve"); len(eveList) > 0 { for name := range eveList { @@ -147,20 +136,20 @@ func (tc *TestContext) GetNodeDescriptions() (nodes []*EdgeNodeDescription) { return } -//GetController returns current controller +// GetController returns current controller func (tc *TestContext) GetController() controller.Cloud { - if tc.cloud == nil { + if tc.Cloud == nil { log.Fatal("Controller not initialized") } - return tc.cloud + return tc.Cloud } -//InitProject init project object with defined name +// InitProject init project object with defined name func (tc *TestContext) InitProject(name string) { tc.project = &Project{name: name} } -//AddEdgeNodesFromDescription adds EdgeNodes from description in test.eve param +// AddEdgeNodesFromDescription adds EdgeNodes from description in test.eve param func (tc *TestContext) AddEdgeNodesFromDescription() { for _, node := range tc.GetNodeDescriptions() { edgeNode := node.GetEdgeNode(tc) @@ -180,18 +169,18 @@ func (tc *TestContext) AddEdgeNodesFromDescription() { } } -//GetEdgeNodeOpts pattern to pass device modifications +// GetEdgeNodeOpts pattern to pass device modifications type GetEdgeNodeOpts func(*device.Ctx) bool -//WithTest assign *testing.T for device +// WithTest assign *testing.T for device func (tc *TestContext) WithTest(t *testing.T) GetEdgeNodeOpts { return func(d *device.Ctx) bool { - tc.tests[d] = t + tc.Tests[d] = t return true } } -//GetEdgeNode return node from context +// GetEdgeNode return node from context func (tc *TestContext) GetEdgeNode(opts ...GetEdgeNodeOpts) *device.Ctx { Node: for _, el := range tc.nodes { @@ -205,12 +194,12 @@ Node: return nil } -//AddNode add node to test context +// AddNode add node to test context func (tc *TestContext) AddNode(node *device.Ctx) { tc.nodes = append(tc.nodes, node) } -//UpdateEdgeNode update edge node +// UpdateEdgeNode update edge node func (tc *TestContext) UpdateEdgeNode(edgeNode *device.Ctx, opts ...EdgeNodeOption) { for _, opt := range opts { opt(edgeNode) @@ -218,7 +207,7 @@ func (tc *TestContext) UpdateEdgeNode(edgeNode *device.Ctx, opts ...EdgeNodeOpti tc.ConfigSync(edgeNode) } -//NewEdgeNode creates edge node +// NewEdgeNode creates edge node func (tc *TestContext) NewEdgeNode(opts ...EdgeNodeOption) *device.Ctx { d := device.CreateEdgeNode() for _, opt := range opts { @@ -231,7 +220,7 @@ func (tc *TestContext) NewEdgeNode(opts ...EdgeNodeOption) *device.Ctx { return d } -//ConfigSync send config to controller +// ConfigSync send config to controller func (tc *TestContext) ConfigSync(edgeNode *device.Ctx) { if edgeNode.GetState() == device.NotOnboarded { if err := tc.GetController().OnBoardDev(edgeNode); err != nil { @@ -245,34 +234,34 @@ func (tc *TestContext) ConfigSync(edgeNode *device.Ctx) { } } -//ExpandOnSuccess adds additional time to global timeout on every success check +// ExpandOnSuccess adds additional time to global timeout on every success check func (tc *TestContext) ExpandOnSuccess(secs int) { tc.addTime = time.Duration(secs) * time.Second } -//WaitForProcWithErrorCallback blocking execution until the time elapses or all Procs gone -//and fires callback in case of timeout +// WaitForProcWithErrorCallback blocking execution until the time elapses or all Procs gone +// and fires callback in case of timeout func (tc *TestContext) WaitForProcWithErrorCallback(secs int, callback Callback) { defer func() { tc.addTime = 0 }() //reset addTime on exit - defer tc.procBus.clean() + defer tc.ProcBus.clean() timeout := time.Duration(secs) * time.Second tc.stopTime = time.Now().Add(timeout) ticker := time.NewTicker(defaults.DefaultRepeatTimeout) defer ticker.Stop() waitChan := make(chan struct{}, 1) go func() { - tc.procBus.wg.Wait() + tc.ProcBus.wg.Wait() waitChan <- struct{}{} }() for { select { case <-waitChan: - for node, el := range tc.tests { + for node, el := range tc.Tests { el.Logf("done for device %s", node.GetID()) } return case <-ticker.C: - for _, el := range tc.tests { + for _, el := range tc.Tests { if el.Failed() { // if one of tests failed, we are failed callback() @@ -281,7 +270,7 @@ func (tc *TestContext) WaitForProcWithErrorCallback(secs int, callback Callback) } if time.Now().After(tc.stopTime) { callback() - for _, el := range tc.tests { + for _, el := range tc.Tests { el.Errorf("WaitForProcWithErrorCallback terminated by timeout %s", timeout) } return @@ -290,53 +279,53 @@ func (tc *TestContext) WaitForProcWithErrorCallback(secs int, callback Callback) } } -//WaitForProc blocking execution until the time elapses or all Procs gone -//returns error on timeout +// WaitForProc blocking execution until the time elapses or all Procs gone +// returns error on timeout func (tc *TestContext) WaitForProc(secs int) { timeout := time.Duration(secs) * time.Second callback := func() { - if len(tc.tests) == 0 { + if len(tc.Tests) == 0 { log.Fatalf("WaitForProc terminated by timeout %s", timeout) } - for _, el := range tc.tests { + for _, el := range tc.Tests { el.Errorf("WaitForProc terminated by timeout %s", timeout) } } tc.WaitForProcWithErrorCallback(secs, callback) } -//AddProcLog add processFunction, that will get all logs for edgeNode +// AddProcLog add processFunction, that will get all logs for edgeNode func (tc *TestContext) AddProcLog(edgeNode *device.Ctx, processFunction ProcLogFunc) { - tc.procBus.addProc(edgeNode, processFunction) + tc.ProcBus.addProc(edgeNode, processFunction) } -//AddProcAppLog add processFunction, that will get all app logs for edgeNode +// AddProcAppLog add processFunction, that will get all app logs for edgeNode func (tc *TestContext) AddProcAppLog(edgeNode *device.Ctx, appUUID uuid.UUID, processFunction ProcAppLogFunc) { - tc.procBus.addAppProc(edgeNode, appUUID, processFunction) + tc.ProcBus.addAppProc(edgeNode, appUUID, processFunction) } -//AddProcFlowLog add processFunction, that will get all FlowLogs for edgeNode +// AddProcFlowLog add processFunction, that will get all FlowLogs for edgeNode func (tc *TestContext) AddProcFlowLog(edgeNode *device.Ctx, processFunction ProcLogFlowFunc) { - tc.procBus.addProc(edgeNode, processFunction) + tc.ProcBus.addProc(edgeNode, processFunction) } -//AddProcInfo add processFunction, that will get all info for edgeNode +// AddProcInfo add processFunction, that will get all info for edgeNode func (tc *TestContext) AddProcInfo(edgeNode *device.Ctx, processFunction ProcInfoFunc) { - tc.procBus.addProc(edgeNode, processFunction) + tc.ProcBus.addProc(edgeNode, processFunction) } -//AddProcMetric add processFunction, that will get all metrics for edgeNode +// AddProcMetric add processFunction, that will get all metrics for edgeNode func (tc *TestContext) AddProcMetric(edgeNode *device.Ctx, processFunction ProcMetricFunc) { - tc.procBus.addProc(edgeNode, processFunction) + tc.ProcBus.addProc(edgeNode, processFunction) } -//AddProcTimer add processFunction, that will fire with time intervals for edgeNode +// AddProcTimer add processFunction, that will fire with time intervals for edgeNode func (tc *TestContext) AddProcTimer(edgeNode *device.Ctx, processFunction ProcTimerFunc) { - tc.procBus.addProc(edgeNode, processFunction) + tc.ProcBus.addProc(edgeNode, processFunction) } -//StartTrackingState init function for State monitoring -//if onlyNewElements set no use old information from controller +// StartTrackingState init function for State monitoring +// if onlyNewElements set no use old information from controller func (tc *TestContext) StartTrackingState(onlyNewElements bool) { tc.states = map[*device.Ctx]*State{} for _, dev := range tc.nodes { @@ -347,15 +336,15 @@ func (tc *TestContext) StartTrackingState(onlyNewElements bool) { _ = tc.GetController().InfoLastCallback(dev.GetID(), map[string]string{}, curState.getProcessorInfo()) _ = tc.GetController().MetricLastCallback(dev.GetID(), map[string]string{}, curState.getProcessorMetric()) } - if _, exists := tc.procBus.proc[dev]; !exists { - tc.procBus.initCheckers(dev) + if _, exists := tc.ProcBus.proc[dev]; !exists { + tc.ProcBus.initCheckers(dev) } - tc.procBus.proc[dev] = append(tc.procBus.proc[dev], &absFunc{proc: curState.GetInfoProcessingFunction(), disabled: false, states: true}) - tc.procBus.proc[dev] = append(tc.procBus.proc[dev], &absFunc{proc: curState.GetMetricProcessingFunction(), disabled: false, states: true}) + tc.ProcBus.proc[dev] = append(tc.ProcBus.proc[dev], &absFunc{proc: curState.GetInfoProcessingFunction(), disabled: false, states: true}) + tc.ProcBus.proc[dev] = append(tc.ProcBus.proc[dev], &absFunc{proc: curState.GetMetricProcessingFunction(), disabled: false, states: true}) } } -//WaitForState wait for State initialization from controller +// WaitForState wait for State initialization from controller func (tc *TestContext) WaitForState(edgeNode *device.Ctx, secs int) { state, isOk := tc.states[edgeNode] if !isOk { @@ -374,24 +363,24 @@ func (tc *TestContext) WaitForState(edgeNode *device.Ctx, secs int) { }() select { case <-waitChan: - if el, isOk := tc.tests[edgeNode]; !isOk { + if el, isOk := tc.Tests[edgeNode]; !isOk { log.Println("done waiting for State") } else { el.Logf("done waiting for State") } return case <-time.After(timeout): - if len(tc.tests) == 0 { + if len(tc.Tests) == 0 { log.Fatalf("WaitForState terminated by timeout %s", timeout) } - for _, el := range tc.tests { + for _, el := range tc.Tests { el.Fatalf("WaitForState terminated by timeout %s", timeout) } return } } -//GetState returns State object for edgeNode +// GetState returns State object for edgeNode func (tc *TestContext) GetState(edgeNode *device.Ctx) *State { return tc.states[edgeNode] } @@ -402,7 +391,7 @@ func (tc *TestContext) GetState(edgeNode *device.Ctx) *State { // as the destination IP and fwdPort as the destination port. func (tc *TestContext) PortForwardCommand(cmd func(fwdPort uint16) error, eveIfName string, targetPort uint16) error { - if !tc.withSdn { + if !tc.WithSdn { // Find out what the targetPort is (statically) mapped to in the host. targetHostPort := -1 hostFwd := viper.GetStringMapString("eve.hostfwd") @@ -435,7 +424,7 @@ func (tc *TestContext) PortForwardCommand(cmd func(fwdPort uint16) error, return cmd(uint16(targetHostPort)) } // Temporarily establish port forwarding using SSH. - targetIP, err := tc.sdnClient.GetEveIfIP(eveIfName) + targetIP, err := tc.SdnClient.GetEveIfIP(eveIfName) if err != nil { log.Errorf("failed to get EVE IP address: %v", err) return nil @@ -445,7 +434,7 @@ func (tc *TestContext) PortForwardCommand(cmd func(fwdPort uint16) error, log.Errorf("failed to find unused port number: %v", err) return nil } - closeTunnel, err := tc.sdnClient.SSHPortForwarding(localPort, targetPort, targetIP) + closeTunnel, err := tc.SdnClient.SSHPortForwarding(localPort, targetPort, targetIP) if err != nil { log.Errorf("failed to establish SSH port forwarding: %v", err) return nil diff --git a/pkg/projects/testProc.go b/pkg/testcontext/testProc.go similarity index 98% rename from pkg/projects/testProc.go rename to pkg/testcontext/testProc.go index f1107f0f9..9d8f84b66 100644 --- a/pkg/projects/testProc.go +++ b/pkg/testcontext/testProc.go @@ -1,4 +1,4 @@ -package projects +package testcontext import ( "fmt" @@ -56,7 +56,7 @@ type processingBus struct { proc map[*device.Ctx][]*absFunc } -func initBus(tc *TestContext) *processingBus { +func InitBus(tc *TestContext) *processingBus { return &processingBus{tc: tc, proc: map[*device.Ctx][]*absFunc{}, wg: &sync.WaitGroup{}} } @@ -80,7 +80,7 @@ func (lb *processingBus) processReturn(edgeNode *device.Ctx, procFunc *absFunc, } procFunc.disabled = true toRet := utils.AddTimestamp(fmt.Sprintf("%T done with return: %s", procFunc.proc, result.Error())) - if t, ok := lb.tc.tests[edgeNode]; ok { + if t, ok := lb.tc.Tests[edgeNode]; ok { t.Log(toRet) } log.Info(toRet) diff --git a/pkg/utils/files.go b/pkg/utils/files.go index 3067fb8bf..e9a7d2641 100644 --- a/pkg/utils/files.go +++ b/pkg/utils/files.go @@ -15,7 +15,7 @@ import ( "github.com/spf13/viper" ) -//SHA256SUM calculates sha256 of file +// SHA256SUM calculates sha256 of file func SHA256SUM(filePath string) string { file, err := os.Open(filePath) if err != nil { @@ -30,7 +30,7 @@ func SHA256SUM(filePath string) string { return hex.EncodeToString(hash.Sum(nil)) } -//CopyFileNotExists copy file from src to dst with same permission if not exists +// CopyFileNotExists copy file from src to dst with same permission if not exists func CopyFileNotExists(src string, dst string) (err error) { if _, err = os.Lstat(dst); os.IsNotExist(err) { if err = CopyFile(src, dst); err != nil { @@ -40,7 +40,7 @@ func CopyFileNotExists(src string, dst string) (err error) { return nil } -//CopyFile copy file from src to dst with same permission +// CopyFile copy file from src to dst with same permission func CopyFile(src string, dst string) (err error) { info, err := os.Lstat(src) if err != nil { @@ -81,7 +81,7 @@ func CopyFile(src string, dst string) (err error) { return } -//TouchFile create empty file +// TouchFile create empty file func TouchFile(src string) (err error) { if _, err := os.Stat(src); os.IsNotExist(err) { file, err := os.Create(src) @@ -99,23 +99,28 @@ func TouchFile(src string) (err error) { return nil } -//FileNameWithoutExtension trim file extension and path +// FileNameWithoutExtension trim file extension and path func FileNameWithoutExtension(fileName string) string { return filepath.Base(strings.TrimSuffix(fileName, filepath.Ext(fileName))) } -//ResolveAbsPath use eden.root parameter to resolve path +// ResolveAbsPath use eden.root parameter to resolve path func ResolveAbsPath(curPath string) string { + return ResolveAbsPathWithRoot(viper.GetString("eden.root"), curPath) +} + +// ResolveAbsPathWithRoot use rootPath parameter to resolve path +func ResolveAbsPathWithRoot(rootPath, curPath string) string { if strings.TrimSpace(curPath) == "" { return "" } if !filepath.IsAbs(curPath) { - return filepath.Join(viper.GetString("eden.root"), strings.TrimSpace(curPath)) + return filepath.Join(rootPath, strings.TrimSpace(curPath)) } return curPath } -//GetFileFollowLinks resolve file by walking through symlinks +// GetFileFollowLinks resolve file by walking through symlinks func GetFileFollowLinks(filePath string) (string, error) { log.Debugf("GetFileFollowLinks %s", filePath) filePath = ResolveHomeDir(filePath) @@ -137,7 +142,7 @@ func GetFileFollowLinks(filePath string) (string, error) { return filepath.Join(filepath.Dir(filePath), fileInfo.Name()), nil } -//GetFileSize returns file size +// GetFileSize returns file size func GetFileSize(filePath string) int64 { fi, err := os.Stat(filePath) if err != nil { @@ -146,7 +151,7 @@ func GetFileSize(filePath string) int64 { return fi.Size() } -//ResolveHomeDir resolve ~ in path +// ResolveHomeDir resolve ~ in path func ResolveHomeDir(filePath string) string { usr, err := user.Current() if err != nil { @@ -161,7 +166,7 @@ func ResolveHomeDir(filePath string) string { return filePath } -//CopyFolder from source to destination +// CopyFolder from source to destination func CopyFolder(source, destination string) error { var err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error { var relPath = strings.Replace(path, source, "", 1) @@ -182,7 +187,7 @@ func IsInputFromPipe() bool { return fileInfo.Mode()&os.ModeCharDevice == 0 } -//SHA256SUMAll calculates sha256 of directory +// SHA256SUMAll calculates sha256 of directory func SHA256SUMAll(dir string) (string, error) { hash := sha256.New() err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { @@ -207,7 +212,7 @@ func SHA256SUMAll(dir string) (string, error) { return hex.EncodeToString(hash.Sum(nil)), nil } -//CreateDisk creates empty disk with defined format on diskFile with size bytes capacity +// CreateDisk creates empty disk with defined format on diskFile with size bytes capacity func CreateDisk(diskFile, format string, size uint64) error { if err := os.MkdirAll(filepath.Dir(diskFile), 0755); err != nil { return err diff --git a/pkg/utils/params.go b/pkg/utils/params.go index bff162afc..ede5ef505 100644 --- a/pkg/utils/params.go +++ b/pkg/utils/params.go @@ -1,12 +1,29 @@ package utils import ( + "fmt" "math/rand" "regexp" "strings" "time" ) +// GetControllerMode parse url with controller +func GetControllerMode(controllerMode, modePattern string) (modeType, modeURL string, err error) { + params := GetParams(controllerMode, modePattern) + if len(params) == 0 { + return "", "", fmt.Errorf("cannot parse mode (not [file|proto|adam|zedcloud]://<URL>): %s", controllerMode) + } + ok := false + if modeType, ok = params["Type"]; !ok { + return "", "", fmt.Errorf("cannot parse modeType (not [file|proto|adam|zedcloud]://<URL>): %s", controllerMode) + } + if modeURL, ok = params["URL"]; !ok { + return "", "", fmt.Errorf("cannot parse modeURL (not [file|proto|adam|zedcloud]://<URL>): %s", controllerMode) + } + return +} + // GetParams parse line with regexp into map func GetParams(line, regEx string) (paramsMap map[string]string) { diff --git a/tests/app/app_test.go b/tests/app/app_test.go index 088788511..bce8c8c30 100644 --- a/tests/app/app_test.go +++ b/tests/app/app_test.go @@ -10,7 +10,7 @@ import ( "github.com/lf-edge/eden/pkg/controller/eapps" "github.com/lf-edge/eden/pkg/controller/types" "github.com/lf-edge/eden/pkg/eve" - "github.com/lf-edge/eden/pkg/projects" + "github.com/lf-edge/eden/pkg/testcontext" "github.com/lf-edge/eden/pkg/tests" "github.com/lf-edge/eden/pkg/utils" "github.com/lf-edge/eve-api/go/info" @@ -26,7 +26,7 @@ type appState struct { var ( timewait = flag.Duration("timewait", 10*time.Minute, "Timewait for items waiting") newitems = flag.Bool("check-new", false, "Check only new info messages") - tc *projects.TestContext + tc *testcontext.TestContext states map[string][]appState eveState *eve.State ) @@ -40,7 +40,7 @@ func TestMain(m *testing.M) { tests.TestArgsParse() - tc = projects.NewTestContext() + tc = testcontext.NewTestContext() projectName := fmt.Sprintf("%s_%s", "TestAppState", time.Now()) @@ -131,7 +131,7 @@ func checkState(eveState *eve.State, state string, appNames []string) error { } // checkApp wait for info of ZInfoApp type with state -func checkApp(state string, appNames []string) projects.ProcInfoFunc { +func checkApp(state string, appNames []string) testcontext.ProcInfoFunc { return func(msg *info.ZInfoMsg) error { eveState.InfoCallback()(msg) //feed state with new info return checkState(eveState, state, appNames) diff --git a/tests/docker/docker_test.go b/tests/docker/docker_test.go index dc7151702..0dd84c0d8 100644 --- a/tests/docker/docker_test.go +++ b/tests/docker/docker_test.go @@ -13,7 +13,7 @@ import ( "github.com/dustin/go-humanize" "github.com/lf-edge/eden/pkg/device" "github.com/lf-edge/eden/pkg/expect" - "github.com/lf-edge/eden/pkg/projects" + "github.com/lf-edge/eden/pkg/testcontext" "github.com/lf-edge/eden/pkg/utils" "github.com/lf-edge/eve-api/go/config" "github.com/lf-edge/eve-api/go/info" @@ -33,7 +33,7 @@ var ( cpus = flag.Uint("cpus", 1, "Cpu number for app") memory = flag.String("memory", "1G", "Memory for app") nohyper = flag.Bool("nohyper", false, "Do not use a hypervisor") - tc *projects.TestContext + tc *testcontext.TestContext externalIP string portPublish []string appName string @@ -46,7 +46,7 @@ var ( func TestMain(m *testing.M) { fmt.Println("Docker app deployment Test") - tc = projects.NewTestContext() + tc = testcontext.NewTestContext() projectName := fmt.Sprintf("%s_%s", "TestDockerDeploy", time.Now()) @@ -62,7 +62,7 @@ func TestMain(m *testing.M) { } // checkAppDeployStarted wait for info of ZInfoApp type with mention of deployed AppName -func checkAppDeployStarted(appName string) projects.ProcInfoFunc { +func checkAppDeployStarted(appName string) testcontext.ProcInfoFunc { return func(msg *info.ZInfoMsg) error { if msg.Ztype == info.ZInfoTypes_ZiApp { if msg.GetAinfo().AppName == appName { @@ -74,7 +74,7 @@ func checkAppDeployStarted(appName string) projects.ProcInfoFunc { } // checkAppRunning wait for info of ZInfoApp type with mention of deployed AppName and ZSwState_RUNNING state -func checkAppRunning(appName string) projects.ProcInfoFunc { +func checkAppRunning(appName string) testcontext.ProcInfoFunc { return func(msg *info.ZInfoMsg) error { if msg.Ztype == info.ZInfoTypes_ZiApp { if msg.GetAinfo().AppName == appName { @@ -88,7 +88,7 @@ func checkAppRunning(appName string) projects.ProcInfoFunc { } // getEVEIP wait for IPs of EVE and returns them -func getEVEIP(edgeNode *device.Ctx) projects.ProcTimerFunc { +func getEVEIP(edgeNode *device.Ctx) testcontext.ProcTimerFunc { return func() error { if edgeNode.GetRemoteAddr() == "" { //no eve.remote-addr defined eveIP, err := tc.GetState(edgeNode).LookUp("Dinfo.Network[0].IPAddrs[0]") @@ -108,7 +108,7 @@ func getEVEIP(edgeNode *device.Ctx) projects.ProcTimerFunc { } // checkAppAccess try to access APP with timer -func checkAppAccess(edgeNode *device.Ctx) projects.ProcTimerFunc { +func checkAppAccess(edgeNode *device.Ctx) testcontext.ProcTimerFunc { return func() error { if edgeNode.GetRemote() { if externalIP == "" { @@ -132,7 +132,7 @@ func checkAppAccess(edgeNode *device.Ctx) projects.ProcTimerFunc { } // checkAppAbsent check if APP undefined in EVE -func checkAppAbsent(appName string) projects.ProcInfoFunc { +func checkAppAbsent(appName string) testcontext.ProcInfoFunc { return func(msg *info.ZInfoMsg) error { if msg.Ztype == info.ZInfoTypes_ZiDevice { for _, app := range msg.GetDinfo().AppInstances { diff --git a/tests/fsstress/fsstress_test.go b/tests/fsstress/fsstress_test.go index ae40c0d8d..aa6dc0192 100644 --- a/tests/fsstress/fsstress_test.go +++ b/tests/fsstress/fsstress_test.go @@ -17,7 +17,7 @@ import ( "github.com/lf-edge/eden/pkg/controller/types" "github.com/lf-edge/eden/pkg/device" "github.com/lf-edge/eden/pkg/expect" - "github.com/lf-edge/eden/pkg/projects" + "github.com/lf-edge/eden/pkg/testcontext" "github.com/lf-edge/eden/pkg/utils" "github.com/lf-edge/eve-api/go/config" "github.com/lf-edge/eve-api/go/info" @@ -43,7 +43,7 @@ var ( direct = flag.Bool("direct", true, "Load image from url, not from eserver") password = flag.String("password", "passw0rd", "Password to use for ssh") appLink = flag.String("applink", "https://cloud-images.ubuntu.com/releases/impish/release-20220201/ubuntu-21.10-server-cloudimg-%s.img", "Link to qcow2 image. You can pass %s for automatically set of arch (amd64/arm64)") - tc *projects.TestContext + tc *testcontext.TestContext externalIP string appName string ) @@ -55,7 +55,7 @@ var ( func TestMain(m *testing.M) { fmt.Println("FSstress test") - tc = projects.NewTestContext() + tc = testcontext.NewTestContext() projectName := fmt.Sprintf("%s_%s", "TestFSstress", time.Now()) @@ -82,7 +82,7 @@ func setAppName() { } // checkAppRunning wait for info of ZInfoApp type with mention of deployed AppName and ZSwState_RUNNING state -func checkAppRunning(appName string) projects.ProcInfoFunc { +func checkAppRunning(appName string) testcontext.ProcInfoFunc { return func(msg *info.ZInfoMsg) error { if msg.Ztype == info.ZInfoTypes_ZiApp { if msg.GetAinfo().AppName == appName { @@ -96,7 +96,7 @@ func checkAppRunning(appName string) projects.ProcInfoFunc { } // getEVEIP wait for IPs of EVE and returns them -func getEVEIP(edgeNode *device.Ctx) projects.ProcTimerFunc { +func getEVEIP(edgeNode *device.Ctx) testcontext.ProcTimerFunc { return func() error { if edgeNode.GetRemoteAddr() == "" { //no eve.remote-addr defined eveIPCIDR, err := tc.GetState(edgeNode).LookUp("Dinfo.Network[0].IPAddrs[0]") @@ -116,7 +116,7 @@ func getEVEIP(edgeNode *device.Ctx) projects.ProcTimerFunc { } // checkAppAbsent check if APP undefined in EVE -func checkAppAbsent(appName string) projects.ProcInfoFunc { +func checkAppAbsent(appName string) testcontext.ProcInfoFunc { return func(msg *info.ZInfoMsg) error { if msg.Ztype == info.ZInfoTypes_ZiDevice { for _, app := range msg.GetDinfo().AppInstances { @@ -134,7 +134,7 @@ func checkAppAbsent(appName string) projects.ProcInfoFunc { // and returns success if less than 1 minutes left // also it checks existence of fsstress process on VM // and in case of not existence or some issues with connection it fails test immediately -func CheckTimeWorkOfTest(t *testing.T, edgeNode *device.Ctx, timeStart time.Time) projects.ProcTimerFunc { +func CheckTimeWorkOfTest(t *testing.T, edgeNode *device.Ctx, timeStart time.Time) testcontext.ProcTimerFunc { return func() error { df := time.Since(timeStart) if df >= *timewait-time.Minute { @@ -149,19 +149,19 @@ func CheckTimeWorkOfTest(t *testing.T, edgeNode *device.Ctx, timeStart time.Time } } -func sshCommand(edgeNode *device.Ctx, command string) projects.ProcTimerFunc { +func sshCommand(edgeNode *device.Ctx, command string) testcontext.ProcTimerFunc { return func() error { if edgeNode.GetRemote() { if externalIP == "" { return nil } - sendSSHCommand := projects.SendCommandSSH(&externalIP, sshPort, "ubuntu", *password, command, true) + sendSSHCommand := testcontext.SendCommandSSH(&externalIP, sshPort, "ubuntu", *password, command, true) return sendSSHCommand() } return tc.PortForwardCommand(func(fwdPort uint16) error { localhostIP := "127.0.0.1" sshPort := int(fwdPort) - sendSSHCommand := projects.SendCommandSSH(&localhostIP, &sshPort, "ubuntu", *password, command, true) + sendSSHCommand := testcontext.SendCommandSSH(&localhostIP, &sshPort, "ubuntu", *password, command, true) return sendSSHCommand() }, "eth0", uint16(*sshPort)) } @@ -323,7 +323,7 @@ func TestRunStress(t *testing.T) { } t.Log(utils.AddTimestamp("Send script on guest VM")) - result = projects.SendFileSCP(&externalIP, sshPort, "ubuntu", *password, pathScript, "/home/ubuntu/run-script.sh")() + result = testcontext.SendFileSCP(&externalIP, sshPort, "ubuntu", *password, pathScript, "/home/ubuntu/run-script.sh")() if result == nil { t.Fatal(utils.AddTimestamp("Error in scp")) } diff --git a/tests/lim/lim_test.go b/tests/lim/lim_test.go index 6ec48cebc..2d4d02650 100644 --- a/tests/lim/lim_test.go +++ b/tests/lim/lim_test.go @@ -18,7 +18,7 @@ import ( "github.com/lf-edge/eden/pkg/controller/emetric" "github.com/lf-edge/eden/pkg/controller/types" "github.com/lf-edge/eden/pkg/device" - "github.com/lf-edge/eden/pkg/projects" + "github.com/lf-edge/eden/pkg/testcontext" "github.com/lf-edge/eden/pkg/tests" "github.com/lf-edge/eden/pkg/utils" "github.com/lf-edge/eve-api/go/flowlog" @@ -46,7 +46,7 @@ var ( // ... // } */ - tc *projects.TestContext + tc *testcontext.TestContext query = map[string]string{} found bool @@ -95,7 +95,7 @@ func TestMain(m *testing.M) { tests.TestArgsParse() - tc = projects.NewTestContext() + tc = testcontext.NewTestContext() projectName := fmt.Sprintf("%s_%s", "TestLogInfoMetric", time.Now()) diff --git a/tests/network/nw_test.go b/tests/network/nw_test.go index 829e4e982..7c2c98419 100644 --- a/tests/network/nw_test.go +++ b/tests/network/nw_test.go @@ -8,7 +8,7 @@ import ( "time" "github.com/lf-edge/eden/pkg/eve" - "github.com/lf-edge/eden/pkg/projects" + "github.com/lf-edge/eden/pkg/testcontext" "github.com/lf-edge/eden/pkg/utils" "github.com/lf-edge/eve-api/go/info" ) @@ -22,7 +22,7 @@ type nwState struct { var ( timewait = flag.Duration("timewait", time.Minute, "Timewait for items waiting") newitems = flag.Bool("check-new", false, "Check only new info messages") - tc *projects.TestContext + tc *testcontext.TestContext states map[string][]nwState eveState *eve.State ) @@ -34,7 +34,7 @@ var ( func TestMain(m *testing.M) { fmt.Println("Network's state test") - tc = projects.NewTestContext() + tc = testcontext.NewTestContext() projectName := fmt.Sprintf("%s_%s", "TestNetState", time.Now()) diff --git a/tests/reboot/reboot_test.go b/tests/reboot/reboot_test.go index be33bc04c..e5c56aa3a 100644 --- a/tests/reboot/reboot_test.go +++ b/tests/reboot/reboot_test.go @@ -9,7 +9,7 @@ import ( "time" "github.com/lf-edge/eden/pkg/device" - "github.com/lf-edge/eden/pkg/projects" + "github.com/lf-edge/eden/pkg/testcontext" "github.com/lf-edge/eden/pkg/tests" "github.com/lf-edge/eden/pkg/utils" "github.com/lf-edge/eve-api/go/info" @@ -38,12 +38,12 @@ var ( number int - tc *projects.TestContext + tc *testcontext.TestContext lastRebootTime *timestamppb.Timestamp ) -func checkReboot(t *testing.T, edgeNode *device.Ctx) projects.ProcInfoFunc { +func checkReboot(t *testing.T, edgeNode *device.Ctx) testcontext.ProcInfoFunc { return func(im *info.ZInfoMsg) error { if im.GetZtype() != info.ZInfoTypes_ZiDevice { return nil @@ -91,7 +91,7 @@ func TestMain(m *testing.M) { tests.TestArgsParse() - tc = projects.NewTestContext() + tc = testcontext.NewTestContext() projectName := fmt.Sprintf("%s_%s", "TestReboot", time.Now()) diff --git a/tests/sec/sec_test.go b/tests/sec/sec_test.go index 9da0c122e..8b07bb335 100644 --- a/tests/sec/sec_test.go +++ b/tests/sec/sec_test.go @@ -1,11 +1,15 @@ package sec_test import ( + "fmt" "os" + "path/filepath" "strings" "testing" + "github.com/lf-edge/eden/pkg/defaults" tk "github.com/lf-edge/eden/pkg/evetestkit" + "github.com/lf-edge/eden/pkg/openevec" log "github.com/sirupsen/logrus" ) @@ -18,7 +22,32 @@ func TestMain(m *testing.M) { log.Println("Security Test Suite started") defer log.Println("Security Test Suite finished") - node, err := tk.InitilizeTest(projectName, tk.WithControllerVerbosity("debug")) + currentPath, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + twoLevelsUp := filepath.Dir(filepath.Dir(currentPath)) + + cfg := openevec.GetDefaultConfig(twoLevelsUp) + + if err = openevec.ConfigAdd(cfg, cfg.ConfigName, "", false); err != nil { + log.Fatal(err) + } + + evec := openevec.CreateOpenEVEC(cfg) + configDir := filepath.Join(twoLevelsUp, "eve-config-dir") + if err := evec.SetupEden("config", configDir, "", "", "", []string{}, false, false); err != nil { + log.Fatalf("Failed to setup Eden: %v", err) + } + if err := evec.StartEden(defaults.DefaultVBoxVMName, "", ""); err != nil { + log.Fatalf("Start eden failed: %s", err) + } + if err := evec.OnboardEve(cfg.Eve.CertsUUID); err != nil { + log.Fatalf("Eve onboard failed: %s", err) + } + + fmt.Println("INITIALIZING TEST FROM CONFIG") + node, err := tk.InitilizeTestFromConfig(projectName, cfg, tk.WithControllerVerbosity("debug")) if err != nil { log.Fatalf("Failed to initialize test: %v", err) } diff --git a/tests/vnc/vnc_test.go b/tests/vnc/vnc_test.go index 125c749a8..2cbe32ada 100644 --- a/tests/vnc/vnc_test.go +++ b/tests/vnc/vnc_test.go @@ -16,7 +16,7 @@ import ( "github.com/lf-edge/eden/pkg/controller/types" "github.com/lf-edge/eden/pkg/device" "github.com/lf-edge/eden/pkg/expect" - "github.com/lf-edge/eden/pkg/projects" + "github.com/lf-edge/eden/pkg/testcontext" "github.com/lf-edge/eden/pkg/utils" "github.com/lf-edge/eve-api/go/config" "github.com/lf-edge/eve-api/go/info" @@ -44,7 +44,7 @@ var ( appLink = flag.String("applink", "https://cloud-images.ubuntu.com/releases/groovy/release-20210108/ubuntu-20.10-server-cloudimg-%s.img", "Link to qcow2 image. You can pass %s for automatically set of arch (amd64/arm64)") doPanic = flag.Bool("panic", false, "Test kernel panic") doLogger = flag.Bool("logger", false, "Test logger print to console") - tc *projects.TestContext + tc *testcontext.TestContext externalIP string externalPort int appName string @@ -57,7 +57,7 @@ var ( func TestMain(m *testing.M) { fmt.Println("VNC access to app Test") - tc = projects.NewTestContext() + tc = testcontext.NewTestContext() projectName := fmt.Sprintf("%s_%s", "TestVNCAccess", time.Now()) @@ -91,7 +91,7 @@ func getVNCPort(vncDisplay int) int { } // checkAppRunning wait for info of ZInfoApp type with mention of deployed AppName and ZSwState_RUNNING state -func checkAppRunning(t *testing.T, appName string) projects.ProcInfoFunc { +func checkAppRunning(t *testing.T, appName string) testcontext.ProcInfoFunc { lastState := info.ZSwState_INVALID return func(msg *info.ZInfoMsg) error { if msg.Ztype == info.ZInfoTypes_ZiApp { @@ -110,7 +110,7 @@ func checkAppRunning(t *testing.T, appName string) projects.ProcInfoFunc { } // getEVEIP wait for IPs of EVE and returns them -func getEVEIP(edgeNode *device.Ctx) projects.ProcTimerFunc { +func getEVEIP(edgeNode *device.Ctx) testcontext.ProcTimerFunc { return func() error { if edgeNode.GetRemoteAddr() == "" { //no eve.remote-addr defined eveIPCIDR, err := tc.GetState(edgeNode).LookUp("Dinfo.Network[0].IPAddrs[0]") @@ -130,7 +130,7 @@ func getEVEIP(edgeNode *device.Ctx) projects.ProcTimerFunc { } // checkVNCAccess try to access APP via VNC with timer -func checkVNCAccess(edgeNode *device.Ctx) projects.ProcTimerFunc { +func checkVNCAccess(edgeNode *device.Ctx) testcontext.ProcTimerFunc { return func() error { if edgeNode.GetRemote() { if externalIP == "" { @@ -152,26 +152,26 @@ func checkVNCAccess(edgeNode *device.Ctx) projects.ProcTimerFunc { } } -func sshCommand(edgeNode *device.Ctx, command string, foreground bool) projects.ProcTimerFunc { +func sshCommand(edgeNode *device.Ctx, command string, foreground bool) testcontext.ProcTimerFunc { return func() error { if edgeNode.GetRemote() { if externalIP == "" { return nil } - sendSSHCommand := projects.SendCommandSSH(&externalIP, sshPort, "ubuntu", "passw0rd", command, foreground) + sendSSHCommand := testcontext.SendCommandSSH(&externalIP, sshPort, "ubuntu", "passw0rd", command, foreground) return sendSSHCommand() } return tc.PortForwardCommand(func(fwdPort uint16) error { localhostIP := "127.0.0.1" sshPort := int(fwdPort) - sendSSHCommand := projects.SendCommandSSH(&localhostIP, &sshPort, "ubuntu", "passw0rd", command, foreground) + sendSSHCommand := testcontext.SendCommandSSH(&localhostIP, &sshPort, "ubuntu", "passw0rd", command, foreground) return sendSSHCommand() }, "eth0", uint16(*sshPort)) } } // checkAppAbsent check if APP undefined in EVE -func checkAppAbsent(t *testing.T, appName string) projects.ProcInfoFunc { +func checkAppAbsent(t *testing.T, appName string) testcontext.ProcInfoFunc { lastState := info.ZSwState_INVALID return func(msg *info.ZInfoMsg) error { if msg.Ztype == info.ZInfoTypes_ZiDevice { diff --git a/tests/volume/vol_test.go b/tests/volume/vol_test.go index 555f94116..d3497cf59 100644 --- a/tests/volume/vol_test.go +++ b/tests/volume/vol_test.go @@ -8,7 +8,7 @@ import ( "time" "github.com/lf-edge/eden/pkg/eve" - "github.com/lf-edge/eden/pkg/projects" + "github.com/lf-edge/eden/pkg/testcontext" "github.com/lf-edge/eden/pkg/utils" "github.com/lf-edge/eve-api/go/info" ) @@ -22,7 +22,7 @@ type volState struct { var ( timewait = flag.Duration("timewait", time.Minute, "Timewait for items waiting") newitems = flag.Bool("check-new", false, "Check only new info messages") - tc *projects.TestContext + tc *testcontext.TestContext states map[string][]volState eveState *eve.State ) @@ -34,7 +34,7 @@ var ( func TestMain(m *testing.M) { fmt.Println("Docker volume's state test") - tc = projects.NewTestContext() + tc = testcontext.NewTestContext() projectName := fmt.Sprintf("%s_%s", "TestVolState", time.Now()) @@ -115,7 +115,7 @@ func checkState(eveState *eve.State, state string, volNames []string) error { } // checkVol wait for info of ZInfoApp type with state -func checkVol(state string, volNames []string) projects.ProcInfoFunc { +func checkVol(state string, volNames []string) testcontext.ProcInfoFunc { return func(msg *info.ZInfoMsg) error { eveState.InfoCallback()(msg) //feed state with new info return checkState(eveState, state, volNames)