From 68dba0e2e36d6e59b8b78120bbb3b19a77985b12 Mon Sep 17 00:00:00 2001 From: Pavel Abramov <31950564+uncleDecart@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:47:24 +0100 Subject: [PATCH] Deprecating escript: Introducing golang only tests (#1037) Instead of using escript to write tests, one now can write tests fully in golang and run those like this ``` cd tests/sec go test ``` So there's no layered complexity anymore, compared to calling calling forked from golang test binary, which is written by us which actually interprets text files as bash commands, execs those via exec and sometimes substitute some configuration with that's been written in specification below or call arbitratry bash scripts, or compare values from stdout and file using one character command ! For more information refer to #1037 Signed-off-by: Pavel Abramov --- .github/actions/setup-environment/action.yml | 8 + cmd/certs.go | 2 +- cmd/edenConfig.go | 20 +- cmd/edenSetup.go | 3 - cmd/edenStop.go | 2 +- cmd/edenTest.go | 3 +- docs/design-decisions.md | 73 +++++++ docs/{ => escript}/task-writing.md | 0 docs/{ => escript}/test-anatomy-sample.md | 0 docs/{ => escript}/test-running.md | 0 docs/writing-tests.md | 98 +++++++++ pkg/defaults/templates.go | 153 +++++++++++++- pkg/eden/eden.go | 28 +-- pkg/eve/state.go | 8 +- pkg/evetestkit/utils.go | 136 +++++++++++- pkg/openevec/changers.go | 4 +- pkg/openevec/config.go | 134 ++++++++---- pkg/openevec/config_test.go | 97 +++++++++ pkg/openevec/defaults.go | 200 +++++++++++++++--- pkg/openevec/eden.go | 25 ++- pkg/openevec/edenConfig.go | 30 ++- pkg/openevec/eve.go | 2 +- pkg/openevec/start.go | 10 - pkg/openevec/test.go | 13 +- pkg/projects/project.go | 6 - .../edgeNodeDescription.go | 14 +- pkg/{projects => testcontext}/functions.go | 2 +- pkg/{projects => testcontext}/state.go | 2 +- pkg/{projects => testcontext}/testContext.go | 149 ++++++------- pkg/{projects => testcontext}/testProc.go | 6 +- pkg/utils/files.go | 31 +-- pkg/utils/params.go | 20 ++ shell-scripts/activate.csh.tmpl | 48 ----- shell-scripts/activate.sh.tmpl | 97 --------- tests/README.md | 4 +- tests/app/app_test.go | 8 +- tests/docker/docker_test.go | 16 +- .../go-internal/testscript/testing_1.18.go | 45 ++-- tests/fsstress/fsstress_test.go | 22 +- tests/lim/lim_test.go | 6 +- tests/network/nw_test.go | 8 +- tests/reboot/reboot_test.go | 8 +- tests/sec/sec_test.go | 32 ++- tests/vcom/vcom_test.go | 2 +- tests/vnc/vnc_test.go | 20 +- tests/volume/vol_test.go | 8 +- tests/workflow/eve-upgrade.tests.txt | 2 +- tests/workflow/lps-loc.tests.txt | 2 +- tests/workflow/networking.tests.txt | 2 +- tests/workflow/smoke.tests.txt | 7 +- tests/workflow/storage.tests.txt | 2 +- tests/workflow/testdata/eden_onboard.txt | 9 - tests/workflow/user-apps.tests.txt | 2 +- tests/workflow/virtualization.tests.txt | 4 +- 54 files changed, 1123 insertions(+), 510 deletions(-) create mode 100644 docs/design-decisions.md rename docs/{ => escript}/task-writing.md (100%) rename docs/{ => escript}/test-anatomy-sample.md (100%) rename docs/{ => escript}/test-running.md (100%) create mode 100644 docs/writing-tests.md create mode 100644 pkg/openevec/config_test.go delete mode 100644 pkg/projects/project.go rename pkg/{projects => testcontext}/edgeNodeDescription.go (73%) rename pkg/{projects => testcontext}/functions.go (99%) rename pkg/{projects => testcontext}/state.go (99%) rename pkg/{projects => testcontext}/testContext.go (73%) rename pkg/{projects => testcontext}/testProc.go (98%) delete mode 100644 shell-scripts/activate.csh.tmpl delete mode 100644 shell-scripts/activate.sh.tmpl diff --git a/.github/actions/setup-environment/action.yml b/.github/actions/setup-environment/action.yml index babe1ccae..22ada7fd2 100644 --- a/.github/actions/setup-environment/action.yml +++ b/.github/actions/setup-environment/action.yml @@ -65,6 +65,7 @@ runs: ./eden config set default --key=eve.accel --value=false ./eden config set default --key=eve.firmware --value="$(pwd)/firmware/OVMF_CODE.fd $(pwd)/firmware/OVMF_VARS.fd" fi + ./dist/bin/eden+ports.sh 2223:2223 2224:2224 5912:5902 5911:5901 8027:8027 8028:8028 8029:8029 8030:8030 8031:8031 ./eden config set default --key=eve.tpm --value=${{ inputs.tpm_enabled }} ./eden config set default --key=eve.cpu --value=2 shell: bash @@ -116,3 +117,10 @@ runs: ./eden setup -v debug --grub-options='set_global dom0_extra_args "$dom0_extra_args eve_install_zfs_with_raid_level "' shell: bash working-directory: "./eden" + + - name: Start and Onboard + run: | + ./eden start -v debug + ./eden eve onboard -v debug + shell: bash + working-directory: "./eden" diff --git a/cmd/certs.go b/cmd/certs.go index 98a8fc2f7..f3e98c2af 100644 --- a/cmd/certs.go +++ b/cmd/certs.go @@ -20,7 +20,7 @@ func newCertsCmd(cfg *openevec.EdenSetupArgs) *cobra.Command { Short: "manage certs", Long: `Managed certificates for Adam and EVE.`, Run: func(cmd *cobra.Command, args []string) { - 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.Password, grubOptions, cfg.Adam.APIv1); err != nil { + 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, cfg.Eve.Password, grubOptions, cfg.Adam.APIv1); err != nil { log.Errorf("cannot GenerateEveCerts: %s", err) } else { log.Info("GenerateEveCerts done") diff --git a/cmd/edenConfig.go b/cmd/edenConfig.go index 88014793b..ccbf83fb9 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,14 @@ import ( ) func newConfigCmd(configName, verbosity *string) *cobra.Command { - cfg := &openevec.EdenSetupArgs{} + currentPath, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + cfg, err := openevec.GetDefaultConfig(currentPath) + if err != nil { + log.Fatalf("Failed to generate default config %v\n", err) + } var configCmd = &cobra.Command{ Use: "config", Short: "work with config", @@ -73,13 +81,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..7406d2b5d 100644 --- a/cmd/edenSetup.go +++ b/cmd/edenSetup.go @@ -23,9 +23,6 @@ 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.SetupEden(*configName, configDir, softSerial, zedControlURL, ipxeOverride, grubOptions, netboot, installer); err != nil { log.Fatalf("Setup eden failed: %s", err) diff --git a/cmd/edenStop.go b/cmd/edenStop.go index 6e3c74f32..7fa22ece0 100644 --- a/cmd/edenStop.go +++ b/cmd/edenStop.go @@ -27,7 +27,7 @@ func newStopCmd(configName, verbosity *string) *cobra.Command { registryRm, eServerRm, cfg.Eve.Remote, cfg.Eve.Pid, swtpmPidFile(cfg), cfg.Sdn.PidFile, - cfg.Eve.DevModel, vmName, + cfg.Eve.DevModel, vmName, cfg.Sdn.Disable, ) }, } diff --git a/cmd/edenTest.go b/cmd/edenTest.go index bcf94d154..172910de8 100644 --- a/cmd/edenTest.go +++ b/cmd/edenTest.go @@ -6,7 +6,6 @@ import ( "github.com/lf-edge/eden/pkg/defaults" "github.com/lf-edge/eden/pkg/openevec" - "github.com/lf-edge/eden/pkg/utils" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -42,7 +41,7 @@ test -r [-t ] [-v ] } } - vars, err := utils.InitVars() + vars, err := openevec.InitVarsFromConfig(cfg) if err != nil { return fmt.Errorf("error reading config: %s\n", err) diff --git a/docs/design-decisions.md b/docs/design-decisions.md new file mode 100644 index 000000000..ea055543a --- /dev/null +++ b/docs/design-decisions.md @@ -0,0 +1,73 @@ +# Design decisions + +This document is a collection of high-level decisions, that have been made in the project. Why do we have a section about how things use to be? In case if we want to revisit certain concept it will be useful to know why we chose one approach over another. + +## Using Domain Specific Language (DSL) vs writing native golang tests + +When talking about Eden tests till version 0.9.12, we are talking about `escript`, a Domain Specific Language (DSL) which describes test case and uses Eden to setup, environment. Escript looks like this + +```bash +# 1 Setup environment variables +{{$port := "2223"}} +{{$network_name := "n1"}} +{{$app_name := "eclient"}} + +# ... + +# 2 run eden commands +eden -t 1m network create 10.11.12.0/24 -n {{$network_name}} + +# 3 run escript commands +test eden.network.test -test.v -timewait 10m ACTIVATED {{$network_name}} + +eden pod deploy -n {{$app_name}} --memory=512MB {{template "eclient_image"}} -p {{$port}}:22 --networks={{$network_name}} + +# 4 execute shell script which are defined inside escript file +exec -t 5m bash ssh.sh +stdout 'Ubuntu' + +# 5 overwrite configuration +-- eden-config.yml -- +{{/* Test's config. file */}} +test: + controller: adam://{{EdenConfig "adam.ip"}}:{{EdenConfig "adam.port"}} + eve: + {{EdenConfig "eve.name"}}: + onboard-cert: {{EdenConfigPath "eve.cert"}} + serial: "{{EdenConfig "eve.serial"}}" + model: {{EdenConfig "eve.devmodel"}} + +-- ssh.sh -- +EDEN={{EdenConfig "eden.root"}}/{{EdenConfig "eden.bin-dist"}}/{{EdenConfig "eden.eden-bin"}} +for i in `seq 20` +do + sleep 20 + # Test SSH-access to container + echo $i\) $EDEN sdn fwd eth0 {{$port}} -- {{template "ssh"}} grep Ubuntu /etc/issue + $EDEN sdn fwd eth0 {{$port}} -- {{template "ssh"}} grep Ubuntu /etc/issue && break +done +``` + +So you can + +1) setup some environment variables +2) run eden commands +3) run escript commands with test +4) execute user-defined shell scripts +5) overwrite eden configuration + +Escript file is fed as input to eden test command, which parses the variables using golang templates to substitute some variables using templates in golang, then it's going to read line by line and execute it via os.exec call in golang. test is actually a compiled golang binary, we compile it inside eden repo and then execute it, under the hood it is a manually forked version of standard golang test package with some added commands. + +For more information on specific topics refer to following documents: + +- Escript test structure [doc](./escript/test-anatomy-sample.md) +- Writing eden tasks for escript [doc](./escript/task-writing.md) +- Running tests [doc](./escript/test-running.md) + +So there are several problems with that approach: + +**You have to be an escript expert**: when new person comes to a project, they will have some industry skills, like C++ expertise, Rust, Golang, Computer Networking, etc. but if you never worked on this project, you never heard about escript, its' sole purpose was to be part of Eden and be useful language to describe tests for Eden. One might argue, but that's just bash on steroids. Well, true, but do you know what `!` means? It's not equal parameter in escript. So it is like bash, but not exactly it, and it might take time to figure out other hidden features, that could be solved by proper documentation. But before spiraling down that conversation lets think about tools? Imagine, that you wrote new fancy eden test and it doesn't work. Usual thing, you would say. Test Driven Development is all about writing failing tests first and then making them work. + +**But how do you debug that test?** Because you're running an interpreter, which runs bash commands via os.exec plus you execute golang program, which is written and compiled inside the repository. Debug printing would work, but I find debuggers much more useful and efficient most of the times. It is a matter of taste, but better to debugger at your disposal, than not to have it in this case. Even worse, how do you debug escript problem? You need to know it's internals, there's no Stackoverflow or Google by your side, nor there are books about it. + +**Bumping golang is actually manual action**: last but not least, we have to maintain custom test files which a basically copy-paste with one added function, bumping golang turns into some weird dances in that case. diff --git a/docs/task-writing.md b/docs/escript/task-writing.md similarity index 100% rename from docs/task-writing.md rename to docs/escript/task-writing.md diff --git a/docs/test-anatomy-sample.md b/docs/escript/test-anatomy-sample.md similarity index 100% rename from docs/test-anatomy-sample.md rename to docs/escript/test-anatomy-sample.md diff --git a/docs/test-running.md b/docs/escript/test-running.md similarity index 100% rename from docs/test-running.md rename to docs/escript/test-running.md diff --git a/docs/writing-tests.md b/docs/writing-tests.md new file mode 100644 index 000000000..127685d26 --- /dev/null +++ b/docs/writing-tests.md @@ -0,0 +1,98 @@ +# How to write tests with Eden + +The eden golang test SDK for eve consists of two categories of functions: + +1. `openevec`: infrastructure manager. `openevec` enables you to deploy a controller, a backend for that controller, deploy an edge node, etc. It does not change anything on an individual edge node. +2. `evetestkit`: provides useful collection of functions to describe expected state of the system (controller, EVE, AppInstances) + +And those functions are used in context of standard golang test library. We do a setup in TestMain (for more info check [this](https://pkg.go.dev/testing#hdr-Main)) and then write test functions which interact with the environment we created. Setting up environment takes couple of minutes, so it makes sense to do it once and run tests within that environment. + +Source for the example below can be found [here](../tests/sec/sec_test.go) + +In order to test a feature in Eden you need to + +## 1. Create a configuration file and describe your environment + +The glue between `openevec` and `evetestkit` is a configuration structure called `EdenSetupArgs`. It contains all the necessary information to setup all the components of the test: controller, EVE, etc. You can fill in the structure manually, however `openevec` provides you with a convenient function to create default configuration structure providing project root path: + +```go +// ... +func TestMain(m *testing.M) { + 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) + } + // ... +} +``` + +Due to backward compatibility with escript as of time of writing, the project root path should be Eden repository folder. So if you add tests in the `eden/tests/my-awesome-test` you need to go two levels up, will be removed with escript +Also we need to write configuration file to file system, because there are components (like changer) which read configuration from file system, will be removed with escript + +## 2. Initialize `openevec` Setup, Start and Onboard EVE node + +When configuration you need `openevec` to create all needed certificates, start backend. For that we create `openevec` object based on a configuration provided, it is just a convinient wrapper. + +```go +// ... +func TestMain(m *testing.M) { + // ... + evec := openvec.CreateOpenEVEC(cfg) + + evec.SetupEden(/* ... */) + evec.StartEden(/* ... */) + evec.OnboardEve(/* ... */) + + // ... +} +``` + +## 3. Initialize `evetestkit` and run test suite + +`evetestkit` provides an abstraction over EveNode, which is used to describe expected state of the system. Each EveNode is running within a project You can create a global object within one test file to use it across multiple tests. Note that EveNode is not threadsafe, since controller is stateful, so tests should be run consequently (no t.Parallel()) + +```go +const projectName = "security-test" +var eveNode *evetestkit.EveNode + +func TestMain(m *testing.M) { + // ... + node, err := evetestkit.InitializeTestFromConfig(projectName, cfg, evetestkit.WithControllerVerbosity("debug")) + if err != nil { + log.Fatalf("Failed to initialize test: %v", err) + } + + eveNode = node + res := m.Run() + os.Exit(res) +} +``` + +## 4. Write your test + +Below is an example of test, which check if AppArmor is enabled (specific file on EVE exists). It uses `EveReadFile` function from `evetestkit` + +```go +const appArmorStatus = "/sys/module/apparmor/parameters/enabled" +// ... +func TestAppArmorEnabled(t *testing.T) { + log.Println("TestAppArmorEnabled started") + defer log.Println("TestAppArmorEnabled finished") + + out, err := eveNode.EveReadFile(appArmorStatus) + if err != nil { + t.Fatal(err) + } + + exits := strings.TrimSpace(string(out)) + if exits != "Y" { + t.Fatal("AppArmor is not enabled") + } +``` 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/eden/eden.go b/pkg/eden/eden.go index 40e0f72ce..086bc0b9b 100644 --- a/pkg/eden/eden.go +++ b/pkg/eden/eden.go @@ -385,7 +385,7 @@ func StatusEServer() (status string, err error) { } // GenerateEveCerts function generates certs for EVE -func GenerateEveCerts(certsDir, domain, ip, eveIP, uuid, devModel, ssid, password string, grubOptions []string, apiV1 bool) (err error) { +func GenerateEveCerts(certsDir, domain, ip, eveIP, uuid, devModel, ssid, arch, password string, grubOptions []string, apiV1 bool) (err error) { model, err := models.GetDevModelByName(devModel) if err != nil { return fmt.Errorf("GenerateEveCerts: %s", err) @@ -495,7 +495,7 @@ func GenerateEveCerts(certsDir, domain, ip, eveIP, uuid, devModel, ssid, passwor } } } - if model.DevModelType() == defaults.DefaultQemuModel && viper.GetString("eve.arch") == "arm64" { + if model.DevModelType() == defaults.DefaultQemuModel && arch == "arm64" { // we need to properly set console for qemu arm64 grubOptions = append(grubOptions, "set_global dom0_console \"console=ttyAMA0,115200 $dom0_console\"") } @@ -523,7 +523,7 @@ func GenerateEveCerts(certsDir, domain, ip, eveIP, uuid, devModel, ssid, passwor } // PutEveCerts function put certs for zedcontrol -func PutEveCerts(certsDir, devModel, ssid, password string) (err error) { +func PutEveCerts(certsDir, devModel, ssid, arch, password string) (err error) { model, err := models.GetDevModelByName(devModel) if err != nil { return fmt.Errorf("GenerateEveCerts: %s", err) @@ -591,7 +591,7 @@ func PutEveCerts(certsDir, devModel, ssid, password string) (err error) { } } } - if model.DevModelType() == defaults.DefaultQemuModel && viper.GetString("eve.arch") == "arm64" { + if model.DevModelType() == defaults.DefaultQemuModel && arch == "arm64" { // we need to properly set console for qemu arm64 if err := os.WriteFile(filepath.Join(certsDir, "grub.cfg"), []byte("set_global dom0_console \"console=ttyAMA0,115200 $dom0_console\""), 0666); err != nil { return fmt.Errorf("GenerateEveCerts: %s", err) @@ -754,7 +754,7 @@ func MakeEveInRepo(desc utils.EVEDescription, dist string) (image, additional st } // CleanContext cleanup only context data -func CleanContext(eveDist, certsDist, imagesDist, evePID, eveUUID, sdnPID, vmName string, configSaved string, remote bool) (err error) { +func CleanContext(eveDist, certsDist, imagesDist, evePID, eveUUID, sdnPID, vmName string, configSaved string, remote, sdnDisable bool) (err error) { edenDir, err := utils.DefaultEdenDir() if err != nil { return fmt.Errorf("CleanContext: %s", err) @@ -828,7 +828,7 @@ func CleanContext(eveDist, certsDist, imagesDist, evePID, eveUUID, sdnPID, vmNam log.Infof("swtpm is stopping") } } - StopSDN(devModel, sdnPID) + StopSDN(devModel, sdnPID, sdnDisable) } if _, err = os.Stat(eveDist); !os.IsNotExist(err) { if err = os.RemoveAll(eveDist); err != nil { @@ -855,7 +855,7 @@ func CleanContext(eveDist, certsDist, imagesDist, evePID, eveUUID, sdnPID, vmNam // StopEden teardown Eden func StopEden(adamRm, redisRm, registryRm, eserverRm, eveRemote bool, - evePidFile, swtpmPidFile, sdnPidFile, devModel, vmName string) { + evePidFile, swtpmPidFile, sdnPidFile, devModel, vmName string, sdnDisable bool) { if err := StopAdam(adamRm); err != nil { log.Infof("cannot stop adam: %s", err) } else { @@ -877,12 +877,12 @@ func StopEden(adamRm, redisRm, registryRm, eserverRm, eveRemote bool, log.Infof("eserver stopped") } if !eveRemote { - StopEve(evePidFile, swtpmPidFile, sdnPidFile, devModel, vmName) + StopEve(evePidFile, swtpmPidFile, sdnPidFile, devModel, vmName, sdnDisable) } } // StopEve stops EVE, vTPM and SDN. -func StopEve(evePidFile, swtpmPidFile, sdnPidFile, devModel, vmName string) { +func StopEve(evePidFile, swtpmPidFile, sdnPidFile, devModel, vmName string, sdnDisable bool) { if devModel == defaults.DefaultVBoxModel { if err := StopEVEVBox(vmName); err != nil { log.Infof("cannot stop EVE: %s", err) @@ -910,11 +910,11 @@ func StopEve(evePidFile, swtpmPidFile, sdnPidFile, devModel, vmName string) { } } } - StopSDN(devModel, sdnPidFile) + StopSDN(devModel, sdnPidFile, sdnDisable) } -func StopSDN(devModel, sdnPidFile string) { - if devModel != defaults.DefaultQemuModel || viper.GetBool("sdn.disable") { +func StopSDN(devModel, sdnPidFile string, sdnDisable bool) { + if devModel != defaults.DefaultQemuModel || sdnDisable { // SDN is not running, nothing to do return } @@ -937,11 +937,11 @@ func StopSDN(devModel, sdnPidFile string) { // CleanEden teardown Eden and cleanup func CleanEden(eveDist, adamDist, certsDist, imagesDist, eserverDist, redisDist, registryDist, configDist, evePID, sdnPID, configSaved string, remote bool, - devModel, vmName string) (err error) { + devModel, vmName string, sdnDisable bool) (err error) { command := "swtpm" swtpmPidFile := filepath.Join(imagesDist, fmt.Sprintf("%s.pid", command)) StopEden(true, true, true, true, remote, - evePID, swtpmPidFile, sdnPID, devModel, vmName) + evePID, swtpmPidFile, sdnPID, devModel, vmName, sdnDisable) if _, err = os.Stat(eveDist); !os.IsNotExist(err) { if err = os.RemoveAll(eveDist); err != nil { return fmt.Errorf("1 CleanEden: error in %s delete: %s", eveDist, err) 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 8d4dd522f..43802f6c0 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) @@ -636,7 +639,7 @@ func GetDefaultVMConfig(appName, cloudConfig string, portPub []string) openevec. // WithControllerVerbosity sets the verbosity level of the controller, // possible values are: panic, fatal, error, debug, info, trace, warn -// This is an option for InitilizeTest. +// This is an option for InitializeTest. func WithControllerVerbosity(verbosity string) TestOption { return func() { controllerVerbosiry = verbosity @@ -644,7 +647,7 @@ func WithControllerVerbosity(verbosity string) TestOption { } // WithEdenConfigEnv sets the environment variable that holds the path to the -// eden configuration file. This is an option for InitilizeTest. +// eden configuration file. This is an option for InitializeTest. func WithEdenConfigEnv(env string) TestOption { return func() { edenConfEnv = env @@ -657,14 +660,14 @@ func GetRandomAppName(prefix string) string { return prefix + namesgenerator.GetRandomName(rnd.Intn(1)) } -// InitilizeTest is used to provide setup and teardown for the rest of the +// InitializeTest is used to provide setup and teardown for the rest of the // tests. As part of setup we make sure that context has a slice of // EVE instances that we can operate on. It grabs the first one in the slice // for running tests. -func InitilizeTest(projectName string, options ...TestOption) (*EveNode, error) { +func InitializeTest(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())) @@ -729,3 +732,120 @@ 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 InitializeTestFromConfig(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) + } + + 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..3a0926d1f 100644 --- a/pkg/openevec/changers.go +++ b/pkg/openevec/changers.go @@ -8,7 +8,7 @@ import ( "github.com/lf-edge/eden/pkg/controller" "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 +28,7 @@ func changerByControllerMode(controllerMode string) (configChanger, error) { if controllerMode == "" { return &adamChanger{}, nil } - modeType, modeURL, err := projects.GetControllerMode(controllerMode) + modeType, modeURL, err := utils.GetControllerMode(controllerMode) if err != nil { return nil, err } diff --git a/pkg/openevec/config.go b/pkg/openevec/config.go index 5a8f0194b..f6e393b31 100644 --- a/pkg/openevec/config.go +++ b/pkg/openevec/config.go @@ -2,9 +2,9 @@ package openevec import ( "fmt" + "io" "os" "path" - "path/filepath" "reflect" "strings" @@ -21,6 +21,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 +43,12 @@ type EdenConfig struct { EdenBin string `mapstructure:"eden-bin"` TestBin string `mapstructure:"test-bin"` TestScenario string `mapstructure:"test-scenario"` + Tests string `mapstructure:"tests"` EServer EServerConfig `mapstructure:"eserver"` - Images ImagesConfig `mapstructure:"images"` + EClient EClientConfig `mapstructure:"eclient"` + Images ImagesConfig `mapstructure:"images"` } type RedisConfig struct { @@ -73,6 +81,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 +103,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 +122,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"` @@ -125,8 +133,8 @@ type EveConfig struct { Remote bool `mapstructure:"remote"` RemoteAddr string `mapstructure:"remote-addr"` ModelFile string `mapstructure:"devmodelfile" cobraflag:"devmodel-file"` - Cert string `mapstructure:"cert"` - DeviceCert string `mapstructure:"device-cert"` + Cert string `mapstructure:"cert" resolvepath:""` + DeviceCert string `mapstructure:"device-cert" resolvepath:""` Name string `mapstructure:"name"` AdamLogLevel string `mapstructure:"adam-log-level"` LogLevel string `mapstructure:"log-level"` @@ -152,7 +160,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 +187,7 @@ type EdenSetupArgs struct { ConfigFile string ConfigName string + EdenDir string } // PodConfig store configuration for Pod deployment @@ -284,7 +293,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,60 +309,107 @@ 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) } } } -func ConfigCheck(configName string) error { - configFile := utils.GetConfig(configName) - configSaved := utils.ResolveAbsPath(fmt.Sprintf("%s-%s", configName, defaults.DefaultConfigSaved)) +func getValStrRepr(v reflect.Value) string { + if v.Kind() == reflect.String { + return fmt.Sprintf("'%v'", v.Interface()) + } else { + return fmt.Sprintf("%v", v.Interface()) + } +} - abs, err := filepath.Abs(configSaved) - if err != nil { - return fmt.Errorf("fail in reading filepath: %s\n", err.Error()) +func WriteConfig(dst reflect.Value, root string, writer io.Writer, nestLevel int) { + if dst.Kind() == reflect.Ptr { + dst = dst.Elem() } - if _, err = os.Lstat(abs); os.IsNotExist(err) { - if err = utils.CopyFile(configFile, abs); err != nil { - return fmt.Errorf("copying fail %s\n", err.Error()) + for i := 0; i < dst.NumField(); i++ { + if structTag := dst.Type().Field(i).Tag.Get("mapstructure"); structTag != "" { + io.WriteString(writer, strings.Repeat(" ", nestLevel)) + f := dst.Field(i) + fieldType := dst.Type().Field(i) + + switch f.Kind() { + case reflect.Struct: + io.WriteString(writer, structTag+":\n") + // Pass the addressable value of the struct if it can be set, else create a pointer and pass + if f.CanAddr() { + WriteConfig(f.Addr(), root, writer, nestLevel+1) + } else { + WriteConfig(f, root, writer, nestLevel+1) + } + case reflect.Map: + io.WriteString(writer, structTag+":\n") + iter := f.MapRange() + for iter.Next() { + k := iter.Key() + v := iter.Value() + io.WriteString(writer, strings.Repeat(" ", nestLevel+1)) + io.WriteString(writer, fmt.Sprintf("%v: %s\n", k.Interface(), getValStrRepr(v))) + } + case reflect.Slice: + io.WriteString(writer, structTag+":\n") + for j := 0; j < f.Len(); j++ { + io.WriteString(writer, strings.Repeat(" ", nestLevel+1)) + elem := f.Index(j) + io.WriteString(writer, fmt.Sprintf("- %v\n", getValStrRepr(elem))) + } + case reflect.String: + val := f.String() + if _, ok := fieldType.Tag.Lookup("resolvepath"); ok { + val = strings.TrimPrefix(val, root+"/") + } + io.WriteString(writer, fmt.Sprintf("%s: '%v'\n", structTag, val)) + default: + io.WriteString(writer, fmt.Sprintf("%s: %v\n", structTag, f.Interface())) + } } - } else { + } +} + +func PrintDifferences(a, b interface{}, parentField string) { + valA := reflect.ValueOf(a) + valB := reflect.ValueOf(b) - viperLoaded, err := utils.LoadConfigFile(abs) - if err != nil { - return fmt.Errorf("error reading config %s: %s\n", abs, err.Error()) + 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()) } - if viperLoaded { - confOld := viper.AllSettings() + return + } - if _, err = utils.LoadConfigFile(configFile); err != nil { - return fmt.Errorf("error reading config %s: %s", configFile, err.Error()) - } + typeA := valA.Type() - confCur := viper.AllSettings() + for i := 0; i < valA.NumField(); i++ { + fieldName := typeA.Field(i).Name + fieldValA := valA.Field(i) + fieldValB := valB.Field(i) - if reflect.DeepEqual(confOld, confCur) { - log.Infof("Config file %s is the same as %s\n", configFile, configSaved) - } else { - return fmt.Errorf("the current configuration file %s is different from the saved %s. You can fix this with the commands 'eden config clean' and 'eden config add/set/edit'.\n", configFile, abs) - } + fullFieldName := fieldName + if parentField != "" { + fullFieldName = parentField + "." + fieldName + } + + if fieldValA.Kind() == reflect.Struct { + PrintDifferences(fieldValA.Interface(), fieldValB.Interface(), fullFieldName) } else { - /* Incorrect saved config -- just rewrite by current */ - if err = utils.CopyFile(configFile, abs); err != nil { - return fmt.Errorf("copying fail %s\n", err.Error()) + if fieldValA.Interface() != fieldValB.Interface() { + fmt.Printf("Field %s differs: %v vs %v \n", fullFieldName, fieldValA.Interface(), fieldValB.Interface()) } } } - return nil } diff --git a/pkg/openevec/config_test.go b/pkg/openevec/config_test.go new file mode 100644 index 000000000..315ecddce --- /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..c1f8a7c84 100644 --- a/pkg/openevec/defaults.go +++ b/pkg/openevec/defaults.go @@ -1,97 +1,227 @@ 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(projectRootPath string) (*EdenSetupArgs, error) { + ip, err := utils.GetIPForDockerAccess() + if err != nil { + return nil, err + } + + edenDir, err := utils.DefaultEdenDir() + if err != nil { + return nil, err + } - currentPath, err := os.Getwd() + id, err := uuid.NewV4() if err != nil { - log.Fatal(err) + return nil, err + } + + imageDist := filepath.Join(projectRootPath, defaults.DefaultDist, fmt.Sprintf("%s-%s", defaults.DefaultContext, defaults.DefaultImageDist)) + certsDist := filepath.Join(projectRootPath, 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(projectRootPath, defaults.DefaultDist), + Tests: filepath.Join(projectRootPath, defaults.DefaultDist, "tests"), + Download: true, + BinDir: defaults.DefaultBinDist, + SSHKey: filepath.Join(projectRootPath, 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, + 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", + "2223": "2223", + "2224": "2224", + "5911": "5901", + "5912": "5902", + "8027": "8027", + "8028": "8028", + "8029": "8029", + "8030": "8030", + "8031": "8031", }, - 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, + 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(projectRootPath, defaults.DefaultDist, fmt.Sprintf("%s-eve.pid", strings.ToLower(defaults.DefaultContext))), + Log: filepath.Join(projectRootPath, 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, + ConsoleLogFile: filepath.Join(projectRootPath, defaults.DefaultDist, "sdn-console.log"), + Disable: true, TelnetPort: defaults.DefaultSdnTelnetPort, MgmtPort: defaults.DefaultSdnMgmtPort, - PidFile: filepath.Join(currentPath, defaults.DefaultDist, "sdn.pid"), + PidFile: filepath.Join(projectRootPath, defaults.DefaultDist, "sdn.pid"), SSHPort: defaults.DefaultSdnSSHPort, + SourceDir: filepath.Join(projectRootPath, "sdn"), + ConfigDir: filepath.Join(edenDir, fmt.Sprintf("%s-sdn", "default")), + ImageFile: filepath.Join(imageDist, "eden", "sdn-efi.qcow2"), + LinuxkitBin: filepath.Join(projectRootPath, defaults.DefaultBuildtoolsDir, "linuxkit"), + NetModelFile: "", + }, + + Gcp: GcpConfig{ + Key: "", + }, + + Packet: PacketConfig{ + Key: "", }, ConfigName: defaults.DefaultContext, ConfigFile: utils.GetConfig(defaults.DefaultContext), + EdenDir: edenDir, + } + + return defaultEdenConfig, nil +} + +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 defaultEdenConfig + return dpc } diff --git a/pkg/openevec/eden.go b/pkg/openevec/eden.go index 3ae55b73f..ea0f7b649 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 := viper.GetString("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 } @@ -376,12 +381,12 @@ func setupConfigDir(cfg EdenSetupArgs, eveConfigDir, softSerial, zedControlURL s } 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, wifiPSK, grubOptions, cfg.Adam.APIv1); err != nil { + 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, wifiPSK); err != nil { + 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") @@ -513,13 +518,13 @@ func (openEVEC *OpenEVEC) EdenClean(configName, configDist, vmName string, curre log.Infof("Adam is running and accessible on port %d", cfg.Adam.Port) } if err := eden.CleanContext(cfg.Eve.Dist, cfg.Eden.CertsDir, filepath.Dir(cfg.Eve.ImageFile), cfg.Eve.Pid, cfg.Eve.CertsUUID, - cfg.Sdn.PidFile, vmName, configSaved, cfg.Eve.Remote); err != nil { + cfg.Sdn.PidFile, vmName, configSaved, cfg.Eve.Remote, cfg.Sdn.Disable); err != nil { return fmt.Errorf("cannot CleanContext: %w", err) } } else { if err := eden.CleanEden(cfg.Eve.Dist, cfg.Adam.Dist, cfg.Eden.CertsDir, filepath.Dir(cfg.Eve.ImageFile), cfg.Eden.Images.EServerImageDist, cfg.Redis.Dist, cfg.Registry.Dist, configDist, cfg.Eve.Pid, - cfg.Sdn.PidFile, configSaved, cfg.Eve.Remote, cfg.Eve.DevModel, vmName); err != nil { + cfg.Sdn.PidFile, configSaved, cfg.Eve.Remote, cfg.Eve.DevModel, vmName, cfg.Sdn.Disable); err != nil { return fmt.Errorf("cannot CleanEden: %w", err) } } @@ -829,7 +834,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..87f7843d5 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,6 @@ 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 - } context, err := utils.ContextLoad() if err != nil { @@ -112,12 +104,6 @@ func ConfigAdd(cfg *EdenSetupArgs, currentContext, contextFile string, force boo } } context.SetContext(context.Current) - if err := ReloadConfigDetails(cfg); err != nil { - return err - } - - // we prepare viper config here from EdenSetupArgs - // to feed into GenerateConfigFileFromViper if cfg.Eve.Arch != "" { viper.Set("eve.arch", cfg.Eve.Arch) @@ -138,9 +124,19 @@ 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 %v", err) } + defer file.Close() + + WriteConfig(reflect.ValueOf(cfg), cfg.Eden.Root, file, 0) + context.SetContext(currentContextName) return nil diff --git a/pkg/openevec/eve.go b/pkg/openevec/eve.go index 0287617e6..d7320d985 100644 --- a/pkg/openevec/eve.go +++ b/pkg/openevec/eve.go @@ -213,7 +213,7 @@ func (openEVEC *OpenEVEC) StopEve(vmName string) error { } } } - eden.StopSDN(cfg.Eve.DevModel, cfg.Sdn.PidFile) + eden.StopSDN(cfg.Eve.DevModel, cfg.Sdn.PidFile, cfg.Sdn.Disable) return nil } diff --git a/pkg/openevec/start.go b/pkg/openevec/start.go index ab380f240..6d8f99aed 100644 --- a/pkg/openevec/start.go +++ b/pkg/openevec/start.go @@ -6,7 +6,6 @@ import ( "github.com/lf-edge/eden/pkg/eden" log "github.com/sirupsen/logrus" - "github.com/spf13/viper" ) func (openEVEC *OpenEVEC) StartAdam() error { @@ -28,15 +27,6 @@ func (openEVEC *OpenEVEC) StartAdam() error { return nil } -func stopAdam(_ string) error { - adamRm := viper.GetBool("adam-rm") - - if err := eden.StopAdam(adamRm); err != nil { - return fmt.Errorf("cannot stop adam: %w", err) - } - return nil -} - func (openEVEC *OpenEVEC) GetAdamStatus() (string, error) { statusAdam, err := eden.StatusAdam() if err != nil { diff --git a/pkg/openevec/test.go b/pkg/openevec/test.go index 62958e6ec..00a5dcb55 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 @@ -95,7 +95,6 @@ func InitVarsFromConfig(cfg *EdenSetupArgs) (*utils.ConfigVars, error) { } func Test(tstCfg *TestArgs) error { - switch { case tstCfg.TestList != "": tests.RunTest(tstCfg.TestProg, []string{"-test.list", tstCfg.TestList}, "", tstCfg.TestTimeout, tstCfg.FailScenario, tstCfg.ConfigFile, tstCfg.Verbosity) 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..2a91db426 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")) 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..7da92f4f0 100644 --- a/pkg/utils/params.go +++ b/pkg/utils/params.go @@ -1,12 +1,32 @@ package utils import ( + "fmt" "math/rand" "regexp" "strings" "time" + + "github.com/lf-edge/eden/pkg/defaults" ) +// GetControllerMode parse url with controller +func GetControllerMode(controllerMode string) (modeType, modeURL string, err error) { + + params := 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 +} + // GetParams parse line with regexp into map func GetParams(line, regEx string) (paramsMap map[string]string) { diff --git a/shell-scripts/activate.csh.tmpl b/shell-scripts/activate.csh.tmpl deleted file mode 100644 index 84a2a899a..000000000 --- a/shell-scripts/activate.csh.tmpl +++ /dev/null @@ -1,48 +0,0 @@ -# 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/shell-scripts/activate.sh.tmpl b/shell-scripts/activate.sh.tmpl deleted file mode 100644 index c2446c915..000000000 --- a/shell-scripts/activate.sh.tmpl +++ /dev/null @@ -1,97 +0,0 @@ -# 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 diff --git a/tests/README.md b/tests/README.md index 7d959e304..eb3eae835 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,7 +1,7 @@ # Eden Integration Tests This directory contains a series of integration tests that meet the -[eden task API](../docs/task-writing.md), and thus can be launched using +[eden task API](../docs/escript/task-writing.md), and thus can be launched using `eden test`. Each subdirectory contains an individual test suite with one or more tests. @@ -25,7 +25,7 @@ To run the entire suite of integration tests, run either: ### Running Individual Tests To run any single suite of integration tests, launch them like any other -[eden test/task](../docs/test-running.md): +[eden test/task](../docs/escript/test-running.md): ```console eden test tests/testdir/ 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/escript/go-internal/testscript/testing_1.18.go b/tests/escript/go-internal/testscript/testing_1.18.go index f9d9a4a07..29a6214e9 100644 --- a/tests/escript/go-internal/testscript/testing_1.18.go +++ b/tests/escript/go-internal/testscript/testing_1.18.go @@ -24,10 +24,31 @@ type corpusEntry = struct { IsSeed bool } -func (d nopTestDeps) CoordinateFuzzing(_ time.Duration, _ int64, _ time.Duration, _ int64, _ int, _ []corpusEntry, _ []reflect.Type, _ string, _ string) error { +func (nopTestDeps) SetPanicOnExit0(_ bool) {} + +func (nopTestDeps) MatchString(_, _ string) (result bool, err error) { + return false, nil +} + +func (nopTestDeps) StartCPUProfile(_ io.Writer) error { return nil } +func (nopTestDeps) StopCPUProfile() {} + +func (nopTestDeps) StartTestLog(_ io.Writer) {} + +func (nopTestDeps) StopTestLog() error { + return nil +} + +func (nopTestDeps) WriteProfileTo(_ string, _ io.Writer, _ int) error { + return nil +} + +func (d nopTestDeps) CoordinateFuzzing(_ time.Duration, _ int64, _ time.Duration, _ int64, _ int, _ []corpusEntry, _ []reflect.Type, _ string, _ string) error { + return nil +} func (d nopTestDeps) RunFuzzWorker(_ func(corpusEntry) error) error { return nil } @@ -48,29 +69,15 @@ func (d nopTestDeps) SnapshotCoverage() { return } -func (nopTestDeps) SetPanicOnExit0(_ bool) {} - -func (nopTestDeps) MatchString(_, _ string) (result bool, err error) { - return false, nil -} - -func (nopTestDeps) StartCPUProfile(_ io.Writer) error { - return nil +func (d nopTestDeps) InitRuntimeCoverage() (mode string, tearDown func(coverprofile string, gocoverdir string) (string, error), snapcov func() float64) { + tmp := func(_, _ string) (string, error) { return "", nil } + snapc := func() float64 { return 0 } + return "", tmp, snapc } -func (nopTestDeps) StopCPUProfile() {} - -func (nopTestDeps) WriteProfileTo(_ string, _ io.Writer, _ int) error { - return nil -} func (nopTestDeps) ImportPath() string { return "" } -func (nopTestDeps) StartTestLog(_ io.Writer) {} - -func (nopTestDeps) StopTestLog() error { - return nil -} func getTestingMain() *testing.M { return testing.MainStart(nopTestDeps{}, nil, nil, nil, nil) 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..1a3f13768 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()) @@ -111,7 +111,7 @@ func checkState(eveState *eve.State, state string, netNames []string) error { } // checkNet wait for info of ZInfoApp type with state -func checkNet(state string, volNames []string) projects.ProcInfoFunc { +func checkNet(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) 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..440ecea00 100644 --- a/tests/sec/sec_test.go +++ b/tests/sec/sec_test.go @@ -2,10 +2,13 @@ package sec_test import ( "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 +21,34 @@ 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, err := openevec.GetDefaultConfig(twoLevelsUp) + if err != nil { + log.Fatalf("Failed to generate default config %v\n", err) + } + + 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) + } + + node, err := tk.InitializeTestFromConfig(projectName, cfg, tk.WithControllerVerbosity("debug")) if err != nil { log.Fatalf("Failed to initialize test: %v", err) } diff --git a/tests/vcom/vcom_test.go b/tests/vcom/vcom_test.go index 704baa532..d6bd1dbf6 100644 --- a/tests/vcom/vcom_test.go +++ b/tests/vcom/vcom_test.go @@ -82,7 +82,7 @@ func TestMain(m *testing.M) { logInfof("VCOM Test started") defer logInfof("VCOM Test finished") - node, err := tk.InitilizeTest(projectName, tk.WithControllerVerbosity("debug")) + node, err := tk.InitializeTest(projectName, tk.WithControllerVerbosity("debug")) if err != nil { logFatalf("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) diff --git a/tests/workflow/eve-upgrade.tests.txt b/tests/workflow/eve-upgrade.tests.txt index 10053c1bb..77008bb7e 100644 --- a/tests/workflow/eve-upgrade.tests.txt +++ b/tests/workflow/eve-upgrade.tests.txt @@ -1,7 +1,7 @@ # Number of tests {{$tests := 6}} # EDEN_TEST_SETUP env. var. -- "y"(default) performs the EDEN setup steps -{{$setup := "y"}} +{{$setup := "n"}} {{$setup_env := EdenGetEnv "EDEN_TEST_SETUP"}} {{if $setup_env}}{{$setup = $setup_env}}{{end}} # EDEN_TEST_STOP -- "y" stops EDEN after tests ("n" by default) diff --git a/tests/workflow/lps-loc.tests.txt b/tests/workflow/lps-loc.tests.txt index 534383190..e541f55bc 100644 --- a/tests/workflow/lps-loc.tests.txt +++ b/tests/workflow/lps-loc.tests.txt @@ -1,7 +1,7 @@ # Number of tests {{$tests := 9}} # EDEN_TEST_SETUP env. var. -- "y"(default) performs the EDEN setup steps -{{$setup := "y"}} +{{$setup := "n"}} {{$setup_env := EdenGetEnv "EDEN_TEST_SETUP"}} {{if $setup_env}}{{$setup = $setup_env}}{{end}} # EDEN_TEST_STOP -- "y" stops EDEN after tests ("n" by default) diff --git a/tests/workflow/networking.tests.txt b/tests/workflow/networking.tests.txt index 52c4056cb..b59e42ee4 100644 --- a/tests/workflow/networking.tests.txt +++ b/tests/workflow/networking.tests.txt @@ -1,7 +1,7 @@ # Number of tests {{$tests := 14}} # EDEN_TEST_SETUP env. var. -- "y"(default) performs the EDEN setup steps -{{$setup := "y"}} +{{$setup := "n"}} {{$setup_env := EdenGetEnv "EDEN_TEST_SETUP"}} {{if $setup_env}}{{$setup = $setup_env}}{{end}} # EDEN_TEST_STOP -- "y" stops EDEN after tests ("n" by default) diff --git a/tests/workflow/smoke.tests.txt b/tests/workflow/smoke.tests.txt index 4e7790b10..3a575c5ee 100644 --- a/tests/workflow/smoke.tests.txt +++ b/tests/workflow/smoke.tests.txt @@ -1,7 +1,7 @@ # Number of tests -{{$tests := 23}} +{{$tests := 22}} # EDEN_TEST_SETUP env. var. -- "y"(default) performs the EDEN setup steps -{{$setup := "y"}} +{{$setup := "n"}} {{$setup_env := EdenGetEnv "EDEN_TEST_SETUP"}} {{if $setup_env}}{{$setup = $setup_env}}{{end}} # EDEN_TEST_STOP -- "y" stops EDEN after tests ("n" by default) @@ -78,6 +78,3 @@ eden.escript.test -testdata ../eclient/testdata/ -test.run TestEdenScripts/shutd /bin/echo EVE reset (22/{{$tests}}) eden.escript.test -test.run TestEdenScripts/eden_reset - -/bin/echo EVE security tests (23/{{$tests}}) -eden.escript.test -test.run TestEdenScripts/sec_eden diff --git a/tests/workflow/storage.tests.txt b/tests/workflow/storage.tests.txt index fffc06c3d..d3dd8a97c 100644 --- a/tests/workflow/storage.tests.txt +++ b/tests/workflow/storage.tests.txt @@ -1,7 +1,7 @@ # Number of tests {{$tests := 11}} # EDEN_TEST_SETUP env. var. -- "y"(default) performs the EDEN setup steps -{{$setup := "y"}} +{{$setup := "n"}} {{$setup_env := EdenGetEnv "EDEN_TEST_SETUP"}} {{if $setup_env}}{{$setup = $setup_env}}{{end}} # EDEN_TEST_STOP -- "y" stops EDEN after tests ("n" by default) diff --git a/tests/workflow/testdata/eden_onboard.txt b/tests/workflow/testdata/eden_onboard.txt index c96450d6d..e40be8636 100644 --- a/tests/workflow/testdata/eden_onboard.txt +++ b/tests/workflow/testdata/eden_onboard.txt @@ -18,12 +18,3 @@ EDEN={{EdenConfig "eden.root"}}/{{EdenConfig "eden.bin-dist"}}/{{EdenConfig "ede $EDEN controller get-options|jq ".enforceTemplateAttestation = $1"|$EDEN controller set-options --- eden-config.yml -- -test: - controller: adam://{{EdenConfig "adam.ip"}}:{{EdenConfig "adam.port"}} - eve: - {{EdenConfig "eve.name"}}: - onboard-cert: {{EdenConfigPath "eve.cert"}} - serial: "{{EdenConfig "eve.serial"}}" - model: {{EdenConfig "eve.devmodel"}} - diff --git a/tests/workflow/user-apps.tests.txt b/tests/workflow/user-apps.tests.txt index 7b9093004..994517e2c 100644 --- a/tests/workflow/user-apps.tests.txt +++ b/tests/workflow/user-apps.tests.txt @@ -1,7 +1,7 @@ # Number of tests {{$tests := 8}} # EDEN_TEST_SETUP env. var. -- "y"(default) performs the EDEN setup steps -{{$setup := "y"}} +{{$setup := "n"}} {{$setup_env := EdenGetEnv "EDEN_TEST_SETUP"}} {{if $setup_env}}{{$setup = $setup_env}}{{end}} # EDEN_TEST_STOP -- "y" stops EDEN after tests ("n" by default) diff --git a/tests/workflow/virtualization.tests.txt b/tests/workflow/virtualization.tests.txt index 6e6ab1bd9..2022ff4c9 100644 --- a/tests/workflow/virtualization.tests.txt +++ b/tests/workflow/virtualization.tests.txt @@ -1,7 +1,7 @@ # Number of tests {{$tests := 12}} # EDEN_TEST_SETUP env. var. -- "y"(default) performs the EDEN setup steps -{{$setup := "y"}} +{{$setup := "n"}} {{$setup_env := EdenGetEnv "EDEN_TEST_SETUP"}} {{if $setup_env}}{{$setup = $setup_env}}{{end}} # EDEN_TEST_STOP -- "y" stops EDEN after tests ("n" by default) @@ -53,4 +53,4 @@ eden.escript.test -testdata ../eclient/testdata/ -test.run TestEdenScripts/air-g eden.escript.test -testdata ../vnc/testdata/ -test.run TestEdenScripts/vnc_test /bin/echo EVE VcomLink tests (12/{{$tests}}) -eden.escript.test -test.run TestEdenScripts/eden_vcom \ No newline at end of file +eden.escript.test -test.run TestEdenScripts/eden_vcom