diff --git a/.gitignore b/.gitignore index 18b9454f..7859c0c0 100644 --- a/.gitignore +++ b/.gitignore @@ -25,10 +25,10 @@ /etc/bash_completion.d/ warewulf.spec internal/pkg/buildconfig/setconfigs.go -internal/pkg/warewulfconf/buildconfig.go +internal/pkg/config/buildconfig.go include/systemd/warewulfd.service _dist/ -config +/config warewulf-*.tar.gz Defaults.mk /etc/wwapid.config diff --git a/Makefile b/Makefile index 36e36d98..1abd6869 100644 --- a/Makefile +++ b/Makefile @@ -151,7 +151,7 @@ vet: go vet ./... test-it: - go test -v ./... -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=$(shell pwd)/etc/warewulf.conf'" + go test -v ./... # Generate test coverage test-cover: ## Run test coverage and generate html report @@ -219,13 +219,12 @@ init: wwctl: $(WWCTL_DEPS) @echo Building "$@" @cd cmd/wwctl; GOOS=linux go build -mod vendor -tags "$(WW_GO_BUILD_TAGS)" \ - -ldflags "-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=$(SYSCONFDIR)/warewulf/warewulf.conf'" \ -o ../../wwctl wwclient: $(WWCLIENT_DEPS) @echo Building "$@" - @cd cmd/wwclient; CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -ldflags "-extldflags -static \ - -X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=/etc/warewulf/warewulf.conf'" -o ../../wwclient + @cd cmd/wwclient; CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -ldflags "-extldflags -static" \ + -o ../../wwclient man_pages: wwctl @install -d man_pages @@ -235,7 +234,7 @@ man_pages: wwctl @cd man_pages; for i in wwctl*1 *.5; do gzip --force $$i; echo -n "$$i "; done; echo update_configuration: vendor cmd/update_configuration/update_configuration.go - cd cmd/update_configuration && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=./etc/warewulf.conf'\ + cd cmd/update_configuration && go build \ -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=./etc/nodes.conf'"\ -mod vendor -tags "$(WW_GO_BUILD_TAGS)" -o ../../update_configuration diff --git a/internal/app/api/wwapic/wwapic.go b/internal/app/api/wwapic/wwapic.go index 36cfb002..8eca0814 100644 --- a/internal/app/api/wwapic/wwapic.go +++ b/internal/app/api/wwapic/wwapic.go @@ -12,7 +12,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/api/apiconfig" "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -24,7 +24,7 @@ import ( func main() { log.Println("Client running") - conf := warewulfconf.New() + conf := warewulfconf.Get() // Read the config file. config, err := apiconfig.NewClient(path.Join(conf.Paths.Sysconfdir, "warewulf/wwapic.conf")) diff --git a/internal/app/api/wwapid/wwapid.go b/internal/app/api/wwapid/wwapid.go index ce7457e5..5042b8c7 100644 --- a/internal/app/api/wwapid/wwapid.go +++ b/internal/app/api/wwapid/wwapid.go @@ -16,7 +16,7 @@ import ( apinode "github.com/hpcng/warewulf/internal/pkg/api/node" "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" "github.com/hpcng/warewulf/internal/pkg/version" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" @@ -34,7 +34,7 @@ var apiVersion string func main() { log.Println("Server running") - conf := warewulfconf.New() + conf := warewulfconf.Get() // Read the config file. config, err := apiconfig.NewServer(path.Join(conf.Paths.Sysconfdir, "warewulf/wwapid.conf")) if err != nil { diff --git a/internal/app/api/wwapird/wwapird.go b/internal/app/api/wwapird/wwapird.go index e9b210ac..354b82dd 100644 --- a/internal/app/api/wwapird/wwapird.go +++ b/internal/app/api/wwapird/wwapird.go @@ -18,7 +18,7 @@ import ( "google.golang.org/grpc/credentials/insecure" "github.com/hpcng/warewulf/internal/pkg/api/apiconfig" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" gw "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" @@ -29,7 +29,7 @@ func run() error { log.Println("test0") - conf := warewulfconf.New() + conf := warewulfconf.Get() // Read the config file. config, err := apiconfig.NewClientServer(path.Join(conf.Paths.Sysconfdir, "warewulf/wwapird.conf")) if err != nil { diff --git a/internal/app/wwclient/root.go b/internal/app/wwclient/root.go index b63f3f41..5af79840 100644 --- a/internal/app/wwclient/root.go +++ b/internal/app/wwclient/root.go @@ -17,7 +17,7 @@ import ( "github.com/coreos/go-systemd/daemon" "github.com/google/uuid" "github.com/hpcng/warewulf/internal/pkg/pidfile" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/spf13/cobra" "github.com/talos-systems/go-smbios/smbios" @@ -49,7 +49,7 @@ func GetRootCommand() *cobra.Command { } func CobraRunE(cmd *cobra.Command, args []string) error { - conf := warewulfconf.New() + conf := warewulfconf.Get() pid, err := pidfile.Write(PIDFile) if err != nil && pid == -1 { diff --git a/internal/app/wwctl/configure/dhcp/main.go b/internal/app/wwctl/configure/dhcp/main.go index 38ff6f97..a98561d9 100644 --- a/internal/app/wwctl/configure/dhcp/main.go +++ b/internal/app/wwctl/configure/dhcp/main.go @@ -6,5 +6,5 @@ import ( ) func CobraRunE(cmd *cobra.Command, args []string) error { - return configure.Dhcp() + return configure.DHCP() } diff --git a/internal/app/wwctl/configure/main.go b/internal/app/wwctl/configure/main.go index 3512d2fc..798c1d03 100644 --- a/internal/app/wwctl/configure/main.go +++ b/internal/app/wwctl/configure/main.go @@ -11,7 +11,7 @@ import ( func CobraRunE(cmd *cobra.Command, args []string) error { var err error if allFunctions { - err = configure.Dhcp() + err = configure.DHCP() if err != nil { wwlog.Error("%s", err) os.Exit(1) diff --git a/internal/app/wwctl/container/exec/child/main.go b/internal/app/wwctl/container/exec/child/main.go index 9b62b507..ee2b97b9 100644 --- a/internal/app/wwctl/container/exec/child/main.go +++ b/internal/app/wwctl/container/exec/child/main.go @@ -15,7 +15,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/overlay" "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -33,7 +33,7 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) { wwlog.Error("Unknown Warewulf container: %s", containerName) os.Exit(1) } - conf := warewulfconf.New() + conf := warewulfconf.Get() mountPts := conf.MountsContainer mountPts = append(container.InitMountPnts(binds), mountPts...) // check for valid mount points diff --git a/internal/app/wwctl/container/list/main_test.go b/internal/app/wwctl/container/list/main_test.go index f87702e9..ab3c880f 100644 --- a/internal/app/wwctl/container/list/main_test.go +++ b/internal/app/wwctl/container/list/main_test.go @@ -10,7 +10,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" "github.com/hpcng/warewulf/internal/pkg/node" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/warewulfd" "github.com/stretchr/testify/assert" ) @@ -55,8 +55,8 @@ nodes: WW_INTERNAL: 0 ` - conf := warewulfconf.New() - err := conf.Read([]byte(conf_yml)) + conf := warewulfconf.Get() + err := conf.Parse([]byte(conf_yml)) assert.NoError(t, err) warewulfd.SetNoDaemon() for _, tt := range tests { diff --git a/internal/app/wwctl/genconf/warewulfconf/print/main.go b/internal/app/wwctl/genconf/warewulfconf/print/main.go index 2efca477..e18ea611 100644 --- a/internal/app/wwctl/genconf/warewulfconf/print/main.go +++ b/internal/app/wwctl/genconf/warewulfconf/print/main.go @@ -3,13 +3,13 @@ package print import ( "fmt" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/spf13/cobra" "gopkg.in/yaml.v2" ) func CobraRunE(cmd *cobra.Command, args []string) (err error) { - conf := warewulfconf.New() + conf := warewulfconf.Get() buffer, err := yaml.Marshal(&conf) if err != nil { return diff --git a/internal/app/wwctl/node/add/main_test.go b/internal/app/wwctl/node/add/main_test.go index 6750cedf..71513ea1 100644 --- a/internal/app/wwctl/node/add/main_test.go +++ b/internal/app/wwctl/node/add/main_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/hpcng/warewulf/internal/pkg/node" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/warewulfd" "github.com/stretchr/testify/assert" ) @@ -243,7 +243,7 @@ nodes: _, warewulfConfErr = tempWarewulfConf.Write([]byte(conf_yml)) assert.NoError(t, warewulfConfErr) assert.NoError(t, tempWarewulfConf.Sync()) - warewulfconf.ConfigFile = tempWarewulfConf.Name() + assert.NoError(t, warewulfconf.New().Read(tempWarewulfConf.Name())) nodes_yml := `WW_INTERNAL: 43` tempNodeConf, nodesConfErr := os.CreateTemp("", "nodes.conf-") diff --git a/internal/app/wwctl/node/list/main_test.go b/internal/app/wwctl/node/list/main_test.go index 7d0e5751..2717d156 100644 --- a/internal/app/wwctl/node/list/main_test.go +++ b/internal/app/wwctl/node/list/main_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/hpcng/warewulf/internal/pkg/node" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/warewulfd" "github.com/stretchr/testify/assert" ) @@ -42,7 +42,7 @@ nodes: _, warewulfConfErr = tempWarewulfConf.Write([]byte(conf_yml)) assert.NoError(t, warewulfConfErr) assert.NoError(t, tempWarewulfConf.Sync()) - warewulfconf.ConfigFile = tempWarewulfConf.Name() + assert.NoError(t, warewulfconf.New().Read(tempWarewulfConf.Name())) tempNodeConf, nodesConfErr := os.CreateTemp("", "nodes.conf-") assert.NoError(t, nodesConfErr) diff --git a/internal/app/wwctl/node/status/main.go b/internal/app/wwctl/node/status/main.go index 5d8cdef7..d1675cb9 100644 --- a/internal/app/wwctl/node/status/main.go +++ b/internal/app/wwctl/node/status/main.go @@ -9,7 +9,7 @@ import ( "github.com/fatih/color" apinode "github.com/hpcng/warewulf/internal/pkg/api/node" "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/hpcng/warewulf/pkg/hostlist" "github.com/spf13/cobra" @@ -18,7 +18,7 @@ import ( func CobraRunE(cmd *cobra.Command, args []string) (err error) { - controller := warewulfconf.New() + controller := warewulfconf.Get() if controller.Ipaddr == "" { return fmt.Errorf("warewulf Server IP Address is not properly configured") diff --git a/internal/app/wwctl/overlay/build/main.go b/internal/app/wwctl/overlay/build/main.go index 556833c4..75c7cf82 100644 --- a/internal/app/wwctl/overlay/build/main.go +++ b/internal/app/wwctl/overlay/build/main.go @@ -7,14 +7,14 @@ import ( "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/overlay" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/hpcng/warewulf/pkg/hostlist" "github.com/spf13/cobra" ) func CobraRunE(cmd *cobra.Command, args []string) error { - controller := warewulfconf.New() + controller := warewulfconf.Get() nodeDB, err := node.New() if err != nil { wwlog.Error("Could not open node configuration: %s", err) diff --git a/internal/app/wwctl/profile/add/main_test.go b/internal/app/wwctl/profile/add/main_test.go index 8da95a45..f1a084e7 100644 --- a/internal/app/wwctl/profile/add/main_test.go +++ b/internal/app/wwctl/profile/add/main_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/hpcng/warewulf/internal/pkg/node" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/warewulfd" "github.com/stretchr/testify/assert" ) diff --git a/internal/app/wwctl/profile/list/main_test.go b/internal/app/wwctl/profile/list/main_test.go index 46f29d30..62e2ec03 100644 --- a/internal/app/wwctl/profile/list/main_test.go +++ b/internal/app/wwctl/profile/list/main_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/hpcng/warewulf/internal/pkg/node" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/warewulfd" "github.com/stretchr/testify/assert" ) @@ -43,7 +43,7 @@ nodes: _, warewulfConfErr = tempWarewulfConf.Write([]byte(conf_yml)) assert.NoError(t, warewulfConfErr) assert.NoError(t, tempWarewulfConf.Sync()) - warewulfconf.ConfigFile = tempWarewulfConf.Name() + assert.NoError(t, warewulfconf.New().Read(tempWarewulfConf.Name())) tempNodeConf, nodesConfErr := os.CreateTemp("", "nodes.conf-") assert.NoError(t, nodesConfErr) diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go index 42f9e4e9..e694b09f 100644 --- a/internal/app/wwctl/root.go +++ b/internal/app/wwctl/root.go @@ -15,7 +15,7 @@ import ( "github.com/hpcng/warewulf/internal/app/wwctl/ssh" "github.com/hpcng/warewulf/internal/app/wwctl/version" "github.com/hpcng/warewulf/internal/pkg/help" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/spf13/cobra" ) @@ -76,17 +76,19 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) (err error) { if LogLevel != wwlog.INFO { wwlog.SetLogLevel(LogLevel) } - conf := warewulfconf.New() - if !AllowEmptyConf && !conf.Initialized() { + conf := warewulfconf.Get() + if !AllowEmptyConf && !conf.InitializedFromFile() { if WarewulfConfArg != "" { - err = conf.ReadConf(WarewulfConfArg) + err = conf.Read(WarewulfConfArg) } else if os.Getenv("WAREWULFCONF") != "" { - err = conf.ReadConf(os.Getenv("WAREWULFCONF")) + err = conf.Read(os.Getenv("WAREWULFCONF")) } else { - err = conf.ReadConf(warewulfconf.ConfigFile) + err = conf.Read(warewulfconf.ConfigFile) } - } else { - err = conf.SetDynamicDefaults() } + if err != nil { + return + } + err = conf.SetDynamicDefaults() return } diff --git a/internal/app/wwctl/server/start/main.go b/internal/app/wwctl/server/start/main.go index 00429177..01c9a19f 100644 --- a/internal/app/wwctl/server/start/main.go +++ b/internal/app/wwctl/server/start/main.go @@ -1,7 +1,7 @@ package start import ( - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/warewulfd" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -10,7 +10,7 @@ import ( func CobraRunE(cmd *cobra.Command, args []string) error { if SetForeground { - conf := warewulfconf.New() + conf := warewulfconf.Get() conf.Warewulf.Syslog = false return errors.Wrap(warewulfd.RunServer(), "failed to start Warewulf server") } else { diff --git a/internal/pkg/api/node/node.go b/internal/pkg/api/node/node.go index 2c63d8db..13a10c15 100644 --- a/internal/pkg/api/node/node.go +++ b/internal/pkg/api/node/node.go @@ -10,7 +10,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/hpcng/warewulf/pkg/hostlist" "github.com/pkg/errors" @@ -317,7 +317,7 @@ func NodeStatus(nodeNames []string) (nodeStatusResponse *wwapiv1.NodeStatusRespo Nodes map[string]*nodeStatusInternal `json:"nodes"` } - controller := warewulfconf.New() + controller := warewulfconf.Get() if controller.Ipaddr == "" { err = fmt.Errorf("the Warewulf Server IP Address is not properly configured") diff --git a/internal/pkg/warewulfconf/buildconfig.go.in b/internal/pkg/config/buildconfig.go.in similarity index 90% rename from internal/pkg/warewulfconf/buildconfig.go.in rename to internal/pkg/config/buildconfig.go.in index cd7ac2f4..5316e518 100644 --- a/internal/pkg/warewulfconf/buildconfig.go.in +++ b/internal/pkg/config/buildconfig.go.in @@ -1,4 +1,6 @@ -package warewulfconf +package config + +var ConfigFile = "@SYSCONFDIR@/warewulf/warewulf.conf" type BuildConfig struct { Bindir string `default:"@BINDIR@"` diff --git a/internal/pkg/config/dhcp.go b/internal/pkg/config/dhcp.go new file mode 100644 index 00000000..3dbcd241 --- /dev/null +++ b/internal/pkg/config/dhcp.go @@ -0,0 +1,12 @@ +package config + + +// DHCPConf represents the configuration for the DHCP service that +// Warewulf will configure. +type DHCPConf struct { + Enabled bool `yaml:"enabled" default:"true"` + Template string `yaml:"template" default:"default"` + RangeStart string `yaml:"range start,omitempty"` + RangeEnd string `yaml:"range end,omitempty"` + SystemdName string `yaml:"systemd name" default:"dhcpd"` +} diff --git a/internal/pkg/config/mounts.go b/internal/pkg/config/mounts.go new file mode 100644 index 00000000..daf38c0a --- /dev/null +++ b/internal/pkg/config/mounts.go @@ -0,0 +1,10 @@ +package config + +// A MountEntry represents a bind mount that is applied to a container +// during exec and shell. +type MountEntry struct { + Source string `yaml:"source" default:"/etc/resolv.conf"` + Dest string `yaml:"dest,omitempty" default:"/etc/resolv.conf"` + ReadOnly bool `yaml:"readonly,omitempty" default:"false"` + Options string `yaml:"options,omitempty"` // ignored at the moment +} diff --git a/internal/pkg/config/nfs.go b/internal/pkg/config/nfs.go new file mode 100644 index 00000000..03726379 --- /dev/null +++ b/internal/pkg/config/nfs.go @@ -0,0 +1,35 @@ +package config + + +import ( + "github.com/creasty/defaults" +) + + +// NFSConf represents the NFS configuration that will be used by +// Warewulf to generate exports on the server and mounts on compute +// nodes. +type NFSConf struct { + Enabled bool `yaml:"enabled" default:"true"` + ExportsExtended []*NFSExportConf `yaml:"export paths" default:"[]"` + SystemdName string `yaml:"systemd name" default:"nfsd"` +} + + +// An NFSExportConf reprents a single NFS export / mount. +type NFSExportConf struct { + Path string `yaml:"path" default:"/dev/null"` + ExportOptions string `default:"rw,sync,no_subtree_check" yaml:"export options"` + MountOptions string `default:"defaults" yaml:"mount options"` + Mount bool `default:"true" yaml:"mount"` +} + + +// Implements the Unmarshal interface for NFSConf to set default +// values. +func (conf *NFSConf) Unmarshal(unmarshal func(interface{}) error) error { + if err := defaults.Set(conf); err != nil { + return err + } + return nil +} diff --git a/internal/pkg/config/root.go b/internal/pkg/config/root.go new file mode 100644 index 00000000..67c4447a --- /dev/null +++ b/internal/pkg/config/root.go @@ -0,0 +1,183 @@ +// Package config reads, parses, and represents the warewulf.conf +// config file. +// +// warewulf.conf is a yaml-formatted configuration file that includes +// configuration for the Warewulf daemon and commands, as well as the +// DHCP, TFTP and NFS services that Warewulf manages. +package config + + +import ( + "fmt" + "net" + "os" + "reflect" + + "github.com/pkg/errors" + + "github.com/creasty/defaults" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "gopkg.in/yaml.v2" +) + + +var cachedConf RootConf + + +// RootConf is the main Warewulf configuration structure. It stores +// some information about the Warewulf server locally, and has +// [WarewulfConf], [DHCPConf], [TFTPConf], and [NFSConf] sub-sections. +type RootConf struct { + WWInternal int `yaml:"WW_INTERNAL"` + Comment string `yaml:"comment,omitempty"` + Ipaddr string `yaml:"ipaddr"` + Ipaddr6 string `yaml:"ipaddr6,omitempty"` + Netmask string `yaml:"netmask"` + Network string `yaml:"network,omitempty"` + Ipv6net string `yaml:"ipv6net,omitempty"` + Fqdn string `yaml:"fqdn,omitempty"` + Warewulf *WarewulfConf `yaml:"warewulf"` + DHCP *DHCPConf `yaml:"dhcp"` + TFTP *TFTPConf `yaml:"tftp"` + NFS *NFSConf `yaml:"nfs"` + MountsContainer []*MountEntry `yaml:"container mounts" default:"[{\"source\": \"/etc/resolv.conf\", \"dest\": \"/etc/resolv.conf\"}]"` + Paths *BuildConfig `yaml:"paths"` + + fromFile bool +} + + +// New caches and returns a new [RootConf] initialized with empty +// values, clearing replacing any previously cached value. +func New() (*RootConf) { + cachedConf = RootConf{} + cachedConf.fromFile = false + cachedConf.Warewulf = new(WarewulfConf) + cachedConf.DHCP = new(DHCPConf) + cachedConf.TFTP = new(TFTPConf) + cachedConf.NFS = new(NFSConf) + cachedConf.Paths = new(BuildConfig) + if err := defaults.Set(&cachedConf); err != nil { + panic(err) + } + return &cachedConf +} + + +// Get returns a previously cached [RootConf] if it exists, or returns +// a new RootConf. +func Get() (*RootConf) { + // NOTE: This function can be called before any log level is set + // so using wwlog.Verbose or wwlog.Debug won't work + if reflect.ValueOf(cachedConf).IsZero() { + cachedConf = *New() + } + return &cachedConf +} + + +// Read populates [RootConf] with the values from a configuration +// file. +func (conf *RootConf) Read(confFileName string) (error) { + wwlog.Debug("Reading warewulf.conf from: %s", confFileName) + if data, err := os.ReadFile(confFileName); err != nil { + return err + } else if err := conf.Parse(data); err != nil { + return err + } else { + conf.fromFile = true + return nil + } +} + + +// Parse populates [RootConf] with the values from a yaml document. +func (conf *RootConf) Parse(data []byte) (error) { + // ipxe binaries are merged not overwritten, store defaults separate + defIpxe := make(map[string]string) + for k, v := range conf.TFTP.IpxeBinaries { + defIpxe[k] = v + delete(conf.TFTP.IpxeBinaries, k) + } + if err := yaml.Unmarshal(data, &conf); err != nil { + return err + } + if len(conf.TFTP.IpxeBinaries) == 0 { + conf.TFTP.IpxeBinaries = defIpxe + } + return nil +} + + +// SetDynamicDefaults populates [RootConf] with plausible defaults for +// the runtime environment. +func (conf *RootConf) SetDynamicDefaults() (err error) { + if conf.Ipaddr == "" || conf.Netmask == "" || conf.Network == "" { + var mask net.IPMask + var network *net.IPNet + var ipaddr net.IP + + if conf.Ipaddr == "" { + wwlog.Verbose("Configuration has no valid network, going to dynamic values") + conn, _ := net.Dial("udp", "8.8.8.8:80") + defer conn.Close() + ipaddr = conn.LocalAddr().(*net.UDPAddr).IP + mask = ipaddr.DefaultMask() + sz, _ := mask.Size() + conf.Ipaddr = ipaddr.String() + fmt.Sprintf("/%d", sz) + } + _, network, err = net.ParseCIDR(conf.Ipaddr) + if err == nil { + mask = network.Mask + } else { + return errors.Wrap(err, "Couldn't parse IP address") + } + if conf.Netmask == "" { + conf.Netmask = fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]) + wwlog.Verbose("Netmask address is not configured in warewulf.conf, using %s", conf.Netmask) + } + if conf.Network == "" { + conf.Network = network.IP.String() + wwlog.Verbose("Network is not configured in warewulf.conf, using %s", conf.Network) + } + } + if conf.DHCP.RangeStart == "" && conf.DHCP.RangeEnd == "" { + start := net.ParseIP(conf.Network).To4() + start[3] += 1 + if start.Equal(net.ParseIP(conf.Ipaddr)) { + start[3] += 1 + } + conf.DHCP.RangeStart = start.String() + wwlog.Verbose("dhpd start is not configured in warewulf.conf, using %s", conf.DHCP.RangeStart) + sz, _ := net.IPMask(net.ParseIP(conf.Netmask).To4()).Size() + range_end := (1 << (32 - sz)) / 8 + if range_end > 127 { + range_end = 127 + } + end := net.ParseIP(conf.Network).To4() + end[3] += byte(range_end) + conf.DHCP.RangeEnd = end.String() + wwlog.Verbose("dhpd end is not configured in warewulf.conf, using %s", conf.DHCP.RangeEnd) + + } + // check validity of ipv6 net + if conf.Ipaddr6 != "" { + _, ipv6net, err := net.ParseCIDR(conf.Ipaddr6) + if err != nil { + wwlog.Error("Invalid ipv6 address specified, mut be CIDR notation: %s", conf.Ipaddr6) + return errors.New("invalid ipv6 network") + } + if msize, _ := ipv6net.Mask.Size(); msize > 64 { + wwlog.Error("ipv6 mask size must be smaller than 64") + return errors.New("invalid ipv6 network size") + } + } + return +} + + +// InitializedFromFile returns true if [RootConf] memory was read from +// a file, or false otherwise. +func (conf *RootConf) InitializedFromFile() bool { + return conf.fromFile +} diff --git a/internal/pkg/config/root_test.go b/internal/pkg/config/root_test.go new file mode 100644 index 00000000..d1d0f3d1 --- /dev/null +++ b/internal/pkg/config/root_test.go @@ -0,0 +1,174 @@ +package config + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + + +func TestDefaultRootConf(t *testing.T) { + conf := New() + + assert.Equal(t, 9983, conf.Warewulf.Port) + assert.True(t, conf.Warewulf.Secure) + assert.Equal(t, 60, conf.Warewulf.UpdateInterval) + assert.True(t, conf.Warewulf.AutobuildOverlays) + assert.True(t, conf.Warewulf.EnableHostOverlay) + assert.False(t, conf.Warewulf.Syslog) + assert.NotEmpty(t, conf.Warewulf.DataStore) + + assert.True(t, conf.DHCP.Enabled) + assert.Equal(t, "default", conf.DHCP.Template) + assert.Empty(t, conf.DHCP.RangeStart) + assert.Empty(t, conf.DHCP.RangeEnd) + assert.Equal(t, "dhcpd", conf.DHCP.SystemdName) + + assert.True(t, conf.TFTP.Enabled) + assert.NotEmpty(t, conf.TFTP.TftpRoot) + assert.Equal(t, "tftp", conf.TFTP.SystemdName) + assert.NotEmpty(t, conf.TFTP.IpxeBinaries["00:00"]) + assert.NotEmpty(t, conf.TFTP.IpxeBinaries["00:07"]) + assert.NotEmpty(t, conf.TFTP.IpxeBinaries["00:09"]) + assert.NotEmpty(t, conf.TFTP.IpxeBinaries["00:0B"]) + + assert.True(t, conf.NFS.Enabled) + assert.Empty(t, conf.NFS.ExportsExtended) + assert.Equal(t, "nfsd", conf.NFS.SystemdName) + + assert.Equal(t, "/etc/resolv.conf", conf.MountsContainer[0].Source) + assert.Equal(t, "/etc/resolv.conf", conf.MountsContainer[0].Dest) + assert.False(t, conf.MountsContainer[0].ReadOnly) + assert.Empty(t, conf.MountsContainer[0].Options) + + assert.NotEmpty(t, conf.Paths.Bindir) + assert.NotEmpty(t, conf.Paths.Sysconfdir) + assert.NotEmpty(t, conf.Paths.Datadir) + assert.NotEmpty(t, conf.Paths.Localstatedir) + assert.NotEmpty(t, conf.Paths.Srvdir) + assert.NotEmpty(t, conf.Paths.Tftpdir) + assert.NotEmpty(t, conf.Paths.Firewallddir) + assert.NotEmpty(t, conf.Paths.Systemddir) + assert.NotEmpty(t, conf.Paths.WWOverlaydir) + assert.NotEmpty(t, conf.Paths.WWChrootdir) + assert.NotEmpty(t, conf.Paths.WWProvisiondir) + assert.NotEmpty(t, conf.Paths.Version) + assert.NotEmpty(t, conf.Paths.Release) + assert.NotEmpty(t, conf.Paths.WWClientdir) +} + + +func TestInitializedFromFile(t *testing.T) { + example_warewulf_conf := "WW_INTERNAL: 43" + tempWarewulfConf, warewulfConfErr := os.CreateTemp("", "warewulf.conf-") + assert.NoError(t, warewulfConfErr) + defer os.Remove(tempWarewulfConf.Name()) + _, warewulfConfErr = tempWarewulfConf.Write([]byte(example_warewulf_conf)) + assert.NoError(t, warewulfConfErr) + assert.NoError(t, tempWarewulfConf.Sync()) + + conf := New() + assert.False(t, conf.InitializedFromFile()) + assert.NoError(t, conf.Read(tempWarewulfConf.Name())) + assert.True(t, conf.InitializedFromFile()) +} + + +func TestExampleRootConf(t *testing.T) { + example_warewulf_conf := `WW_INTERNAL: 43 +ipaddr: 192.168.200.1 +netmask: 255.255.255.0 +network: 192.168.200.0 +warewulf: + port: 9873 + secure: false + update interval: 60 + autobuild overlays: true + host overlay: true + syslog: false +dhcp: + enabled: true + range start: 192.168.200.50 + range end: 192.168.200.99 + systemd name: dhcpd +tftp: + enabled: true + systemd name: tftp +nfs: + enabled: true + export paths: + - path: /home + export options: rw,sync + mount options: defaults + mount: true + - path: /opt + export options: ro,sync,no_root_squash + mount options: defaults + mount: false + systemd name: nfs-server +container mounts: + - source: /etc/resolv.conf + dest: /etc/resolv.conf + readonly: true` + + tempWarewulfConf, warewulfConfErr := os.CreateTemp("", "warewulf.conf-") + assert.NoError(t, warewulfConfErr) + defer os.Remove(tempWarewulfConf.Name()) + _, warewulfConfErr = tempWarewulfConf.Write([]byte(example_warewulf_conf)) + assert.NoError(t, warewulfConfErr) + assert.NoError(t, tempWarewulfConf.Sync()) + + conf := New() + assert.NoError(t, conf.Read(tempWarewulfConf.Name())) + + assert.Equal(t, "192.168.200.1", conf.Ipaddr) + assert.Equal(t, "255.255.255.0", conf.Netmask) + assert.Equal(t, "192.168.200.0", conf.Network) + + assert.Equal(t, 9873, conf.Warewulf.Port) + assert.False(t, conf.Warewulf.Secure) + assert.Equal(t, 60, conf.Warewulf.UpdateInterval) + assert.True(t, conf.Warewulf.AutobuildOverlays) + assert.True(t, conf.Warewulf.EnableHostOverlay) + assert.False(t, conf.Warewulf.Syslog) + + assert.True(t, conf.DHCP.Enabled) + assert.Equal(t, "192.168.200.50", conf.DHCP.RangeStart) + assert.Equal(t, "192.168.200.99", conf.DHCP.RangeEnd) + assert.Equal(t, "dhcpd", conf.DHCP.SystemdName) + + assert.True(t, conf.TFTP.Enabled) + assert.Equal(t, "tftp", conf.TFTP.SystemdName) + + assert.True(t, conf.NFS.Enabled) + assert.Equal(t, "/home", conf.NFS.ExportsExtended[0].Path) + assert.Equal(t, "rw,sync", conf.NFS.ExportsExtended[0].ExportOptions) + assert.Equal(t, "defaults", conf.NFS.ExportsExtended[0].MountOptions) + assert.True(t, conf.NFS.ExportsExtended[0].Mount) + assert.Equal(t, "/opt", conf.NFS.ExportsExtended[1].Path) + assert.Equal(t, "ro,sync,no_root_squash", conf.NFS.ExportsExtended[1].ExportOptions) + assert.Equal(t, "defaults", conf.NFS.ExportsExtended[1].MountOptions) + assert.False(t, conf.NFS.ExportsExtended[1].Mount) + assert.Equal(t, "nfs-server", conf.NFS.SystemdName) + + assert.Equal(t, "/etc/resolv.conf", conf.MountsContainer[0].Source) + assert.Equal(t, "/etc/resolv.conf", conf.MountsContainer[0].Dest) + assert.True(t, conf.MountsContainer[0].ReadOnly) +} + + +func TestCache(t *testing.T) { + confOrig := New() + confCached := Get() + + assert.Equal(t, 9983, confOrig.Warewulf.Port) + assert.Equal(t, 9983, confCached.Warewulf.Port) + + confOrig.Warewulf.Port = 9999 + assert.Equal(t, 9999, confCached.Warewulf.Port) + assert.Equal(t, 9999, Get().Warewulf.Port) + + New() + assert.NotEqual(t, 9999, Get().Warewulf.Port) +} diff --git a/internal/pkg/config/tftp.go b/internal/pkg/config/tftp.go new file mode 100644 index 00000000..1c898e39 --- /dev/null +++ b/internal/pkg/config/tftp.go @@ -0,0 +1,13 @@ +package config + + +// TFTPConf represents that configuration for the TFTP service that +// Warewulf will configure. +type TFTPConf struct { + Enabled bool `yaml:"enabled" default:"true"` + TftpRoot string `yaml:"tftproot" default:"/var/lib/tftpboot"` + SystemdName string `yaml:"systemd name" default:"tftp"` + + // Path is relative to buildconfig.DATADIR() + IpxeBinaries map[string]string `yaml:"ipxe" default:"{\"00:09\": \"ipxe/ipxe-snponly-x86_64.efi\",\"00:00\": \"ipxe/undionly.kpxe\",\"00:0B\": \"ipxe/arm64-efi/snponly.efi\",\"00:07\": \"ipxe/ipxe-snponly-x86_64.efi\"}"` +} diff --git a/internal/pkg/config/warewulf.go b/internal/pkg/config/warewulf.go new file mode 100644 index 00000000..75d6b6ba --- /dev/null +++ b/internal/pkg/config/warewulf.go @@ -0,0 +1,14 @@ +package config + + +// WarewulfConf adds additional Warewulf-specific configuration to +// BaseConf. +type WarewulfConf struct { + Port int `yaml:"port" default:"9983"` + Secure bool `yaml:"secure" default:"true"` + UpdateInterval int `yaml:"update interval" default:"60"` + AutobuildOverlays bool `yaml:"autobuild overlays" default:"true"` + EnableHostOverlay bool `yaml:"host overlay" default:"true"` + Syslog bool `yaml:"syslog" default:"false"` + DataStore string `yaml:"datastore" default:"/var/lib/warewulf"` +} diff --git a/internal/pkg/configure/dhcp.go b/internal/pkg/configure/dhcp.go index 4d2f42df..02613f05 100644 --- a/internal/pkg/configure/dhcp.go +++ b/internal/pkg/configure/dhcp.go @@ -6,7 +6,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/overlay" "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/pkg/errors" ) @@ -15,21 +15,21 @@ import ( Configures the dhcpd server, when show is set to false, else the dhcp configuration is checked. */ -func Dhcp() (err error) { +func DHCP() (err error) { - controller := warewulfconf.New() + controller := warewulfconf.Get() - if !controller.Dhcp.Enabled { + if !controller.DHCP.Enabled { wwlog.Info("This system is not configured as a Warewulf DHCP controller") os.Exit(1) } - if controller.Dhcp.RangeStart == "" { + if controller.DHCP.RangeStart == "" { wwlog.Error("Configuration is not defined: `dhcpd range start`") os.Exit(1) } - if controller.Dhcp.RangeEnd == "" { + if controller.DHCP.RangeEnd == "" { wwlog.Error("Configuration is not defined: `dhcpd range end`") os.Exit(1) } @@ -42,7 +42,7 @@ func Dhcp() (err error) { wwlog.Info("host overlays are disabled, did not modify/create dhcpd configuration") } fmt.Printf("Enabling and restarting the DHCP services\n") - err = util.SystemdStart(controller.Dhcp.SystemdName) + err = util.SystemdStart(controller.DHCP.SystemdName) if err != nil { return errors.Wrap(err, "failed to start") } diff --git a/internal/pkg/configure/nfs.go b/internal/pkg/configure/nfs.go index d7e18469..82f70e84 100644 --- a/internal/pkg/configure/nfs.go +++ b/internal/pkg/configure/nfs.go @@ -5,7 +5,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/overlay" "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/pkg/errors" ) @@ -16,9 +16,9 @@ nfs server. */ func NFS() error { - controller := warewulfconf.New() + controller := warewulfconf.Get() - if controller.Nfs.Enabled { + if controller.NFS.Enabled { if controller.Warewulf.EnableHostOverlay { err := overlay.BuildHostOverlay() if err != nil { @@ -28,13 +28,13 @@ func NFS() error { wwlog.Info("host overlays are disabled, did not modify exports") } fmt.Printf("Enabling and restarting the NFS services\n") - if controller.Nfs.SystemdName == "" { + if controller.NFS.SystemdName == "" { err := util.SystemdStart("nfs-server") if err != nil { return errors.Wrap(err, "failed to start nfs-server") } } else { - err := util.SystemdStart(controller.Nfs.SystemdName) + err := util.SystemdStart(controller.NFS.SystemdName) if err != nil { return errors.Wrap(err, "failed to start") } diff --git a/internal/pkg/configure/ssh.go b/internal/pkg/configure/ssh.go index 10186bc2..1c24a3b2 100644 --- a/internal/pkg/configure/ssh.go +++ b/internal/pkg/configure/ssh.go @@ -6,7 +6,7 @@ import ( "path" "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/pkg/errors" ) @@ -14,7 +14,7 @@ import ( func SSH() error { if os.Getuid() == 0 { fmt.Printf("Updating system keys\n") - conf := warewulfconf.New() + conf := warewulfconf.Get() wwkeydir := path.Join(conf.Paths.Sysconfdir, "warewulf/keys") + "/" err := os.MkdirAll(path.Join(conf.Paths.Sysconfdir, "warewulf/keys"), 0755) diff --git a/internal/pkg/configure/tftp.go b/internal/pkg/configure/tftp.go index 74bed889..6cfb9561 100644 --- a/internal/pkg/configure/tftp.go +++ b/internal/pkg/configure/tftp.go @@ -6,12 +6,12 @@ import ( "path" "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" ) func TFTP() error { - controller := warewulfconf.New() + controller := warewulfconf.Get() var tftpdir string = path.Join(controller.Paths.Tftpdir, "warewulf") err := os.MkdirAll(tftpdir, 0755) @@ -22,24 +22,24 @@ func TFTP() error { fmt.Printf("Writing PXE files to: %s\n", tftpdir) copyCheck := make(map[string]bool) - for _, f := range controller.Tftp.IpxeBinaries { + for _, f := range controller.TFTP.IpxeBinaries { if copyCheck[f] { continue } copyCheck[f] = true - err = util.SafeCopyFile(path.Join(controller.Paths.Datadir, f), path.Join(tftpdir, f)) + err = util.SafeCopyFile(path.Join(controller.Paths.Datadir, f), path.Join(tftpdir, path.Base(f))) if err != nil { wwlog.Warn("ipxe binary could not be copied, booting may not work: %s", err) } } - if !controller.Tftp.Enabled { + if !controller.TFTP.Enabled { wwlog.Info("Warewulf does not auto start TFTP services due to disable by warewulf.conf") os.Exit(0) } fmt.Printf("Enabling and restarting the TFTP services\n") - err = util.SystemdStart(controller.Tftp.SystemdName) + err = util.SystemdStart(controller.TFTP.SystemdName) if err != nil { wwlog.Error("%s", err) return err diff --git a/internal/pkg/container/config.go b/internal/pkg/container/config.go index ecd751fa..f166e643 100644 --- a/internal/pkg/container/config.go +++ b/internal/pkg/container/config.go @@ -3,11 +3,11 @@ package container import ( "path" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" ) func SourceParentDir() string { - conf := warewulfconf.New() + conf := warewulfconf.Get() return conf.Paths.WWChrootdir } @@ -20,7 +20,7 @@ func RootFsDir(name string) string { } func ImageParentDir() string { - conf := warewulfconf.New() + conf := warewulfconf.Get() return path.Join(conf.Paths.WWProvisiondir, "container/") } diff --git a/internal/pkg/container/imprt.go b/internal/pkg/container/imprt.go index 7a050f49..2f62b438 100644 --- a/internal/pkg/container/imprt.go +++ b/internal/pkg/container/imprt.go @@ -9,13 +9,13 @@ import ( "github.com/containers/storage/drivers/copy" "github.com/pkg/errors" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/oci" "github.com/hpcng/warewulf/internal/pkg/util" ) func ImportDocker(uri string, name string, sCtx *types.SystemContext) error { - OciBlobCacheDir := warewulfconf.DataStore() + "/oci" + OciBlobCacheDir := warewulfconf.Get().Warewulf.DataStore + "/oci" err := os.MkdirAll(OciBlobCacheDir, 0755) if err != nil { diff --git a/internal/pkg/container/mountpoints.go b/internal/pkg/container/mountpoints.go index 727ff35c..d7281758 100644 --- a/internal/pkg/container/mountpoints.go +++ b/internal/pkg/container/mountpoints.go @@ -3,7 +3,7 @@ package container import ( "strings" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" ) diff --git a/internal/pkg/kernel/kernel.go b/internal/pkg/kernel/kernel.go index 32a1da47..a735f384 100644 --- a/internal/pkg/kernel/kernel.go +++ b/internal/pkg/kernel/kernel.go @@ -12,7 +12,7 @@ import ( "github.com/pkg/errors" "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" ) @@ -28,7 +28,7 @@ var ( ) func KernelImageTopDir() string { - conf := warewulfconf.New() + conf := warewulfconf.Get() return path.Join(conf.Paths.WWProvisiondir, "kernel") } diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index 1b44e454..612eacbd 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -8,7 +8,7 @@ import ( "sort" "strings" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" "gopkg.in/yaml.v2" @@ -41,7 +41,7 @@ defaultnode: onboot: true` func init() { - conf := warewulfconf.New() + conf := warewulfconf.Get() if ConfigFile == "" { ConfigFile = path.Join(conf.Paths.Sysconfdir, "warewulf/nodes.conf") } diff --git a/internal/pkg/oci/defaults.go b/internal/pkg/oci/defaults.go index ee6dc0e6..f7158a6f 100644 --- a/internal/pkg/oci/defaults.go +++ b/internal/pkg/oci/defaults.go @@ -1,11 +1,13 @@ package oci -import "github.com/hpcng/warewulf/internal/pkg/warewulfconf" -import "path/filepath" +import ( + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" + "path/filepath" +) -var defaultCachePath = filepath.Join(warewulfconf.DataStore(), "/container-cache/oci/") +var defaultCachePath = filepath.Join(warewulfconf.Get().Warewulf.DataStore, "/container-cache/oci/") const ( blobPrefix = "blobs" rootfsPrefix = "rootfs" -) \ No newline at end of file +) diff --git a/internal/pkg/overlay/config.go b/internal/pkg/overlay/config.go index 91452e4a..d2a9e924 100644 --- a/internal/pkg/overlay/config.go +++ b/internal/pkg/overlay/config.go @@ -5,11 +5,11 @@ import ( "path" "strings" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" ) func OverlaySourceTopDir() string { - conf := warewulfconf.New() + conf := warewulfconf.Get() return conf.Paths.WWOverlaydir } @@ -31,6 +31,6 @@ func OverlaySourceDir(overlayName string) string { Returns the overlay name of the image for a given node */ func OverlayImage(nodeName string, overlayName []string) string { - conf := warewulfconf.New() + conf := warewulfconf.Get() return path.Join(conf.Paths.WWProvisiondir, "overlays/", nodeName, strings.Join(overlayName, "-")+".img") } diff --git a/internal/pkg/overlay/datastructure.go b/internal/pkg/overlay/datastructure.go index c7da9a70..6af7adb2 100644 --- a/internal/pkg/overlay/datastructure.go +++ b/internal/pkg/overlay/datastructure.go @@ -7,7 +7,7 @@ import ( "time" "github.com/hpcng/warewulf/internal/pkg/node" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" ) @@ -28,10 +28,10 @@ type TemplateStruct struct { Network string NetworkCIDR string Ipv6 bool - Dhcp warewulfconf.DhcpConf - Nfs warewulfconf.NfsConf + Dhcp warewulfconf.DHCPConf + Nfs warewulfconf.NFSConf Warewulf warewulfconf.WarewulfConf - Tftp warewulfconf.TftpConf + Tftp warewulfconf.TFTPConf Paths warewulfconf.BuildConfig AllNodes []node.NodeInfo node.NodeConf @@ -44,7 +44,7 @@ Initialize an TemplateStruct with the given node.NodeInfo */ func InitStruct(nodeInfo node.NodeInfo) TemplateStruct { var tstruct TemplateStruct - controller := warewulfconf.New() + controller := warewulfconf.Get() nodeDB, err := node.New() if err != nil { wwlog.Error("%s", err) @@ -60,9 +60,9 @@ func InitStruct(nodeInfo node.NodeInfo) TemplateStruct { tstruct.Hostname = nodeInfo.Id.Get() // Backwards compatibility for templates using "Keys" tstruct.AllNodes = allNodes - tstruct.Nfs = *controller.Nfs - tstruct.Dhcp = *controller.Dhcp - tstruct.Tftp = *controller.Tftp + tstruct.Nfs = *controller.NFS + tstruct.Dhcp = *controller.DHCP + tstruct.Tftp = *controller.TFTP tstruct.Warewulf = *controller.Warewulf tstruct.Ipaddr = controller.Ipaddr tstruct.Ipaddr6 = controller.Ipaddr6 diff --git a/internal/pkg/overlay/funcmap.go b/internal/pkg/overlay/funcmap.go index ebdb1b1a..4acf6705 100644 --- a/internal/pkg/overlay/funcmap.go +++ b/internal/pkg/overlay/funcmap.go @@ -8,7 +8,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/container" "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" ) @@ -17,7 +17,7 @@ Reads a file file from the host fs. If the file has nor '/' prefix the path is relative to Paths.SysconfdirTemplates in the file are no evaluated. */ func templateFileInclude(inc string) string { - conf := warewulfconf.New() + conf := warewulfconf.Get() if !strings.HasPrefix(inc, "/") { inc = path.Join(conf.Paths.Sysconfdir, "warewulf", inc) } @@ -35,7 +35,7 @@ is the file to read, the second the abort string Templates in the file are no evaluated. */ func templateFileBlock(inc string, abortStr string) (string, error) { - conf := warewulfconf.New() + conf := warewulfconf.Get() if !strings.HasPrefix(inc, "/") { inc = path.Join(conf.Paths.Sysconfdir, "warewulf", inc) } diff --git a/internal/pkg/overlay/overlay.go b/internal/pkg/overlay/overlay.go index 3754fa9c..0d441cc7 100644 --- a/internal/pkg/overlay/overlay.go +++ b/internal/pkg/overlay/overlay.go @@ -371,6 +371,7 @@ func RenderTemplateFile(fileName string, data TemplateStruct) ( "Include": templateFileInclude, "IncludeFrom": templateContainerFileInclude, "IncludeBlock": templateFileBlock, + "basename": path.Base, "inc": func(i int) int { return i + 1 }, "dec": func(i int) int { return i - 1 }, "file": func(str string) string { return fmt.Sprintf("{{ /* file \"%s\" */ }}", str) }, diff --git a/internal/pkg/version/version.go b/internal/pkg/version/version.go index 6b4771bb..d4b5fb85 100644 --- a/internal/pkg/version/version.go +++ b/internal/pkg/version/version.go @@ -4,14 +4,14 @@ import ( "fmt" "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" ) /* Return the version of wwctl */ func GetVersion() string { - conf := warewulfconf.New() + conf := warewulfconf.Get() return fmt.Sprintf("%s-%s", conf.Paths.Version, conf.Paths.Release) } diff --git a/internal/pkg/warewulfconf/constructors.go b/internal/pkg/warewulfconf/constructors.go deleted file mode 100644 index 2a1e0ff0..00000000 --- a/internal/pkg/warewulfconf/constructors.go +++ /dev/null @@ -1,156 +0,0 @@ -package warewulfconf - -import ( - "fmt" - "net" - "os" - - "github.com/pkg/errors" - - "github.com/creasty/defaults" - "github.com/hpcng/warewulf/internal/pkg/wwlog" - "gopkg.in/yaml.v2" -) - -var cachedConf ControllerConf - -var ConfigFile string - -/* -Creates a new empty ControllerConf object, returns a cached -one if called in a nother context. -*/ -func New() (conf ControllerConf) { - // NOTE: This function can be called before any log level is set - // so using wwlog.Verbose or wwlog.Debug won't work - if !cachedConf.current { - conf.Warewulf = new(WarewulfConf) - conf.Dhcp = new(DhcpConf) - conf.Tftp = new(TftpConf) - conf.Nfs = new(NfsConf) - conf.Paths = new(BuildConfig) - _ = defaults.Set(&conf) - cachedConf = conf - cachedConf.readConf = false - cachedConf.current = true - } else { - // If cached struct isn't empty, use it as the return value - conf = cachedConf - } - return conf -} - -/* -Populate the configuration with the values from the configuration file. -*/ -func (conf *ControllerConf) ReadConf(confFileName string) (err error) { - wwlog.Debug("Reading warewulf.conf from: %s", confFileName) - fileHandle, err := os.ReadFile(confFileName) - if err != nil { - return err - } - return conf.Read(fileHandle) -} - -/* -Populate the configuration with the values from the given yaml information -*/ -func (conf *ControllerConf) Read(data []byte) (err error) { - // ipxe binaries are merged not overwritten, store defaults separate - defIpxe := make(map[string]string) - for k, v := range conf.Tftp.IpxeBinaries { - defIpxe[k] = v - delete(conf.Tftp.IpxeBinaries, k) - } - err = yaml.Unmarshal(data, &conf) - if err != nil { - return - } - err = conf.SetDynamicDefaults() - if err != nil { - return - } - if len(conf.Tftp.IpxeBinaries) == 0 { - conf.Tftp.IpxeBinaries = defIpxe - } - cachedConf = *conf - cachedConf.current = true - cachedConf.readConf = true - return -} - -/* -Set the runtime defaults like IP address of running system to the config -*/ -func (conf *ControllerConf) SetDynamicDefaults() (err error) { - if conf.Ipaddr == "" || conf.Netmask == "" || conf.Network == "" { - var mask net.IPMask - var network *net.IPNet - var ipaddr net.IP - - if conf.Ipaddr == "" { - wwlog.Verbose("Configuration has no valid network, going to dynamic values") - conn, _ := net.Dial("udp", "8.8.8.8:80") - defer conn.Close() - ipaddr = conn.LocalAddr().(*net.UDPAddr).IP - mask = ipaddr.DefaultMask() - sz, _ := mask.Size() - conf.Ipaddr = ipaddr.String() + fmt.Sprintf("/%d", sz) - } - _, network, err = net.ParseCIDR(conf.Ipaddr) - if err == nil { - mask = network.Mask - } else { - return errors.Wrap(err, "Couldn't parse IP address") - } - if conf.Netmask == "" { - conf.Netmask = fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]) - wwlog.Verbose("Netmask address is not configured in warewulf.conf, using %s", conf.Netmask) - } - if conf.Network == "" { - conf.Network = network.IP.String() - wwlog.Verbose("Network is not configured in warewulf.conf, using %s", conf.Network) - } - } - if conf.Dhcp.RangeStart == "" && conf.Dhcp.RangeEnd == "" { - start := net.ParseIP(conf.Network).To4() - start[3] += 1 - if start.Equal(net.ParseIP(conf.Ipaddr)) { - start[3] += 1 - } - conf.Dhcp.RangeStart = start.String() - wwlog.Verbose("dhpd start is not configured in warewulf.conf, using %s", conf.Dhcp.RangeStart) - sz, _ := net.IPMask(net.ParseIP(conf.Netmask).To4()).Size() - range_end := (1 << (32 - sz)) / 8 - if range_end > 127 { - range_end = 127 - } - end := net.ParseIP(conf.Network).To4() - end[3] += byte(range_end) - conf.Dhcp.RangeEnd = end.String() - wwlog.Verbose("dhpd end is not configured in warewulf.conf, using %s", conf.Dhcp.RangeEnd) - - } - // check validity of ipv6 net - if conf.Ipaddr6 != "" { - _, ipv6net, err := net.ParseCIDR(conf.Ipaddr6) - if err != nil { - wwlog.Error("Invalid ipv6 address specified, mut be CIDR notation: %s", conf.Ipaddr6) - return errors.New("invalid ipv6 network") - } - if msize, _ := ipv6net.Mask.Size(); msize > 64 { - wwlog.Error("ipv6 mask size must be smaller than 64") - return errors.New("invalid ipv6 network size") - } - } - cachedConf = *conf - 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 deleted file mode 100644 index 1958a2b5..00000000 --- a/internal/pkg/warewulfconf/datastructure.go +++ /dev/null @@ -1,87 +0,0 @@ -package warewulfconf - -import ( - "github.com/creasty/defaults" -) - -type ControllerConf struct { - WWInternal int `yaml:"WW_INTERNAL"` - Comment string `yaml:"comment,omitempty"` - Ipaddr string `yaml:"ipaddr"` - Ipaddr6 string `yaml:"ipaddr6,omitempty"` - Netmask string `yaml:"netmask"` - Network string `yaml:"network,omitempty"` - Ipv6net string `yaml:"ipv6net,omitempty"` - Fqdn string `yaml:"fqdn,omitempty"` - Warewulf *WarewulfConf `yaml:"warewulf"` - Dhcp *DhcpConf `yaml:"dhcp"` - Tftp *TftpConf `yaml:"tftp"` - Nfs *NfsConf `yaml:"nfs"` - 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 { - Port int `yaml:"port" default:"9983"` - Secure bool `yaml:"secure" default:"true"` - UpdateInterval int `yaml:"update interval" default:"60"` - AutobuildOverlays bool `yaml:"autobuild overlays" default:"true"` - EnableHostOverlay bool `yaml:"host overlay" default:"true"` - Syslog bool `yaml:"syslog" default:"false"` - DataStore string `yaml:"datastore" default:"/var/lib/warewulf"` -} - -type DhcpConf struct { - Enabled bool `yaml:"enabled" default:"true"` - Template string `yaml:"template" default:"default"` - RangeStart string `yaml:"range start,omitempty"` - RangeEnd string `yaml:"range end,omitempty"` - SystemdName string `yaml:"systemd name" default:"dhcpd"` -} - -type TftpConf struct { - Enabled bool `yaml:"enabled" default:"true"` - TftpRoot string `yaml:"tftproot" default:"/var/lib/tftpboot"` - SystemdName string `yaml:"systemd name" default:"tftp"` - // Path is relative to buildconfig.DATADIR() - IpxeBinaries map[string]string `yaml:"ipxe" default:"{\"00:09\": \"x86_64.efi\",\"00:00\": \"x86_64.kpxe\",\"00:0B\": \"arm64.efi\",\"00:07\": \"x86_64.efi\"}"` -} - -type NfsConf struct { - Enabled bool `yaml:"enabled" default:"true"` - ExportsExtended []*NfsExportConf `yaml:"export paths" default:"[]"` - SystemdName string `yaml:"systemd name" default:"nfsd"` -} - -type NfsExportConf struct { - Path string `yaml:"path" default:"/dev/null"` - ExportOptions string `default:"rw,sync,no_subtree_check" yaml:"export options"` - MountOptions string `default:"defaults" yaml:"mount options"` - Mount bool `default:"true" yaml:"mount"` -} - -/* -Describe a mount point for a container exec -*/ -type MountEntry struct { - Source string `yaml:"source" default:"/etc/resolv.conf"` - Dest string `yaml:"dest,omitempty" default:"/etc/resolv.conf"` - ReadOnly bool `yaml:"readonly,omitempty" default:"false"` - Options string `yaml:"options,omitempty"` // ignored at the moment -} - -func (s *NfsConf) Unmarshal(unmarshal func(interface{}) error) error { - if err := defaults.Set(s); err != nil { - return err - } - return nil -} - -// Waste processor cycles to make code more readable - -func DataStore() string { - _ = New() - return cachedConf.Warewulf.DataStore -} diff --git a/internal/pkg/warewulfconf/modifiers.go b/internal/pkg/warewulfconf/modifiers.go deleted file mode 100644 index 457ed7f0..00000000 --- a/internal/pkg/warewulfconf/modifiers.go +++ /dev/null @@ -1,32 +0,0 @@ -package warewulfconf - -import ( - "os" - - "github.com/hpcng/warewulf/internal/pkg/wwlog" - "gopkg.in/yaml.v2" -) - -func (controller *ControllerConf) Persist() error { - - out, err := yaml.Marshal(controller) - if err != nil { - return err - } - - file, err := os.OpenFile(ConfigFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) - if err != nil { - wwlog.Error("%s", err) - os.Exit(1) - } - - defer file.Close() - - _, err = file.WriteString(string(out)+"\n") - if err != nil { - wwlog.Error("Unable to write to warewulf.conf") - return err - } - - return nil -} diff --git a/internal/pkg/warewulfd/daemon.go b/internal/pkg/warewulfd/daemon.go index 34a989b6..bfa16f9d 100644 --- a/internal/pkg/warewulfd/daemon.go +++ b/internal/pkg/warewulfd/daemon.go @@ -11,7 +11,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/util" "github.com/hpcng/warewulf/internal/pkg/version" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/pkg/errors" ) @@ -61,7 +61,7 @@ func DaemonInitLogging() error { wwlog.SetLogLevel(wwlog.SERV) } - conf := warewulfconf.New() + conf := warewulfconf.Get() if conf.Warewulf.Syslog { diff --git a/internal/pkg/warewulfd/provision.go b/internal/pkg/warewulfd/provision.go index c96267cc..60457208 100644 --- a/internal/pkg/warewulfd/provision.go +++ b/internal/pkg/warewulfd/provision.go @@ -10,7 +10,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/container" "github.com/hpcng/warewulf/internal/pkg/kernel" "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" ) @@ -30,7 +30,7 @@ type iPxeTemplate struct { } func ProvisionSend(w http.ResponseWriter, req *http.Request) { - conf := warewulfconf.New() + conf := warewulfconf.Get() rinfo, err := parseReq(req) if err != nil { diff --git a/internal/pkg/warewulfd/warewulfd.go b/internal/pkg/warewulfd/warewulfd.go index d69aa210..796054b0 100644 --- a/internal/pkg/warewulfd/warewulfd.go +++ b/internal/pkg/warewulfd/warewulfd.go @@ -7,7 +7,7 @@ import ( "strconv" "syscall" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/pkg/errors" ) @@ -58,7 +58,7 @@ func RunServer() error { http.HandleFunc("/overlay-runtime/", ProvisionSend) http.HandleFunc("/status", StatusSend) - conf := warewulfconf.New() + conf := warewulfconf.Get() daemonPort := conf.Warewulf.Port wwlog.Serv("Starting HTTPD REST service on port %d", daemonPort) diff --git a/overlays/host/etc/dhcp/dhcpd.conf.ww b/overlays/host/etc/dhcp/dhcpd.conf.ww index 66b9d22e..4a9637f7 100644 --- a/overlays/host/etc/dhcp/dhcpd.conf.ww +++ b/overlays/host/etc/dhcp/dhcpd.conf.ww @@ -22,7 +22,7 @@ if exists user-class and option user-class = "iPXE" { } else { {{range $type,$name := $.Tftp.IpxeBinaries }} if option architecture-type = {{ $type }} { - filename "/warewulf/{{ $name }}"; + filename "/warewulf/{{ basename $name }}"; } {{ end }} }