From 3367b67b9f09188b4ebd098e22dda690a99d9ed7 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Tue, 7 Mar 2023 08:58:29 +0100 Subject: [PATCH] added a test for `wwctl node add` Signed-off-by: Christian Goll --- internal/app/wwctl/node/add/main_test.go | 89 ++++++++++++++++++++++ internal/app/wwctl/root.go | 4 +- internal/pkg/node/constructors.go | 43 ++++++++++- internal/pkg/node/datastructure.go | 2 + internal/pkg/node/modifiers.go | 5 +- internal/pkg/warewulfconf/constructors.go | 10 ++- internal/pkg/warewulfconf/datastructure.go | 1 + internal/pkg/warewulfd/daemon.go | 28 +++++++ 8 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 internal/app/wwctl/node/add/main_test.go diff --git a/internal/app/wwctl/node/add/main_test.go b/internal/app/wwctl/node/add/main_test.go new file mode 100644 index 00000000..29750a47 --- /dev/null +++ b/internal/app/wwctl/node/add/main_test.go @@ -0,0 +1,89 @@ +package add + +import ( + "bytes" + "testing" + + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + "github.com/hpcng/warewulf/internal/pkg/warewulfd" + "github.com/stretchr/testify/assert" +) + +func Test_Add(t *testing.T) { + t.Helper() + conf_yml := ` +WW_INTERNAL: 0 + ` + nodes_yml := ` +WW_INTERNAL: 43 +` + conf := warewulfconf.New() + err := conf.Read([]byte(conf_yml)) + assert.NoError(t, err) + db, err := node.TestNew([]byte(nodes_yml)) + assert.NoError(t, err) + warewulfd.SetNoDaemon() + buf := new(bytes.Buffer) + baseCmd.SetOut(buf) + baseCmd.SetErr(buf) + tests := []struct { + name string + args []string + wantErr bool + stdout string + outDb string + }{ + {name: "single node add", + args: []string{"n01"}, + wantErr: false, + stdout: "", + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: + n01: + profiles: + - default + network devices: + default: {} +`}, + {name: "double node add", + args: []string{"n0[1-2]"}, + wantErr: false, + stdout: "", + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: + n01: + profiles: + - default + network devices: + default: {} + n02: + profiles: + - default + network devices: + default: {} +`}, + } + for _, tt := range tests { + db, err = node.TestNew([]byte(nodes_yml)) + assert.NoError(t, err) + t.Run(tt.name, func(t *testing.T) { + baseCmd.SetArgs(tt.args) + err = baseCmd.Execute() + if (err != nil) != tt.wantErr { + t.Errorf("Got unwanted error: %s", err) + return + } + dump := string(db.DBDump()) + if dump != tt.outDb { + t.Errorf("DB dump is wrong, got:'%s'\nwant:'%s'", dump, tt.outDb) + return + } + if buf.String() != tt.stdout { + t.Errorf("Got wrong output, got:'%s'\nwant:'%s'", buf.String(), tt.stdout) + } + }) + } +} diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go index 395c4b4c..42f9e4e9 100644 --- a/internal/app/wwctl/root.go +++ b/internal/app/wwctl/root.go @@ -77,7 +77,7 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) (err error) { wwlog.SetLogLevel(LogLevel) } conf := warewulfconf.New() - if !AllowEmptyConf { + if !AllowEmptyConf && !conf.Initialized() { if WarewulfConfArg != "" { err = conf.ReadConf(WarewulfConfArg) } else if os.Getenv("WAREWULFCONF") != "" { @@ -86,7 +86,7 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) (err error) { err = conf.ReadConf(warewulfconf.ConfigFile) } } else { - conf.SetDynamicDefaults() + err = conf.SetDynamicDefaults() } return } diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index bcf57d0b..89079144 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -16,6 +16,8 @@ import ( var ConfigFile string var DefaultConfig string +var cachedDB NodeYaml + // used as fallback if DefaultConfig can't be read var FallBackConf = `--- defaultnode: @@ -44,9 +46,18 @@ func init() { if DefaultConfig == "" { DefaultConfig = path.Join(conf.Paths.Sysconfdir, "warewulf/defaults.conf") } + cachedDB.current = false + cachedDB.persist = true } +/* +Creates a new nodeDb object from the actual configuration +*/ func New() (NodeYaml, error) { + if cachedDB.current { + wwlog.Debug("Returning cached object") + return cachedDB, nil + } var ret NodeYaml wwlog.Verbose("Opening node configuration file: %s", ConfigFile) @@ -62,10 +73,40 @@ func New() (NodeYaml, error) { } wwlog.Debug("Returning node object") - + cachedDB = ret + cachedDB.current = true return ret, nil } +/* +Creates a database object from a given buffer, always create +a new object, never return the cached one. +*/ +func TestNew(buffer []byte) (db NodeYaml, err error) { + db.NodeProfiles = make(map[string]*NodeConf) + db.Nodes = make(map[string]*NodeConf) + err = yaml.Unmarshal(buffer, &db) + db.persist = false + cachedDB = db + cachedDB.current = true + wwlog.Debug("Created cached object") + return +} + +func (config *NodeYaml) DBDump() (buffer []byte) { + for _, n := range config.Nodes { + n.Flatten() + } + for _, p := range config.NodeProfiles { + p.Flatten() + } + buffer, err := yaml.Marshal(config) + if err != nil { + wwlog.Warn("porblems on dumping nodedb: %s", err) + } + return +} + /* Get all the nodes of a configuration. This function also merges the nodes with the given profiles and set the default values diff --git a/internal/pkg/node/datastructure.go b/internal/pkg/node/datastructure.go index 72bea85a..cecd1db5 100644 --- a/internal/pkg/node/datastructure.go +++ b/internal/pkg/node/datastructure.go @@ -10,6 +10,8 @@ type NodeYaml struct { WWInternal int `yaml:"WW_INTERNAL"` NodeProfiles map[string]*NodeConf Nodes map[string]*NodeConf + current bool + persist bool } /* diff --git a/internal/pkg/node/modifiers.go b/internal/pkg/node/modifiers.go index 1537f93c..2c644657 100644 --- a/internal/pkg/node/modifiers.go +++ b/internal/pkg/node/modifiers.go @@ -127,7 +127,10 @@ func (config *NodeYaml) Persist() error { if err != nil { return err } - + // Can't persist for unit tests + if !config.persist { + return err + } file, err := os.OpenFile(ConfigFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { wwlog.Error("%s", err) diff --git a/internal/pkg/warewulfconf/constructors.go b/internal/pkg/warewulfconf/constructors.go index 03d510ca..217b8cfc 100644 --- a/internal/pkg/warewulfconf/constructors.go +++ b/internal/pkg/warewulfconf/constructors.go @@ -30,8 +30,8 @@ func New() (conf ControllerConf) { conf.Nfs = new(NfsConf) conf.Paths = new(BuildConfig) _ = defaults.Set(&conf) - cachedConf = conf + cachedConf.readConf = false cachedConf.current = true } else { @@ -76,6 +76,7 @@ func (conf *ControllerConf) Read(data []byte) (err error) { } cachedConf = *conf cachedConf.current = true + cachedConf.readConf = true return } @@ -148,3 +149,10 @@ func (conf *ControllerConf) SetDynamicDefaults() (err error) { cachedConf.current = true return } + +/* +Return if configuration was read from disk +*/ +func (conf *ControllerConf) Initialized() bool { + return conf.readConf +} diff --git a/internal/pkg/warewulfconf/datastructure.go b/internal/pkg/warewulfconf/datastructure.go index 4c4ae71b..1958a2b5 100644 --- a/internal/pkg/warewulfconf/datastructure.go +++ b/internal/pkg/warewulfconf/datastructure.go @@ -20,6 +20,7 @@ type ControllerConf struct { MountsContainer []*MountEntry `yaml:"container mounts" default:"[{\"source\": \"/etc/resolv.conf\", \"dest\": \"/etc/resolv.conf\"}]"` Paths *BuildConfig `yaml:"paths"` current bool + readConf bool } type WarewulfConf struct { diff --git a/internal/pkg/warewulfd/daemon.go b/internal/pkg/warewulfd/daemon.go index 99407d23..34a989b6 100644 --- a/internal/pkg/warewulfd/daemon.go +++ b/internal/pkg/warewulfd/daemon.go @@ -23,6 +23,23 @@ const ( var loginit bool +// allow to run without daemon for tests +var nodaemon bool + +func init() { + nodaemon = false +} + +// run without daemon +func SetNoDaemon() { + nodaemon = true +} + +// run with daemon +func SetDaemon() { + nodaemon = false +} + func DaemonFormatter(logLevel int, rec *wwlog.LogRecord) string { return "[" + rec.Time.Format(time.UnixDate) + "] " + wwlog.DefaultFormatter(logLevel, rec) } @@ -66,6 +83,10 @@ func DaemonInitLogging() error { } func DaemonStart() error { + if nodaemon { + return nil + } + if os.Getenv("WAREWULFD_BACKGROUND") == "1" { err := RunServer() if err != nil { @@ -116,6 +137,10 @@ func DaemonStart() error { } func DaemonStatus() error { + if nodaemon { + return nil + } + if !util.IsFile(WAREWULFD_PIDFILE) { return errors.New("Warewulf server is not running") } @@ -142,6 +167,9 @@ func DaemonStatus() error { } func DaemonReload() error { + if nodaemon { + return nil + } if !util.IsFile(WAREWULFD_PIDFILE) { return errors.New("Warewulf server is not running") }