Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
git checkout "${NEXT_VERSION}"

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
uses: goreleaser/goreleaser-action@v7
with:
distribution: goreleaser
version: latest
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
bin/
coverage.html
coverage.out
.env
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
# bmc-test-go
bmc testing framework

## Environment Variables for config

The config parsing checks for the following environment variables:
- IMAGE : The base image to check
- GOLDEN_IMAGE : Known good image to downgrade to
- BMC_SSH_KEY : SSH Key for bmc root user
- HOST_SSH_KEY : SSH Key for the host root user

If the configuration file has the environment variable set, but the value is empty, the parser detects an error.
2 changes: 1 addition & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ tasks:
matrix:
OS: ['linux']
ARCH: ['amd64', 'arm', 'arm64']
cmd: GOOS="{{.ITEM.OS}}" GOARCH="{{.ITEM.ARCH}}" go build -ldflags="-s -w" -o "bin/bmc-test-go-{{.ITEM.OS}}-{{.ITEM.ARCH}}-{{.SEMVER}}"
cmd: GOOS="{{.ITEM.OS}}" GOARCH="{{.ITEM.ARCH}}" go build -ldflags="-s -w" -o "bin/bmc-test-go-{{.ITEM.OS}}-{{.ITEM.ARCH}}-{{.SEMVER}}" ./cmds/openbmc-tests

lint:
desc: Run linters
Expand Down
98 changes: 98 additions & 0 deletions cmds/openbmc-tests/cmdline.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package main

import (
"flag"
)

var (
runSingleCmd string = "single"
runSuiteCmd string = "suite"
runAllCmd string = "all"
cfgCheckCmd string = "cfg-check"
listTestsCmd string = "list-tests"
listSuitesCmd string = "list-suites"
runCfgCmd string = "run-from-cfg"
)

type flags struct {
cmd string
configPath string
execEnv string
suite string
testName string
logType string
logFile string
}

func parseFlags(args []string) (*flags, error) {
f := &flags{
cmd: args[0],
}

cfgCheckFS := flag.NewFlagSet(cfgCheckCmd, flag.ExitOnError)
cfgCheckFS.StringVar(&f.configPath, "config", "contrib/example_config.yaml", "Path to device specific test case configuration file")

runSingleFS := flag.NewFlagSet(runSingleCmd, flag.ExitOnError)
runSingleFS.StringVar(&f.testName, "testname", "", "Run a single test with 'testname'. Use the short name. No default")
runSingleFS.StringVar(&f.execEnv, "exec-env", "remote", "Run the test locally or remotely '<local, remote>'. Default: remote")
runSingleFS.StringVar(&f.configPath, "config", "contrib/example_config.yaml", "Path to device specific test case configuration file")
runSingleFS.StringVar(&f.logType, "log", "default", "Logging options <default, structured>")
runSingleFS.StringVar(&f.logFile, "logfile", "", "Path to log file. Only with structured logging option.")

runSuiteFS := flag.NewFlagSet(runSuiteCmd, flag.ExitOnError)
runSuiteFS.StringVar(&f.execEnv, "exec-env", "remote", "Run the test locally or remotely '<local, remote>'. Default: remote")
runSuiteFS.StringVar(&f.suite, "suite", "", "Specify the suite. <redfish, ipmi, ifaces>")
runSuiteFS.StringVar(&f.configPath, "config", "contrib/example_config.yaml", "Path to device specific test case configuration file")
runSuiteFS.StringVar(&f.logType, "log", "default", "Logging options <default, structured>")
runSuiteFS.StringVar(&f.logFile, "logfile", "", "Path to log file. Only with structured logging option.")

runAllFS := flag.NewFlagSet(runAllCmd, flag.ExitOnError)
runAllFS.StringVar(&f.execEnv, "exec-env", "remote", "Run the test locally or remotely '<local, remote>'.")
runAllFS.StringVar(&f.configPath, "config", "contrib/example_config.yaml", "Path to device specific test case configuration file")
runAllFS.StringVar(&f.logType, "log", "default", "Logging options <default, structured>")
runAllFS.StringVar(&f.logFile, "logfile", "", "Path to log file. Only with structured logging option.")

listTestsFS := flag.NewFlagSet(listTestsCmd, flag.ExitOnError)
listTestsFS.StringVar(&f.logType, "log", "default", "Logging options <default, structured>")
listTestsFS.StringVar(&f.logFile, "logfile", "", "Path to log file. Only with structured logging option.")
listTestsFS.StringVar(&f.suite, "suite", "", "Specify the suite. <redfish, ipmi, ifaces>")

listSuitesFS := flag.NewFlagSet(listSuitesCmd, flag.ExitOnError)

runFromCfgFS := flag.NewFlagSet(runCfgCmd, flag.ExitOnError)
runFromCfgFS.StringVar(&f.execEnv, "exec-env", "remote", "Run the test locally or remotely '<local, remote>'. Default: remote")
runFromCfgFS.StringVar(&f.configPath, "config", "contrib/example_config.yaml", "Path to device specific test case configuration file")
runFromCfgFS.StringVar(&f.logType, "log", "default", "Logging options <default, structured>")
runFromCfgFS.StringVar(&f.logFile, "logfile", "", "Path to log file. Only with structured logging option.")

var err error
switch args[0] {
case cfgCheckCmd:
err = cfgCheckFS.Parse(args[1:])
case runSingleCmd:
err = runSingleFS.Parse(args[1:])
case runSuiteCmd:
err = runSuiteFS.Parse(args[1:])
case runAllCmd:
err = runAllFS.Parse(args[1:])
case listTestsCmd:
err = listTestsFS.Parse(args[1:])
case listSuitesCmd:
err = listSuitesFS.Parse(args[1:])
case runCfgCmd:
err = runFromCfgFS.Parse(args[1:])
default:
cfgCheckFS.Usage()
runSingleFS.Usage()
runSuiteFS.Usage()
runAllFS.Usage()
listTestsFS.Usage()
listSuitesFS.Usage()
runFromCfgFS.Usage()
}
if err != nil {
return nil, err
}

return f, nil
}
225 changes: 225 additions & 0 deletions cmds/openbmc-tests/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
// SPDX-License-Identifier: BSD-3-Clause

// Package main implements the core logic
package main

import (
"fmt"
"log"
"os"
"strings"

"github.com/9elements/bmc-test-go/pkg/configuration"
"github.com/9elements/bmc-test-go/pkg/framework"
"github.com/9elements/bmc-test-go/pkg/testdevice"
"github.com/9elements/bmc-test-go/pkg/tests"
"github.com/9elements/bmc-test-go/pkg/tests/ipmi"
)

func runAll(dev *testdevice.Device, cfg *configuration.Config) error {
ipmiPre := &ipmi.IPMISDRPrecondition{}
if err := ipmiPre.Run(dev); err != nil {
return err
}

tests := tests.AllTests()
if len(tests) < 1 {
return fmt.Errorf("no tests to execute")
}

for _, test := range tests {
if !framework.RunTest(test, dev, cfg, ipmiPre) {
log.Printf("%s", test.ErrorText())
continue
}
}
return nil
}

func runSingle(testname string, dev *testdevice.Device, cfg *configuration.Config) error {
var pre framework.Prerequisites
if strings.Contains(testname, "ipmi") {
ipmiPre := &ipmi.IPMISDRPrecondition{}
if err := ipmiPre.Run(dev); err != nil {
return err
}
pre = ipmiPre
}

tests := tests.AllTests()
if len(tests) < 1 {
return fmt.Errorf("no tests to execute")
}

for _, test := range tests {
if test.ShortName() == testname {
if !framework.RunTest(test, dev, cfg, pre) {
log.Printf("%s", test.ErrorText())
}
}
}

return nil
}

func runSuite(suite string, dev *testdevice.Device, cfg *configuration.Config) error {
tests := tests.GetSuite(suite)
if len(tests) < 1 {
return fmt.Errorf("no tests to execute")
}

var pre framework.Prerequisites
if suite == "ipmi" {
ipmiPre := &ipmi.IPMISDRPrecondition{}
if err := ipmiPre.Run(dev); err != nil {
return err
}
pre = ipmiPre
}

for _, test := range tests {
if !framework.RunTest(test, dev, cfg, pre) {
log.Printf("%s", test.ErrorText())
}
}
return nil
}

func listTests(suite string) error {
var t []framework.Test
if suite != "" {
t = tests.GetSuite(suite)
} else {
t = tests.AllTests()
}

logger := log.New(os.Stdout, "", log.Lmsgprefix)

logger.Printf("List tests %s", suite)
logger.Printf("-------------------------------------------------------")
logger.Printf("-------------------------------------------------------")
logger.Printf("%-30s | %s\n", "Long name", "Short name")
logger.Printf("-------------------------------------------------------")
for _, test := range t {
logger.Printf("%-30s | %s\n", test.Name(), test.ShortName())
logger.Printf("-------------------------------------------------------")
}

return nil
}

func runFromCfg(dev *testdevice.Device, cfg *configuration.Config) error {
ipmiPre := &ipmi.IPMISDRPrecondition{}
if err := ipmiPre.Run(dev); err != nil {
return err
}

alltests := tests.AllTests()

for _, testName := range cfg.Tests {
for _, test := range alltests {
if testName == test.ShortName() {
framework.RunTest(test, dev, cfg, ipmiPre)
}
}
}
return nil
}

func listSuites() error {
logger := log.New(os.Stdout, "", log.Lmsgprefix)

logger.Printf("List Suites")
logger.Printf("-------------------------------------------------------")
logger.Printf("-------------------------------------------------------")
logger.Println("ipmi")
logger.Println("ipmi-mct")
logger.Println("ipmi-twitter")
logger.Println("redfish")
logger.Println("pci")
logger.Println("iface")
logger.Println("smbios")
logger.Println("bmc-linux")
return nil
}

func run() error {
if len(os.Args) < 2 {
return fmt.Errorf("invalid argument count")
}
flags, err := parseFlags(os.Args[1:])
if err != nil {
return err
}

cfg, err := configuration.LoadConfig(flags.configPath)
if err != nil {
return err
}

if err := framework.SetupReporting(flags.logType, flags.logFile); err != nil {
return err
}

switch flags.cmd {
case runSingleCmd:
bmc, err := testdevice.NewDevice(flags.execEnv, cfg)
if err != nil {
return err
}
defer func() {
if err := bmc.Close(); err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
log.Print(err)
}
}()
return runSingle(flags.testName, bmc, cfg)
case runSuiteCmd:
bmc, err := testdevice.NewDevice(flags.execEnv, cfg)
if err != nil {
return err
}
defer func() {
if err := bmc.Close(); err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
log.Print(err)
}
}()
return runSuite(flags.suite, bmc, cfg)
case runAllCmd:
bmc, err := testdevice.NewDevice(flags.execEnv, cfg)
if err != nil {
return err
}
defer func() {
if err := bmc.Close(); err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
log.Print(err)
}
}()
return runAll(bmc, cfg)
case runCfgCmd:
bmc, err := testdevice.NewDevice(flags.execEnv, cfg)
if err != nil {
return err
}
defer func() {
if err := bmc.Close(); err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
log.Print(err)
}
}()
return runFromCfg(bmc, cfg)
case cfgCheckCmd:
return configuration.Validate(cfg)
case listTestsCmd:
return listTests(flags.suite)
case listSuitesCmd:
return listSuites()
default:
return fmt.Errorf("unknown command: %s", flags.cmd)
}
}

func main() {
if err := run(); err != nil {
log.Print(err)
os.Exit(1)
}
}
Loading
Loading