From 3526c2bd5f46d83ae435d87700006cac590986f1 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Wed, 1 Mar 2023 16:42:58 +0100 Subject: [PATCH 01/25] added wwctl genconf used to generate completions Signed-off-by: Christian Goll --- Makefile | 30 ++++++++----------- .../app/wwctl/genconf/completions/main.go | 23 ++++++++++++++ .../app/wwctl/genconf/completions/root.go | 22 ++++++++++++++ internal/app/wwctl/genconf/root.go | 26 ++++++++++++++++ internal/app/wwctl/root.go | 2 ++ 5 files changed, 86 insertions(+), 17 deletions(-) create mode 100644 internal/app/wwctl/genconf/completions/main.go create mode 100644 internal/app/wwctl/genconf/completions/root.go create mode 100644 internal/app/wwctl/genconf/root.go diff --git a/Makefile b/Makefile index 61e470bc..3d57dcb8 100644 --- a/Makefile +++ b/Makefile @@ -88,6 +88,11 @@ GO_TOOLS_VENDOR := $(addprefix vendor/, $(GO_TOOLS)) GOLANGCI_LINT := $(TOOLS_BIN)/golangci-lint GOLANGCI_LINT_VERSION := v1.50.0 +# helper functions +godeps=$(shell go list -deps -f '{{if not .Standard}}{{ $$dep := . }}{{range .GoFiles}}{{$$dep.Dir}}/{{.}} {{end}}{{end}}' $(1) | sed "s%${PWD}/%%g") +WWCTL_DEPS:=$(call godeps,cmd/wwctl/main.go) +WWCLIENT_DEPS:=$(call godeps,cmd/wwclient/main.go) + # use GOPROXY for older git clients and speed up downloads GOPROXY ?= https://proxy.golang.org export GOPROXY @@ -96,7 +101,7 @@ export GOPROXY WW_GO_BUILD_TAGS := containers_image_openpgp containers_image_ostree # Default target -all: config vendor wwctl wwclient bash_completion.d man_pages config_defaults print_defaults wwapid wwapic wwapird print_mnts +all: config vendor wwctl wwclient man_pages config_defaults print_defaults wwapid wwapic wwapird print_mnts # Validate source and build all packages build: lint test-it vet all @@ -197,7 +202,7 @@ files: all install -m 0644 include/firewalld/warewulf.xml $(DESTDIR)$(FIREWALLDDIR) install -m 0644 include/systemd/warewulfd.service $(DESTDIR)$(SYSTEMDDIR) install -m 0644 LICENSE.md $(DESTDIR)$(WWDOCDIR) - cp bash_completion.d/warewulf $(DESTDIR)$(BASHCOMPDIR) + ./wwctl genconf completions > $(DESTDIR)$(BASHCOMPDIR)/wwctl cp man_pages/*.1* $(DESTDIR)$(MANDIR)/man1/ cp man_pages/*.5* $(DESTDIR)$(MANDIR)/man5/ install -m 0644 staticfiles/README-ipxe.md $(DESTDIR)$(WWDATADIR)/ipxe @@ -210,25 +215,18 @@ init: cp -r tftpboot/* $(WWTFTPDIR)/ipxe/ restorecon -r $(WWTFTPDIR) -wwctl: - cd cmd/wwctl; GOOS=linux go build -mod vendor -tags "$(WW_GO_BUILD_TAGS)" -o ../../wwctl +wwctl: $(WWCTL_DEPS) + @echo Building "$@" + @cd cmd/wwctl; GOOS=linux go build -mod vendor -tags "$(WW_GO_BUILD_TAGS)" -o ../../wwctl -wwclient: - cd cmd/wwclient; CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -ldflags "-extldflags -static \ +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 install_wwclient: wwclient install -m 0755 wwclient $(DESTDIR)$(WWOVERLAYDIR)/wwinit/$(WWCLIENTDIR)/wwclient -bash_completion: - cd cmd/bash_completion && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=./etc/warewulf.conf'\ - -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=./etc/nodes.conf'"\ - -mod vendor -tags "$(WW_GO_BUILD_TAGS)" -o ../../bash_completion - -bash_completion.d: bash_completion - install -d -m 0755 bash_completion.d - ./bash_completion bash_completion.d/warewulf - man_page: cd cmd/man_page && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=./etc/warewulf.conf'\ -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=./etc/nodes.conf'"\ @@ -297,8 +295,6 @@ contclean: rm -f wwctl rm -rf .dist rm -f $(WAREWULF)-$(VERSION).tar.gz - rm -f bash_completion - rm -rf bash_completion.d rm -f man_page rm -rf man_pages rm -f warewulf.spec diff --git a/internal/app/wwctl/genconf/completions/main.go b/internal/app/wwctl/genconf/completions/main.go new file mode 100644 index 00000000..53827131 --- /dev/null +++ b/internal/app/wwctl/genconf/completions/main.go @@ -0,0 +1,23 @@ +package completions + +import ( + "os" + + "github.com/spf13/cobra" +) + +func CobraRunE(cmd *cobra.Command, args []string) error { + myArg := "bash" + if len(args) == 1 { + myArg = args[0] + } + switch myArg { + case "zsh": + cmd.GenZshCompletion(os.Stdout) + case "fish": + cmd.GenFishCompletion(os.Stdout, true) + default: + cmd.GenBashCompletion(os.Stdout) + } + return nil +} diff --git a/internal/app/wwctl/genconf/completions/root.go b/internal/app/wwctl/genconf/completions/root.go new file mode 100644 index 00000000..1e22f904 --- /dev/null +++ b/internal/app/wwctl/genconf/completions/root.go @@ -0,0 +1,22 @@ +package completions + +import "github.com/spf13/cobra" + +var ( + baseCmd = &cobra.Command{ + Use: "completions", + Short: "shell completion", + Long: "This command generates the bash completions if no argument is given.", + RunE: CobraRunE, + Args: cobra.MaximumNArgs(1), + Aliases: []string{"bash"}, + } + Zsh bool +) + +func init() { +} + +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/app/wwctl/genconf/root.go b/internal/app/wwctl/genconf/root.go new file mode 100644 index 00000000..3584164d --- /dev/null +++ b/internal/app/wwctl/genconf/root.go @@ -0,0 +1,26 @@ +package genconf + +import ( + "github.com/hpcng/warewulf/internal/app/wwctl/genconf/completions" + "github.com/spf13/cobra" +) + +var ( + baseCmd = &cobra.Command{ + Use: "genconfig", + Short: "Generate various configurations", + Long: "This command will allow you to generate different configurations like bash-completions.", + Args: cobra.ExactArgs(0), + Aliases: []string{"cnf"}, + } + ListFull bool +) + +func init() { + baseCmd.AddCommand(completions.GetCommand()) +} + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go index cc8927d6..f60918b1 100644 --- a/internal/app/wwctl/root.go +++ b/internal/app/wwctl/root.go @@ -3,6 +3,7 @@ package wwctl import ( "github.com/hpcng/warewulf/internal/app/wwctl/configure" "github.com/hpcng/warewulf/internal/app/wwctl/container" + "github.com/hpcng/warewulf/internal/app/wwctl/genconf" "github.com/hpcng/warewulf/internal/app/wwctl/kernel" "github.com/hpcng/warewulf/internal/app/wwctl/node" "github.com/hpcng/warewulf/internal/app/wwctl/overlay" @@ -52,6 +53,7 @@ func init() { rootCmd.AddCommand(server.GetCommand()) rootCmd.AddCommand(version.GetCommand()) rootCmd.AddCommand(ssh.GetCommand()) + rootCmd.AddCommand(genconf.GetCommand()) } // GetRootCommand returns the root cobra.Command for the application. From ee7bc75bd72e5c4c847d969403547dbeb35ce8f9 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Thu, 2 Mar 2023 11:01:11 +0100 Subject: [PATCH 02/25] generate man pages via sub command Signed-off-by: Christian Goll --- Makefile | 18 ++++----------- cmd/bash_completion/bash_completion.go | 23 ------------------- cmd/man_page/man_page.go | 17 -------------- .../app/wwctl/genconf/completions/main.go | 10 ++++---- internal/app/wwctl/genconf/man/main.go | 15 ++++++++++++ internal/app/wwctl/genconf/man/root.go | 23 +++++++++++++++++++ internal/app/wwctl/genconf/root.go | 5 +++- internal/app/wwctl/root.go | 19 --------------- 8 files changed, 52 insertions(+), 78 deletions(-) delete mode 100644 cmd/bash_completion/bash_completion.go delete mode 100644 cmd/man_page/man_page.go create mode 100644 internal/app/wwctl/genconf/man/main.go create mode 100644 internal/app/wwctl/genconf/man/root.go diff --git a/Makefile b/Makefile index 3d57dcb8..739c16d1 100644 --- a/Makefile +++ b/Makefile @@ -196,6 +196,7 @@ files: all chmod 600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/warewulf/config.ww chmod 750 $(DESTDIR)$(WWOVERLAYDIR)/host install -m 0755 wwctl $(DESTDIR)$(BINDIR) + install -m 0755 wwclient $(DESTDIR)$(WWOVERLAYDIR)/wwinit/$(WWCLIENTDIR)/wwclient install -m 0755 wwapic $(DESTDIR)$(BINDIR) install -m 0755 wwapid $(DESTDIR)$(BINDIR) install -m 0755 wwapird $(DESTDIR)$(BINDIR) @@ -224,17 +225,9 @@ wwclient: $(WWCLIENT_DEPS) @ 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 -install_wwclient: wwclient - install -m 0755 wwclient $(DESTDIR)$(WWOVERLAYDIR)/wwinit/$(WWCLIENTDIR)/wwclient - -man_page: - cd cmd/man_page && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=./etc/warewulf.conf'\ - -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=./etc/nodes.conf'"\ - -mod vendor -tags "$(WW_GO_BUILD_TAGS)" -o ../../man_page - -man_pages: man_page +man_pages: wwctl install -d man_pages - ./man_page ./man_pages + ./wwctl genconfig man man_pages cp docs/man/man5/*.5 ./man_pages/ cd man_pages; for i in wwctl*1 *.5; do echo "Compressing manpage: $$i"; gzip --force $$i; done @@ -272,7 +265,7 @@ dist: vendor config ## sudo make install ## sudo ldconfig # refresh shared library cache. ## To setup protoc-gen-grpc-gateway, see https://github.com/grpc-ecosystem/grpc-gateway -proto: +proto: rm -rf internal/pkg/api/routes/wwapiv1/ protoc -I internal/pkg/api/routes/v1 -I=. \ --grpc-gateway_out=. \ @@ -295,7 +288,6 @@ contclean: rm -f wwctl rm -rf .dist rm -f $(WAREWULF)-$(VERSION).tar.gz - rm -f man_page rm -rf man_pages rm -f warewulf.spec rm -f config @@ -309,6 +301,6 @@ contclean: clean: contclean rm -rf vendor -install: files install_wwclient +install: files debinstall: files debfiles diff --git a/cmd/bash_completion/bash_completion.go b/cmd/bash_completion/bash_completion.go deleted file mode 100644 index 752884bc..00000000 --- a/cmd/bash_completion/bash_completion.go +++ /dev/null @@ -1,23 +0,0 @@ -// usage: ./bash_completion -package main - -import ( - "fmt" - "github.com/hpcng/warewulf/internal/app/wwctl" - "os" -) - -func main() { - fh, err := os.Create(os.Args[1]) - if err != nil { - fmt.Println(err) - return - } - - defer fh.Close() - - if err := wwctl.GenBashCompletion(fh); err != nil { - fmt.Println(err) - return - } -} diff --git a/cmd/man_page/man_page.go b/cmd/man_page/man_page.go deleted file mode 100644 index 8a35a3be..00000000 --- a/cmd/man_page/man_page.go +++ /dev/null @@ -1,17 +0,0 @@ -// Generate man pages for wwctl command. -// usage: ./man_page -package main - -import ( - "fmt" - "github.com/hpcng/warewulf/internal/app/wwctl" - "os" -) - -func main() { - - if err := wwctl.GenManTree(os.Args[1]); err != nil { - fmt.Println(err) - return - } -} diff --git a/internal/app/wwctl/genconf/completions/main.go b/internal/app/wwctl/genconf/completions/main.go index 53827131..01557f52 100644 --- a/internal/app/wwctl/genconf/completions/main.go +++ b/internal/app/wwctl/genconf/completions/main.go @@ -6,18 +6,18 @@ import ( "github.com/spf13/cobra" ) -func CobraRunE(cmd *cobra.Command, args []string) error { +func CobraRunE(cmd *cobra.Command, args []string) (err error) { myArg := "bash" if len(args) == 1 { myArg = args[0] } switch myArg { case "zsh": - cmd.GenZshCompletion(os.Stdout) + err = cmd.Parent().Parent().GenZshCompletion(os.Stdout) case "fish": - cmd.GenFishCompletion(os.Stdout, true) + err = cmd.Parent().Parent().GenFishCompletion(os.Stdout, true) default: - cmd.GenBashCompletion(os.Stdout) + err = cmd.Parent().Parent().GenBashCompletion(os.Stdout) } - return nil + return } diff --git a/internal/app/wwctl/genconf/man/main.go b/internal/app/wwctl/genconf/man/main.go new file mode 100644 index 00000000..3e752307 --- /dev/null +++ b/internal/app/wwctl/genconf/man/main.go @@ -0,0 +1,15 @@ +package man + +import ( + "github.com/spf13/cobra" + "github.com/spf13/cobra/doc" +) + +func CobraRunE(cmd *cobra.Command, args []string) (err error) { + header := &doc.GenManHeader{ + Title: "WWCTL", + Section: "1", + } + err = doc.GenManTree(cmd.Parent().Parent(), header, args[0]) + return +} diff --git a/internal/app/wwctl/genconf/man/root.go b/internal/app/wwctl/genconf/man/root.go new file mode 100644 index 00000000..d450e5d3 --- /dev/null +++ b/internal/app/wwctl/genconf/man/root.go @@ -0,0 +1,23 @@ +package man + +import ( + "github.com/spf13/cobra" +) + +var ( + baseCmd = &cobra.Command{ + Use: "man", + Short: "manpage generation", + Long: "This command generates the man pages for all commands, needs target dir as argument", + RunE: CobraRunE, + Args: cobra.ExactArgs(1), + Aliases: []string{"man_pages"}, + } +) + +func init() { +} + +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/app/wwctl/genconf/root.go b/internal/app/wwctl/genconf/root.go index 3584164d..0c8afb2f 100644 --- a/internal/app/wwctl/genconf/root.go +++ b/internal/app/wwctl/genconf/root.go @@ -2,6 +2,7 @@ package genconf import ( "github.com/hpcng/warewulf/internal/app/wwctl/genconf/completions" + "github.com/hpcng/warewulf/internal/app/wwctl/genconf/man" "github.com/spf13/cobra" ) @@ -13,11 +14,13 @@ var ( Args: cobra.ExactArgs(0), Aliases: []string{"cnf"}, } - ListFull bool + ListFull bool + WWctlRoot *cobra.Command ) func init() { baseCmd.AddCommand(completions.GetCommand()) + baseCmd.AddCommand(man.GetCommand()) } // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go index f60918b1..20a02657 100644 --- a/internal/app/wwctl/root.go +++ b/internal/app/wwctl/root.go @@ -15,9 +15,6 @@ import ( "github.com/hpcng/warewulf/internal/pkg/help" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/spf13/cobra" - "github.com/spf13/cobra/doc" - - "io" ) var ( @@ -74,19 +71,3 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) error { } return nil } - -// External functions not used by the wwctl command line - -// Generate Bash completion file -func GenBashCompletion(w io.Writer) error { - return rootCmd.GenBashCompletion(w) -} - -// Generate man pages -func GenManTree(fileName string) error { - header := &doc.GenManHeader{ - Title: "WWCTL", - Section: "1", - } - return doc.GenManTree(rootCmd, header, fileName) -} From fa78d582eb866389ffd52c095ead8a43ed51d2c6 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Thu, 2 Mar 2023 12:18:59 +0100 Subject: [PATCH 03/25] generate reference documentation Signed-off-by: Christian Goll --- .gitignore | 1 + Makefile | 8 ++++++- internal/app/wwctl/genconf/reference/main.go | 17 ++++++++++++++ internal/app/wwctl/genconf/reference/root.go | 22 +++++++++++++++++++ internal/app/wwctl/genconf/root.go | 2 ++ userdocs/{reference => contents}/glossary.rst | 0 userdocs/index.rst | 7 +++++- 7 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 internal/app/wwctl/genconf/reference/main.go create mode 100644 internal/app/wwctl/genconf/reference/root.go rename userdocs/{reference => contents}/glossary.rst (100%) diff --git a/.gitignore b/.gitignore index 96fe0936..10a7f077 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ Defaults.mk /etc/wwapird.conf .dist/ userdocs/_* +userdocs/reference/* diff --git a/Makefile b/Makefile index 739c16d1..690e28e1 100644 --- a/Makefile +++ b/Makefile @@ -101,7 +101,7 @@ export GOPROXY WW_GO_BUILD_TAGS := containers_image_openpgp containers_image_ostree # Default target -all: config vendor wwctl wwclient man_pages config_defaults print_defaults wwapid wwapic wwapird print_mnts +all: config vendor wwctl wwclient man_pages config_defaults print_defaults wwapid wwapic wwapird # Validate source and build all packages build: lint test-it vet all @@ -255,6 +255,12 @@ dist: vendor config cd .dist; tar -czf ../$(WAREWULF)-$(VERSION).tar.gz $(WAREWULF)-$(VERSION) rm -rf .dist +reference: wwctl + ./wwctl genconfig reference userdocs/reference/ + +latexpdf: reference + make -C userdocs latexpdf + ## wwapi generate code from protobuf. Requires protoc and protoc-grpc-gen-gateway to generate code. ## To setup latest protoc: ## Download the protobuf-all-[VERSION].tar.gz from https://github.com/protocolbuffers/protobuf/releases diff --git a/internal/app/wwctl/genconf/reference/main.go b/internal/app/wwctl/genconf/reference/main.go new file mode 100644 index 00000000..c3cdacaa --- /dev/null +++ b/internal/app/wwctl/genconf/reference/main.go @@ -0,0 +1,17 @@ +package reference + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/cobra/doc" +) + +func CobraRunE(cmd *cobra.Command, args []string) (err error) { + linkHandler := func(name, ref string) string { + return fmt.Sprintf(":ref:`%s <%s>`", name, ref) + } + err = doc.GenReSTTreeCustom(cmd.Parent().Parent(), args[0], func(arg string) string { return "" }, linkHandler) + //err = doc.GenReSTTree(cmd.Parent().Parent(), args[0]) + return +} diff --git a/internal/app/wwctl/genconf/reference/root.go b/internal/app/wwctl/genconf/reference/root.go new file mode 100644 index 00000000..de89d871 --- /dev/null +++ b/internal/app/wwctl/genconf/reference/root.go @@ -0,0 +1,22 @@ +package reference + +import ( + "github.com/spf13/cobra" +) + +var ( + baseCmd = &cobra.Command{ + Use: "reference", + Short: "reference generation", + Long: "This command generates the references in ReStructured Text, needs target dir as argument", + RunE: CobraRunE, + Args: cobra.ExactArgs(1), + } +) + +func init() { +} + +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/app/wwctl/genconf/root.go b/internal/app/wwctl/genconf/root.go index 0c8afb2f..fc240021 100644 --- a/internal/app/wwctl/genconf/root.go +++ b/internal/app/wwctl/genconf/root.go @@ -3,6 +3,7 @@ package genconf import ( "github.com/hpcng/warewulf/internal/app/wwctl/genconf/completions" "github.com/hpcng/warewulf/internal/app/wwctl/genconf/man" + "github.com/hpcng/warewulf/internal/app/wwctl/genconf/reference" "github.com/spf13/cobra" ) @@ -21,6 +22,7 @@ var ( func init() { baseCmd.AddCommand(completions.GetCommand()) baseCmd.AddCommand(man.GetCommand()) + baseCmd.AddCommand(reference.GetCommand()) } // GetRootCommand returns the root cobra.Command for the application. diff --git a/userdocs/reference/glossary.rst b/userdocs/contents/glossary.rst similarity index 100% rename from userdocs/reference/glossary.rst rename to userdocs/contents/glossary.rst diff --git a/userdocs/index.rst b/userdocs/index.rst index 7b2b2ecc..c65537cd 100644 --- a/userdocs/index.rst +++ b/userdocs/index.rst @@ -41,9 +41,14 @@ Welcome to the Warewulf User Guide! Documentation Development Environment (KVM) Development Environment (VirtualBox) + Glossary .. toctree:: :maxdepth: 2 :caption: Reference + :glob: + + reference/* + + - Glossary From 1a19cfda63ce3dfd58c744d37873a5d2bca47bf8 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Thu, 2 Mar 2023 15:23:46 +0100 Subject: [PATCH 04/25] moved print_defaults to wwctl genconfig defaults Signed-off-by: Christian Goll --- Makefile | 11 +++-------- cmd/print_defaults/print_defaults.go | 17 ----------------- internal/app/wwctl/genconf/dfaults/main.go | 13 +++++++++++++ internal/app/wwctl/genconf/dfaults/root.go | 22 ++++++++++++++++++++++ internal/app/wwctl/genconf/root.go | 2 ++ 5 files changed, 40 insertions(+), 25 deletions(-) delete mode 100644 cmd/print_defaults/print_defaults.go create mode 100644 internal/app/wwctl/genconf/dfaults/main.go create mode 100644 internal/app/wwctl/genconf/dfaults/root.go diff --git a/Makefile b/Makefile index 690e28e1..d3fba332 100644 --- a/Makefile +++ b/Makefile @@ -101,7 +101,7 @@ export GOPROXY WW_GO_BUILD_TAGS := containers_image_openpgp containers_image_ostree # Default target -all: config vendor wwctl wwclient man_pages config_defaults print_defaults wwapid wwapic wwapird +all: config vendor wwctl wwclient man_pages config_defaults wwapid wwapic wwapird # Validate source and build all packages build: lint test-it vet all @@ -183,7 +183,7 @@ files: all test -f $(DESTDIR)$(WWCONFIGDIR)/wwapic.conf || install -m 644 etc/wwapic.conf $(DESTDIR)$(WWCONFIGDIR) test -f $(DESTDIR)$(WWCONFIGDIR)/wwapid.conf || install -m 644 etc/wwapid.conf $(DESTDIR)$(WWCONFIGDIR) test -f $(DESTDIR)$(WWCONFIGDIR)/wwapird.conf || install -m 644 etc/wwapird.conf $(DESTDIR)$(WWCONFIGDIR) - test -f $(DESTDIR)$(WWCONFIGDIR)/defaults.conf || ./print_defaults > $(DESTDIR)$(WWCONFIGDIR)/defaults.conf + test -f $(DESTDIR)$(WWCONFIGDIR)/defaults.conf || ./wwctl genconfig defaults > $(DESTDIR)$(WWCONFIGDIR)/defaults.conf cp -r etc/examples $(DESTDIR)$(WWCONFIGDIR)/ cp -r etc/ipxe $(DESTDIR)$(WWCONFIGDIR)/ cp -r overlays/* $(DESTDIR)$(WWOVERLAYDIR)/ @@ -222,7 +222,7 @@ wwctl: $(WWCTL_DEPS) wwclient: $(WWCLIENT_DEPS) @echo Building "$@" - @ cd cmd/wwclient; CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -ldflags "-extldflags -static \ + @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 man_pages: wwctl @@ -236,10 +236,6 @@ config_defaults: vendor cmd/config_defaults/config_defaults.go -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=./etc/nodes.conf'"\ -mod vendor -tags "$(WW_GO_BUILD_TAGS)" -o ../../config_defaults -print_defaults: vendor cmd/print_defaults/print_defaults.go - cd cmd/print_defaults && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=./etc/warewulf.conf'" -o ../../print_defaults - - 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'\ -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=./etc/nodes.conf'"\ @@ -301,7 +297,6 @@ contclean: rm -rf $(TOOLS_DIR) rm -f config_defaults rm -f update_configuration - rm -f print_defaults rm -f etc/wwapi{c,d,rd}.conf clean: contclean diff --git a/cmd/print_defaults/print_defaults.go b/cmd/print_defaults/print_defaults.go deleted file mode 100644 index 397294dd..00000000 --- a/cmd/print_defaults/print_defaults.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/hpcng/warewulf/internal/pkg/node" -) - -/* -Print the build in defaults for the nodes. -Called via Makefile so that there is single upstream -source of the defaults which is FallBackConf -*/ - -func main() { - fmt.Println(node.FallBackConf) -} diff --git a/internal/app/wwctl/genconf/dfaults/main.go b/internal/app/wwctl/genconf/dfaults/main.go new file mode 100644 index 00000000..a10309f0 --- /dev/null +++ b/internal/app/wwctl/genconf/dfaults/main.go @@ -0,0 +1,13 @@ +package dfaults + +import ( + "fmt" + + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/spf13/cobra" +) + +func CobraRunE(cmd *cobra.Command, args []string) (err error) { + fmt.Println(node.FallBackConf) + return +} diff --git a/internal/app/wwctl/genconf/dfaults/root.go b/internal/app/wwctl/genconf/dfaults/root.go new file mode 100644 index 00000000..ad575709 --- /dev/null +++ b/internal/app/wwctl/genconf/dfaults/root.go @@ -0,0 +1,22 @@ +package dfaults + +import "github.com/spf13/cobra" + +var ( + baseCmd = &cobra.Command{ + Use: "defaults", + Short: "print defaults", + Long: "This command prints the fallbacks which are used if defaults.conf isn't present", + RunE: CobraRunE, + Args: cobra.NoArgs, + Aliases: []string{"dfaults"}, + } + Zsh bool +) + +func init() { +} + +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/app/wwctl/genconf/root.go b/internal/app/wwctl/genconf/root.go index fc240021..5f9a8a4b 100644 --- a/internal/app/wwctl/genconf/root.go +++ b/internal/app/wwctl/genconf/root.go @@ -2,6 +2,7 @@ package genconf import ( "github.com/hpcng/warewulf/internal/app/wwctl/genconf/completions" + "github.com/hpcng/warewulf/internal/app/wwctl/genconf/dfaults" "github.com/hpcng/warewulf/internal/app/wwctl/genconf/man" "github.com/hpcng/warewulf/internal/app/wwctl/genconf/reference" "github.com/spf13/cobra" @@ -23,6 +24,7 @@ func init() { baseCmd.AddCommand(completions.GetCommand()) baseCmd.AddCommand(man.GetCommand()) baseCmd.AddCommand(reference.GetCommand()) + baseCmd.AddCommand(dfaults.GetCommand()) } // GetRootCommand returns the root cobra.Command for the application. From 29a6d39a2448189a0d6ba31ee56a0200754341fb Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Thu, 2 Mar 2023 19:44:48 +0100 Subject: [PATCH 05/25] added make_defaults to wwctl genconfig defaults Signed-off-by: Christian Goll --- internal/app/wwctl/genconf/dfaults/root.go | 1 - internal/app/wwctl/root.go | 10 +++++++--- internal/pkg/warewulfconf/constructors.go | 12 +++++++++--- internal/pkg/warewulfconf/datastructure.go | 7 +------ 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/internal/app/wwctl/genconf/dfaults/root.go b/internal/app/wwctl/genconf/dfaults/root.go index ad575709..9eadd58a 100644 --- a/internal/app/wwctl/genconf/dfaults/root.go +++ b/internal/app/wwctl/genconf/dfaults/root.go @@ -11,7 +11,6 @@ var ( Args: cobra.NoArgs, Aliases: []string{"dfaults"}, } - Zsh bool ) func init() { diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go index 20a02657..9c746c79 100644 --- a/internal/app/wwctl/root.go +++ b/internal/app/wwctl/root.go @@ -13,6 +13,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" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/spf13/cobra" ) @@ -27,9 +28,10 @@ var ( SilenceUsage: true, SilenceErrors: true, } - verboseArg bool - DebugFlag bool - LogLevel int + verboseArg bool + DebugFlag bool + LogLevel int + WarewulfConfArg string ) func init() { @@ -37,6 +39,7 @@ func init() { rootCmd.PersistentFlags().BoolVarP(&DebugFlag, "debug", "d", false, "Run with debugging messages enabled.") rootCmd.PersistentFlags().IntVar(&LogLevel, "loglevel", wwlog.INFO, "Set log level to given string") _ = rootCmd.PersistentFlags().MarkHidden("loglevel") + rootCmd.PersistentFlags().StringVar(&WarewulfConfArg, "warewulfconf", "", "Set the warewulf configuration file") rootCmd.SetUsageTemplate(help.UsageTemplate) rootCmd.SetHelpTemplate(help.HelpTemplate) @@ -69,5 +72,6 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) error { if LogLevel != wwlog.INFO { wwlog.SetLogLevel(LogLevel) } + warewulfconf.ConfigFile = WarewulfConfArg return nil } diff --git a/internal/pkg/warewulfconf/constructors.go b/internal/pkg/warewulfconf/constructors.go index c434d66e..f956bcd0 100644 --- a/internal/pkg/warewulfconf/constructors.go +++ b/internal/pkg/warewulfconf/constructors.go @@ -19,7 +19,13 @@ var cachedConf ControllerConf var ConfigFile string +/* +Get the configFile location from the os.ev if set +*/ func init() { + if ConfigFile == "" { + ConfigFile = os.Getenv("WARWULFCONF") + } if ConfigFile == "" { ConfigFile = path.Join(buildconfig.SYSCONFDIR(), "warewulf/warewulf.conf") } @@ -51,7 +57,7 @@ func New() (ControllerConf, error) { wwlog.Debug("Opening Warewulf configuration file: %s", ConfigFile) data, err := os.ReadFile(ConfigFile) if err != nil { - wwlog.Warn("Error reading Warewulf configuration file") + wwlog.Warn("Error reading Warewulf configuration file, falling back on defaults") } wwlog.Debug("Unmarshaling the Warewulf configuration") @@ -71,12 +77,12 @@ func New() (ControllerConf, error) { localIp := conn.LocalAddr().(*net.UDPAddr) if ret.Ipaddr == "" { ret.Ipaddr = localIp.IP.String() - wwlog.Warn("IP address is not configured in warewulfd.conf, using %s", ret.Ipaddr) + wwlog.Verbose("IP address is not configured in warewulfd.conf, using %s", ret.Ipaddr) } if ret.Netmask == "" { mask := localIp.IP.DefaultMask() ret.Netmask = fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]) - wwlog.Warn("Netmask address is not configured in warewulfd.conf, using %s", ret.Netmask) + wwlog.Verbose("Netmask address is not configured in warewulfd.conf, using %s", ret.Netmask) } } diff --git a/internal/pkg/warewulfconf/datastructure.go b/internal/pkg/warewulfconf/datastructure.go index 8403e878..38443883 100644 --- a/internal/pkg/warewulfconf/datastructure.go +++ b/internal/pkg/warewulfconf/datastructure.go @@ -2,7 +2,6 @@ package warewulfconf import ( "github.com/creasty/defaults" - "github.com/hpcng/warewulf/internal/pkg/util" "github.com/hpcng/warewulf/internal/pkg/wwlog" ) @@ -80,13 +79,9 @@ func (s *NfsConf) Unmarshal(unmarshal func(interface{}) error) error { } func init() { - if !util.IsFile(ConfigFile) { - wwlog.Error("Configuration file not found: %s", ConfigFile) - // fail silently as this also called by bash_completion - } _, err := New() if err != nil { - wwlog.Error("Could not read Warewulf configuration file: %s", err) + wwlog.Warn("Could not get any Warewulf configuration: %s", err) } } From 6b510865fe2239632189714fcba6d4e2c93a1bdc Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Fri, 3 Mar 2023 11:18:49 +0100 Subject: [PATCH 06/25] added warewulfconf as command line parameter the environment variable WAREWULFCONF is also recognized Signed-off-by: Christian Goll --- .../app/wwctl/container/exec/child/main.go | 7 +- internal/app/wwctl/node/status/main.go | 11 +- internal/app/wwctl/overlay/build/main.go | 6 +- internal/app/wwctl/root.go | 9 +- internal/app/wwctl/server/start/main.go | 5 +- internal/pkg/api/node/node.go | 6 +- internal/pkg/configure/dhcp.go | 10 +- internal/pkg/configure/nfs.go | 12 +- internal/pkg/configure/tftp.go | 8 +- internal/pkg/overlay/datastructure.go | 6 +- internal/pkg/warewulfconf/constructors.go | 169 ++++++++++-------- internal/pkg/warewulfconf/datastructure.go | 9 +- internal/pkg/warewulfd/daemon.go | 5 +- internal/pkg/warewulfd/provision.go | 69 ++++--- internal/pkg/warewulfd/warewulfd.go | 5 +- 15 files changed, 151 insertions(+), 186 deletions(-) diff --git a/internal/app/wwctl/container/exec/child/main.go b/internal/app/wwctl/container/exec/child/main.go index bf32ad17..9b62b507 100644 --- a/internal/app/wwctl/container/exec/child/main.go +++ b/internal/app/wwctl/container/exec/child/main.go @@ -21,7 +21,7 @@ import ( "github.com/spf13/cobra" ) -func CobraRunE(cmd *cobra.Command, args []string) error { +func CobraRunE(cmd *cobra.Command, args []string) (err error) { if os.Getpid() != 1 { wwlog.Error("PID is not 1: %d", os.Getpid()) os.Exit(1) @@ -33,10 +33,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { wwlog.Error("Unknown Warewulf container: %s", containerName) os.Exit(1) } - conf, err := warewulfconf.New() - if err != nil { - wwlog.Verbose("Couldn't get warewulf ocnfiguration: %s", err) - } + conf := warewulfconf.New() mountPts := conf.MountsContainer mountPts = append(container.InitMountPnts(binds), mountPts...) // check for valid mount points diff --git a/internal/app/wwctl/node/status/main.go b/internal/app/wwctl/node/status/main.go index 1999627f..5d8cdef7 100644 --- a/internal/app/wwctl/node/status/main.go +++ b/internal/app/wwctl/node/status/main.go @@ -2,7 +2,6 @@ package nodestatus import ( "fmt" - "os" "sort" "strings" "time" @@ -19,15 +18,11 @@ import ( func CobraRunE(cmd *cobra.Command, args []string) (err error) { - controller, err := warewulfconf.New() - if err != nil { - wwlog.Error("%s", err) - os.Exit(1) - } + controller := warewulfconf.New() if controller.Ipaddr == "" { - wwlog.Error("The Warewulf Server IP Address is not properly configured") - os.Exit(1) + return fmt.Errorf("warewulf Server IP Address is not properly configured") + } for { diff --git a/internal/app/wwctl/overlay/build/main.go b/internal/app/wwctl/overlay/build/main.go index 30babd08..556833c4 100644 --- a/internal/app/wwctl/overlay/build/main.go +++ b/internal/app/wwctl/overlay/build/main.go @@ -14,11 +14,7 @@ import ( ) func CobraRunE(cmd *cobra.Command, args []string) error { - controller, err := warewulfconf.New() - if err != nil { - wwlog.Error("%s", err) - os.Exit(1) - } + controller := warewulfconf.New() nodeDB, err := node.New() if err != nil { wwlog.Error("Could not open node configuration: %s", err) diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go index 9c746c79..f40e859d 100644 --- a/internal/app/wwctl/root.go +++ b/internal/app/wwctl/root.go @@ -1,6 +1,8 @@ package wwctl import ( + "os" + "github.com/hpcng/warewulf/internal/app/wwctl/configure" "github.com/hpcng/warewulf/internal/app/wwctl/container" "github.com/hpcng/warewulf/internal/app/wwctl/genconf" @@ -72,6 +74,11 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) error { if LogLevel != wwlog.INFO { wwlog.SetLogLevel(LogLevel) } - warewulfconf.ConfigFile = WarewulfConfArg + conf := warewulfconf.New() + if WarewulfConfArg != "" { + conf.ReadConf(WarewulfConfArg) + } else if os.Getenv("WAREWULFCONF") != "" { + conf.ReadConf(os.Getenv("WAREWULFCONF")) + } return nil } diff --git a/internal/app/wwctl/server/start/main.go b/internal/app/wwctl/server/start/main.go index b1954a46..00429177 100644 --- a/internal/app/wwctl/server/start/main.go +++ b/internal/app/wwctl/server/start/main.go @@ -10,10 +10,7 @@ import ( func CobraRunE(cmd *cobra.Command, args []string) error { if SetForeground { - conf, err := warewulfconf.New() - if err != nil { - return errors.Wrap(err, "Could not read Warewulf configuration file") - } + conf := warewulfconf.New() 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 47cc4c0b..69ad38ff 100644 --- a/internal/pkg/api/node/node.go +++ b/internal/pkg/api/node/node.go @@ -290,11 +290,7 @@ func NodeStatus(nodeNames []string) (nodeStatusResponse *wwapiv1.NodeStatusRespo Nodes map[string]*nodeStatusInternal `json:"nodes"` } - controller, err := warewulfconf.New() - if err != nil { - wwlog.Error("%s", err) - return - } + controller := warewulfconf.New() if controller.Ipaddr == "" { err = fmt.Errorf("the Warewulf Server IP Address is not properly configured") diff --git a/internal/pkg/configure/dhcp.go b/internal/pkg/configure/dhcp.go index 356dbefe..4d2f42df 100644 --- a/internal/pkg/configure/dhcp.go +++ b/internal/pkg/configure/dhcp.go @@ -15,13 +15,9 @@ import ( Configures the dhcpd server, when show is set to false, else the dhcp configuration is checked. */ -func Dhcp() error { +func Dhcp() (err error) { - controller, err := warewulfconf.New() - if err != nil { - wwlog.Error("%s", err) - os.Exit(1) - } + controller := warewulfconf.New() if !controller.Dhcp.Enabled { wwlog.Info("This system is not configured as a Warewulf DHCP controller") @@ -51,5 +47,5 @@ func Dhcp() error { return errors.Wrap(err, "failed to start") } - return nil + return } diff --git a/internal/pkg/configure/nfs.go b/internal/pkg/configure/nfs.go index 5a8f8158..d7e18469 100644 --- a/internal/pkg/configure/nfs.go +++ b/internal/pkg/configure/nfs.go @@ -2,7 +2,6 @@ package configure import ( "fmt" - "os" "github.com/hpcng/warewulf/internal/pkg/overlay" "github.com/hpcng/warewulf/internal/pkg/util" @@ -17,18 +16,11 @@ nfs server. */ func NFS() error { - controller, err := warewulfconf.New() - if err != nil { - wwlog.Error("%s", err) - os.Exit(1) - } + controller := warewulfconf.New() if controller.Nfs.Enabled { - if err != nil { - fmt.Println(err) - } if controller.Warewulf.EnableHostOverlay { - err = overlay.BuildHostOverlay() + err := overlay.BuildHostOverlay() if err != nil { wwlog.Warn("host overlay could not be built: %s", err) } diff --git a/internal/pkg/configure/tftp.go b/internal/pkg/configure/tftp.go index 9e71f67a..808852f2 100644 --- a/internal/pkg/configure/tftp.go +++ b/internal/pkg/configure/tftp.go @@ -13,13 +13,9 @@ import ( func TFTP() error { var tftpdir string = path.Join(buildconfig.TFTPDIR(), "warewulf") - controller, err := warewulfconf.New() - if err != nil { - wwlog.Error("%s", err) - return err - } + controller := warewulfconf.New() - err = os.MkdirAll(tftpdir, 0755) + err := os.MkdirAll(tftpdir, 0755) if err != nil { wwlog.Error("%s", err) return err diff --git a/internal/pkg/overlay/datastructure.go b/internal/pkg/overlay/datastructure.go index eb8004cf..f02bdedc 100644 --- a/internal/pkg/overlay/datastructure.go +++ b/internal/pkg/overlay/datastructure.go @@ -43,11 +43,7 @@ Initialize an TemplateStruct with the given node.NodeInfo */ func InitStruct(nodeInfo node.NodeInfo) TemplateStruct { var tstruct TemplateStruct - controller, err := warewulfconf.New() - if err != nil { - wwlog.Error("%s", err) - os.Exit(1) - } + controller := warewulfconf.New() nodeDB, err := node.New() if err != nil { wwlog.Error("%s", err) diff --git a/internal/pkg/warewulfconf/constructors.go b/internal/pkg/warewulfconf/constructors.go index f956bcd0..231749c2 100644 --- a/internal/pkg/warewulfconf/constructors.go +++ b/internal/pkg/warewulfconf/constructors.go @@ -11,7 +11,6 @@ import ( "github.com/creasty/defaults" "github.com/hpcng/warewulf/internal/pkg/buildconfig" "github.com/hpcng/warewulf/internal/pkg/wwlog" - "gopkg.in/yaml.v2" ) @@ -31,91 +30,107 @@ func init() { } } -func New() (ControllerConf, error) { - var ret ControllerConf - var warewulfconf WarewulfConf - var dhpdconf DhcpConf - var tftpconf TftpConf - var nfsConf NfsConf - ret.Warewulf = &warewulfconf - ret.Dhcp = &dhpdconf - ret.Tftp = &tftpconf - ret.Nfs = &nfsConf - err := defaults.Set(&ret) - // ipxe binaries are merged not overwritten, store defaults separate - defIpxe := make(map[string]string) - for k, v := range ret.Tftp.IpxeBinaries { - defIpxe[k] = v - delete(ret.Tftp.IpxeBinaries, k) - } - if err != nil { - wwlog.Error("Could initialize default variables") - return ret, err - } - // Check if cached config is old before re-reading config file +/* +Creates a new empty ControllerConf object, returns a cached +one if called in a nother context. +*/ +func New() (ret 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 { - wwlog.Debug("Opening Warewulf configuration file: %s", ConfigFile) - data, err := os.ReadFile(ConfigFile) + ret.Warewulf = new(WarewulfConf) + ret.Dhcp = new(DhcpConf) + ret.Tftp = new(TftpConf) + ret.Nfs = new(NfsConf) + err := defaults.Set(&ret) if err != nil { - wwlog.Warn("Error reading Warewulf configuration file, falling back on defaults") + ret.setDynamicDefaults() } - wwlog.Debug("Unmarshaling the Warewulf configuration") - err = yaml.Unmarshal(data, &ret) - if err != nil { - return ret, err - } - if len(ret.Tftp.IpxeBinaries) == 0 { - ret.Tftp.IpxeBinaries = defIpxe - } - if ret.Ipaddr == "" || ret.Netmask == "" { - conn, error := net.Dial("udp", "8.8.8.8:80") - if error != nil { - return ret, err - } - defer conn.Close() - localIp := conn.LocalAddr().(*net.UDPAddr) - if ret.Ipaddr == "" { - ret.Ipaddr = localIp.IP.String() - wwlog.Verbose("IP address is not configured in warewulfd.conf, using %s", ret.Ipaddr) - } - if ret.Netmask == "" { - mask := localIp.IP.DefaultMask() - ret.Netmask = fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]) - wwlog.Verbose("Netmask address is not configured in warewulfd.conf, using %s", ret.Netmask) - } - } - - if ret.Network == "" { - mask := net.IPMask(net.ParseIP(ret.Netmask).To4()) - size, _ := mask.Size() - - sub := ipsubnet.SubnetCalculator(ret.Ipaddr, size) - - ret.Network = sub.GetNetworkPortion() - } - // check validity of ipv6 net - if ret.Ipaddr6 != "" { - _, ipv6net, err := net.ParseCIDR(ret.Ipaddr6) - if err != nil { - wwlog.Error("Invalid ipv6 address specified, mut be CIDR notation: %s", ret.Ipaddr6) - return ret, errors.New("invalid ipv6 network") - } - if msize, _ := ipv6net.Mask.Size(); msize > 64 { - wwlog.Error("ipv6 mask size must be smaller than 64") - return ret, errors.New("invalid ipv6 network size") - } - } - - wwlog.Debug("Returning warewulf config object") cachedConf = ret cachedConf.current = true } else { - wwlog.Debug("Returning cached warewulf config object") // If cached struct isn't empty, use it as the return value ret = cachedConf } - - return ret, nil + return ret +} + +/* +Populate the configuration with the values from the configuration file. +*/ +func (conf *ControllerConf) ReadConf(confFileName string) (err error) { + 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 + } + if len(conf.Tftp.IpxeBinaries) == 0 { + conf.Tftp.IpxeBinaries = defIpxe + } + cachedConf = *conf + cachedConf.current = true + return +} + +/* +Set the runtime defaults like IP address of running system to the config +*/ +func (ret *ControllerConf) setDynamicDefaults() (err error) { + if ret.Ipaddr == "" || ret.Netmask == "" { + conn, error := net.Dial("udp", "8.8.8.8:80") + if error != nil { + return err + } + defer conn.Close() + localIp := conn.LocalAddr().(*net.UDPAddr) + if ret.Ipaddr == "" { + ret.Ipaddr = localIp.IP.String() + wwlog.Verbose("IP address is not configured in warewulfd.conf, using %s", ret.Ipaddr) + } + if ret.Netmask == "" { + mask := localIp.IP.DefaultMask() + ret.Netmask = fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]) + wwlog.Verbose("Netmask address is not configured in warewulfd.conf, using %s", ret.Netmask) + } + } + + if ret.Network == "" { + mask := net.IPMask(net.ParseIP(ret.Netmask).To4()) + size, _ := mask.Size() + + sub := ipsubnet.SubnetCalculator(ret.Ipaddr, size) + + ret.Network = sub.GetNetworkPortion() + } + // check validity of ipv6 net + if ret.Ipaddr6 != "" { + _, ipv6net, err := net.ParseCIDR(ret.Ipaddr6) + if err != nil { + wwlog.Error("Invalid ipv6 address specified, mut be CIDR notation: %s", ret.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 } diff --git a/internal/pkg/warewulfconf/datastructure.go b/internal/pkg/warewulfconf/datastructure.go index 38443883..ad0921a1 100644 --- a/internal/pkg/warewulfconf/datastructure.go +++ b/internal/pkg/warewulfconf/datastructure.go @@ -2,7 +2,6 @@ package warewulfconf import ( "github.com/creasty/defaults" - "github.com/hpcng/warewulf/internal/pkg/wwlog" ) type ControllerConf struct { @@ -78,15 +77,9 @@ func (s *NfsConf) Unmarshal(unmarshal func(interface{}) error) error { return nil } -func init() { - _, err := New() - if err != nil { - wwlog.Warn("Could not get any Warewulf configuration: %s", err) - } -} - // Waste processor cycles to make code more readable func DataStore() string { + _ = New() return cachedConf.Warewulf.DataStore } diff --git a/internal/pkg/warewulfd/daemon.go b/internal/pkg/warewulfd/daemon.go index 2ed57ed4..99407d23 100644 --- a/internal/pkg/warewulfd/daemon.go +++ b/internal/pkg/warewulfd/daemon.go @@ -44,10 +44,7 @@ func DaemonInitLogging() error { wwlog.SetLogLevel(wwlog.SERV) } - conf, err := warewulfconf.New() - if err != nil { - return errors.Wrap(err, "Could not read Warewulf configuration file") - } + conf := warewulfconf.New() if conf.Warewulf.Syslog { diff --git a/internal/pkg/warewulfd/provision.go b/internal/pkg/warewulfd/provision.go index a2abe4dd..67d23ac1 100644 --- a/internal/pkg/warewulfd/provision.go +++ b/internal/pkg/warewulfd/provision.go @@ -31,12 +31,7 @@ type iPxeTemplate struct { } func ProvisionSend(w http.ResponseWriter, req *http.Request) { - conf, err := warewulfconf.New() - if err != nil { - wwlog.Error("Could not open Warewulf configuration: %s", err) - w.WriteHeader(http.StatusServiceUnavailable) - return - } + conf := warewulfconf.New() rinfo, err := parseReq(req) if err != nil { @@ -45,7 +40,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { return } - wwlog.Recv("hwaddr: %s, ipaddr: %s, stage: %s", rinfo.hwaddr, req.RemoteAddr, rinfo.stage ) + wwlog.Recv("hwaddr: %s, ipaddr: %s, stage: %s", rinfo.hwaddr, req.RemoteAddr, rinfo.stage) if rinfo.stage == "runtime" && conf.Warewulf.Secure { if rinfo.remoteport >= 1024 { @@ -56,11 +51,11 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { } status_stages := map[string]string{ - "ipxe": "IPXE", - "kernel": "KERNEL", - "kmods": "KMODS_OVERLAY", - "system": "SYSTEM_OVERLAY", - "runtime": "RUNTIME_OVERLAY" } + "ipxe": "IPXE", + "kernel": "KERNEL", + "kmods": "KMODS_OVERLAY", + "system": "SYSTEM_OVERLAY", + "runtime": "RUNTIME_OVERLAY"} status_stage := status_stages[rinfo.stage] var stage_overlays []string @@ -87,24 +82,24 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { if rinfo.stage == "ipxe" { stage_file = path.Join(buildconfig.SYSCONFDIR(), "/warewulf/ipxe/unconfigured.ipxe") tmpl_data = iPxeTemplate{ - Hwaddr : rinfo.hwaddr } + Hwaddr: rinfo.hwaddr} } - }else if rinfo.stage == "ipxe" { + } else if rinfo.stage == "ipxe" { stage_file = path.Join(buildconfig.SYSCONFDIR(), "warewulf/ipxe/"+node.Ipxe.Get()+".ipxe") tmpl_data = iPxeTemplate{ - Id : node.Id.Get(), - Cluster : node.ClusterName.Get(), - Fqdn : node.Id.Get(), - Ipaddr : conf.Ipaddr, - Port : strconv.Itoa(conf.Warewulf.Port), - Hostname : node.Id.Get(), - Hwaddr : rinfo.hwaddr, - ContainerName : node.ContainerName.Get(), - KernelArgs : node.Kernel.Args.Get(), - KernelOverride : node.Kernel.Override.Get() } + Id: node.Id.Get(), + Cluster: node.ClusterName.Get(), + Fqdn: node.Id.Get(), + Ipaddr: conf.Ipaddr, + Port: strconv.Itoa(conf.Warewulf.Port), + Hostname: node.Id.Get(), + Hwaddr: rinfo.hwaddr, + ContainerName: node.ContainerName.Get(), + KernelArgs: node.Kernel.Args.Get(), + KernelOverride: node.Kernel.Override.Get()} - }else if rinfo.stage == "kernel" { + } else if rinfo.stage == "kernel" { if node.Kernel.Override.Defined() { stage_file = kernel.KernelImage(node.Kernel.Override.Get()) } else if node.ContainerName.Defined() { @@ -117,28 +112,28 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { wwlog.Warn("No kernel version set for node %s", node.Id.Get()) } - }else if rinfo.stage == "kmods" { + } else if rinfo.stage == "kmods" { if node.Kernel.Override.Defined() { stage_file = kernel.KmodsImage(node.Kernel.Override.Get()) - }else{ + } else { wwlog.Warn("No kernel override modules set for node %s", node.Id.Get()) } - }else if rinfo.stage == "container" { + } else if rinfo.stage == "container" { if node.ContainerName.Defined() { stage_file = container.ImageFile(node.ContainerName.Get()) } else { wwlog.Warn("No container set for node %s", node.Id.Get()) } - }else if rinfo.stage == "system" { + } else if rinfo.stage == "system" { if len(node.SystemOverlay.GetSlice()) != 0 { stage_overlays = node.SystemOverlay.GetSlice() } else { wwlog.Warn("No system overlay set for node %s", node.Id.Get()) } - }else if rinfo.stage == "runtime" { + } else if rinfo.stage == "runtime" { if rinfo.overlay != "" { stage_overlays = []string{rinfo.overlay} } else if len(node.RuntimeOverlay.GetSlice()) != 0 { @@ -153,7 +148,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { stage_file, err = getOverlayFile( node.Id.Get(), stage_overlays, - conf.Warewulf.AutobuildOverlays ) + conf.Warewulf.AutobuildOverlays) if err != nil { w.WriteHeader(http.StatusInternalServerError) @@ -162,7 +157,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { } } - wwlog.Serv("stage_file '%s'", stage_file ) + wwlog.Serv("stage_file '%s'", stage_file) if util.IsFile(stage_file) { @@ -200,7 +195,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { wwlog.Send("%15s: %s", node.Id.Get(), stage_file) - }else{ + } else { if rinfo.compress == "gz" { stage_file += ".gz" @@ -210,7 +205,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { w.WriteHeader(http.StatusNotFound) return } - }else if rinfo.compress != "" { + } else if rinfo.compress != "" { wwlog.Error("unsupported %s compressed version of file %s", rinfo.compress, stage_file) w.WriteHeader(http.StatusNotFound) @@ -225,14 +220,14 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { updateStatus(node.Id.Get(), status_stage, path.Base(stage_file), rinfo.ipaddr) - }else if stage_file == "" { + } else if stage_file == "" { w.WriteHeader(http.StatusBadRequest) wwlog.Error("No resource selected") updateStatus(node.Id.Get(), status_stage, "BAD_REQUEST", rinfo.ipaddr) - }else{ + } else { w.WriteHeader(http.StatusNotFound) - wwlog.Error("Not found: %s", stage_file ) + wwlog.Error("Not found: %s", stage_file) updateStatus(node.Id.Get(), status_stage, "NOT_FOUND", rinfo.ipaddr) } diff --git a/internal/pkg/warewulfd/warewulfd.go b/internal/pkg/warewulfd/warewulfd.go index 5ecf48b6..d69aa210 100644 --- a/internal/pkg/warewulfd/warewulfd.go +++ b/internal/pkg/warewulfd/warewulfd.go @@ -58,10 +58,7 @@ func RunServer() error { http.HandleFunc("/overlay-runtime/", ProvisionSend) http.HandleFunc("/status", StatusSend) - conf, err := warewulfconf.New() - if err != nil { - return errors.Wrap(err, "could not get Warewulf configuration") - } + conf := warewulfconf.New() daemonPort := conf.Warewulf.Port wwlog.Serv("Starting HTTPD REST service on port %d", daemonPort) From f2eff0f7b8bafde5da6fea53a10fbdb056a24c6e Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Fri, 3 Mar 2023 14:07:26 +0100 Subject: [PATCH 07/25] added `wwctl genconfig warewulfconf print` Signed-off-by: Christian Goll --- cmd/config_defaults/config_defaults.go | 26 ------------------ internal/app/wwctl/genconf/root.go | 2 ++ .../wwctl/genconf/warewulfconf/print/main.go | 19 +++++++++++++ .../wwctl/genconf/warewulfconf/print/root.go | 22 +++++++++++++++ .../app/wwctl/genconf/warewulfconf/root.go | 27 +++++++++++++++++++ internal/pkg/warewulfconf/datastructure.go | 2 +- 6 files changed, 71 insertions(+), 27 deletions(-) delete mode 100644 cmd/config_defaults/config_defaults.go create mode 100644 internal/app/wwctl/genconf/warewulfconf/print/main.go create mode 100644 internal/app/wwctl/genconf/warewulfconf/print/root.go create mode 100644 internal/app/wwctl/genconf/warewulfconf/root.go diff --git a/cmd/config_defaults/config_defaults.go b/cmd/config_defaults/config_defaults.go deleted file mode 100644 index 70068c90..00000000 --- a/cmd/config_defaults/config_defaults.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -// Regenerates the default configuration files -// Keeps the current content and adds missing default values -// Won't create a new file, but will update a blank file - -//TODO: Add nodes.conf - -import ( - "fmt" - "github.com/hpcng/warewulf/internal/pkg/warewulfconf" -) - -func main() { - tmpConf, err := warewulfconf.New() - if err != nil { - fmt.Println(err) - return - } - - err = tmpConf.Persist() - if err != nil { - fmt.Println(err) - return - } -} diff --git a/internal/app/wwctl/genconf/root.go b/internal/app/wwctl/genconf/root.go index 5f9a8a4b..1a979be4 100644 --- a/internal/app/wwctl/genconf/root.go +++ b/internal/app/wwctl/genconf/root.go @@ -5,6 +5,7 @@ import ( "github.com/hpcng/warewulf/internal/app/wwctl/genconf/dfaults" "github.com/hpcng/warewulf/internal/app/wwctl/genconf/man" "github.com/hpcng/warewulf/internal/app/wwctl/genconf/reference" + "github.com/hpcng/warewulf/internal/app/wwctl/genconf/warewulfconf" "github.com/spf13/cobra" ) @@ -25,6 +26,7 @@ func init() { baseCmd.AddCommand(man.GetCommand()) baseCmd.AddCommand(reference.GetCommand()) baseCmd.AddCommand(dfaults.GetCommand()) + baseCmd.AddCommand(warewulfconf.GetCommand()) } // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/app/wwctl/genconf/warewulfconf/print/main.go b/internal/app/wwctl/genconf/warewulfconf/print/main.go new file mode 100644 index 00000000..2efca477 --- /dev/null +++ b/internal/app/wwctl/genconf/warewulfconf/print/main.go @@ -0,0 +1,19 @@ +package print + +import ( + "fmt" + + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + "github.com/spf13/cobra" + "gopkg.in/yaml.v2" +) + +func CobraRunE(cmd *cobra.Command, args []string) (err error) { + conf := warewulfconf.New() + buffer, err := yaml.Marshal(&conf) + if err != nil { + return + } + fmt.Println(string(buffer)) + return +} diff --git a/internal/app/wwctl/genconf/warewulfconf/print/root.go b/internal/app/wwctl/genconf/warewulfconf/print/root.go new file mode 100644 index 00000000..8bd53f6f --- /dev/null +++ b/internal/app/wwctl/genconf/warewulfconf/print/root.go @@ -0,0 +1,22 @@ +package print + +import ( + "github.com/spf13/cobra" +) + +var ( + baseCmd = &cobra.Command{ + Use: "print", + Short: "print wareweulf.conf", + Long: "This command prints the actual used warewulf.conf, can be used to create an empty valid warewulf.conf", + RunE: CobraRunE, + Args: cobra.ExactArgs(0), + } +) + +func init() { +} + +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/app/wwctl/genconf/warewulfconf/root.go b/internal/app/wwctl/genconf/warewulfconf/root.go new file mode 100644 index 00000000..856c2f70 --- /dev/null +++ b/internal/app/wwctl/genconf/warewulfconf/root.go @@ -0,0 +1,27 @@ +package warewulfconf + +import ( + "github.com/hpcng/warewulf/internal/app/wwctl/genconf/warewulfconf/print" + "github.com/spf13/cobra" +) + +var ( + baseCmd = &cobra.Command{ + Use: "warewulfconf", + Short: "access warewulf.conf", + Long: "Commands for accessing the actual used warewulf.conf", + Args: cobra.ExactArgs(0), + Aliases: []string{"cnf"}, + } + ListFull bool + WWctlRoot *cobra.Command +) + +func init() { + baseCmd.AddCommand(print.GetCommand()) +} + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/pkg/warewulfconf/datastructure.go b/internal/pkg/warewulfconf/datastructure.go index ad0921a1..ff12e4cb 100644 --- a/internal/pkg/warewulfconf/datastructure.go +++ b/internal/pkg/warewulfconf/datastructure.go @@ -17,7 +17,7 @@ type ControllerConf struct { Dhcp *DhcpConf `yaml:"dhcp"` Tftp *TftpConf `yaml:"tftp"` Nfs *NfsConf `yaml:"nfs"` - MountsContainer []*MountEntry `yaml:"container mounts"` + MountsContainer []*MountEntry `yaml:"container mounts" default:"[{\"source\": \"/etc/resolv.conf\", \"dest\": \"/etc/resolv.conf\"}]"` current bool } From 4deae97bc8d5598251b67e8efe9483b2c8dc7dcb Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Fri, 3 Mar 2023 16:02:41 +0100 Subject: [PATCH 08/25] make the configuration to be based in warewulf.conf defaults will be se by the Makefile Signed-off-by: Christian Goll --- .gitignore | 1 + Makefile | 8 -- internal/app/wwclient/root.go | 6 +- internal/pkg/buildconfig/defaults.go | 98 --------------------- internal/pkg/buildconfig/setconfigs.go.in | 19 ---- internal/pkg/configure/ssh.go | 8 +- internal/pkg/configure/tftp.go | 5 +- internal/pkg/container/config.go | 8 +- internal/pkg/kernel/kernel.go | 5 +- internal/pkg/node/constructors.go | 7 +- internal/pkg/overlay/config.go | 8 +- internal/pkg/overlay/funcmap.go | 8 +- internal/pkg/version/version.go | 5 +- internal/pkg/warewulfconf/buildconfig.go.in | 18 ++++ internal/pkg/warewulfconf/constructors.go | 21 +---- internal/pkg/warewulfconf/datastructure.go | 1 + internal/pkg/warewulfconf/getter.go | 73 +++++++++++++++ internal/pkg/warewulfd/provision.go | 5 +- 18 files changed, 130 insertions(+), 174 deletions(-) delete mode 100644 internal/pkg/buildconfig/defaults.go delete mode 100644 internal/pkg/buildconfig/setconfigs.go.in create mode 100644 internal/pkg/warewulfconf/buildconfig.go.in create mode 100644 internal/pkg/warewulfconf/getter.go diff --git a/.gitignore b/.gitignore index 10a7f077..18b9454f 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ /etc/bash_completion.d/ warewulf.spec internal/pkg/buildconfig/setconfigs.go +internal/pkg/warewulfconf/buildconfig.go include/systemd/warewulfd.service _dist/ config diff --git a/Makefile b/Makefile index d3fba332..b8a8db93 100644 --- a/Makefile +++ b/Makefile @@ -231,19 +231,11 @@ man_pages: wwctl cp docs/man/man5/*.5 ./man_pages/ cd man_pages; for i in wwctl*1 *.5; do echo "Compressing manpage: $$i"; gzip --force $$i; done -config_defaults: vendor cmd/config_defaults/config_defaults.go - cd cmd/config_defaults && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=./etc/warewulf.conf'\ - -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=./etc/nodes.conf'"\ - -mod vendor -tags "$(WW_GO_BUILD_TAGS)" -o ../../config_defaults - 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'\ -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=./etc/nodes.conf'"\ -mod vendor -tags "$(WW_GO_BUILD_TAGS)" -o ../../update_configuration -warewulfconf: config_defaults - ./config_defaults - dist: vendor config rm -rf .dist/$(WAREWULF)-$(VERSION) $(WAREWULF)-$(VERSION).tar.gz mkdir -p .dist/$(WAREWULF)-$(VERSION) diff --git a/internal/app/wwclient/root.go b/internal/app/wwclient/root.go index dc06e684..e657c21b 100644 --- a/internal/app/wwclient/root.go +++ b/internal/app/wwclient/root.go @@ -16,7 +16,6 @@ import ( "github.com/coreos/go-systemd/daemon" "github.com/google/uuid" - "github.com/hpcng/warewulf/internal/pkg/buildconfig" "github.com/hpcng/warewulf/internal/pkg/pidfile" "github.com/hpcng/warewulf/internal/pkg/warewulfconf" "github.com/hpcng/warewulf/internal/pkg/wwlog" @@ -50,10 +49,7 @@ func GetRootCommand() *cobra.Command { } func CobraRunE(cmd *cobra.Command, args []string) error { - conf, err := warewulfconf.New() - if err != nil { - return err - } + conf := warewulfconf.New() pid, err := pidfile.Write(PIDFile) if err != nil && pid == -1 { diff --git a/internal/pkg/buildconfig/defaults.go b/internal/pkg/buildconfig/defaults.go deleted file mode 100644 index f739cff1..00000000 --- a/internal/pkg/buildconfig/defaults.go +++ /dev/null @@ -1,98 +0,0 @@ -package buildconfig - -import "github.com/hpcng/warewulf/internal/pkg/wwlog" - -const WWVer = 43 - -var ( - bindir string = "UNDEF" - sysconfdir string = "UNDEF" - localstatedir string = "UNDEF" - srvdir string = "UNDEF" - tftpdir string = "UNDEF" - firewallddir string = "UNDEF" - systemddir string = "UNDEF" - wwoverlaydir string = "UNDEF" - wwchrootdir string = "UNDEF" - wwprovisiondir string = "UNDEF" - version string = "UNDEF" - release string = "UNDEF" - wwclientdir string = "UNDEF" - datadir string = "UNDEF" - tmpdir string = "UNDEF" -) - -func BINDIR() string { - wwlog.Debug("BINDIR = '%s'", bindir) - return bindir -} - -func DATADIR() string { - wwlog.Debug("DATADIR = '%s'", datadir) - return datadir -} - -func SYSCONFDIR() string { - wwlog.Debug("SYSCONFDIR = '%s'", sysconfdir) - return sysconfdir -} - -func LOCALSTATEDIR() string { - wwlog.Debug("LOCALSTATEDIR = '%s'", localstatedir) - return localstatedir -} - -func SRVDIR() string { - wwlog.Debug("SRVDIR = '%s'", srvdir) - return srvdir -} - -func TFTPDIR() string { - wwlog.Debug("TFTPDIR = '%s'", tftpdir) - return tftpdir -} - -func FIREWALLDDIR() string { - wwlog.Debug("FIREWALLDDIR = '%s'", firewallddir) - return firewallddir -} - -func SYSTEMDDIR() string { - wwlog.Debug("SYSTEMDDIR = '%s'", systemddir) - return systemddir -} - -func WWOVERLAYDIR() string { - wwlog.Debug("WWOVERLAYDIR = '%s'", wwoverlaydir) - return wwoverlaydir -} - -func WWCHROOTDIR() string { - wwlog.Debug("WWCHROOTDIR = '%s'", wwchrootdir) - return wwchrootdir -} - -func WWPROVISIONDIR() string { - wwlog.Debug("WWPROVISIONDIR = '%s'", wwprovisiondir) - return wwprovisiondir -} - -func VERSION() string { - wwlog.Debug("VERSION = '%s'", version) - return version -} - -func RELEASE() string { - wwlog.Debug("RELEASE = '%s'", release) - return release -} - -func WWCLIENTDIR() string { - wwlog.Debug("WWCLIENTDIR = '%s'", wwclientdir) - return wwclientdir -} - -func TMPDIR() string { - wwlog.Debug("TMPDIR = '%s'", tmpdir) - return tmpdir -} diff --git a/internal/pkg/buildconfig/setconfigs.go.in b/internal/pkg/buildconfig/setconfigs.go.in deleted file mode 100644 index f049ffeb..00000000 --- a/internal/pkg/buildconfig/setconfigs.go.in +++ /dev/null @@ -1,19 +0,0 @@ -package buildconfig - -func init() { - bindir = "@BINDIR@" - sysconfdir = "@SYSCONFDIR@" - datadir = "@DATADIR@" - localstatedir = "@LOCALSTATEDIR@" - srvdir = "@SRVDIR@" - tftpdir = "@TFTPDIR@" - firewallddir = "@FIREWALLDDIR@" - systemddir = "@SYSTEMDDIR@" - wwoverlaydir = "@WWOVERLAYDIR@" - wwchrootdir = "@WWCHROOTDIR@" - wwprovisiondir = "@WWPROVISIONDIR@" - version = "@VERSION@" - release = "@RELEASE@" - wwclientdir = "@WWCLIENTDIR@" - tmpdir = "@TMPDIR@" -} diff --git a/internal/pkg/configure/ssh.go b/internal/pkg/configure/ssh.go index c4b5e04f..0c60e053 100644 --- a/internal/pkg/configure/ssh.go +++ b/internal/pkg/configure/ssh.go @@ -5,8 +5,8 @@ import ( "os" "path" - "github.com/hpcng/warewulf/internal/pkg/buildconfig" "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/pkg/errors" ) @@ -14,10 +14,10 @@ import ( func SSH() error { if os.Getuid() == 0 { fmt.Printf("Updating system keys\n") + conf := warewulfconf.New() + wwkeydir := path.Join(conf.SYSCONFDIR(), "warewulf/keys") + "/" - wwkeydir := path.Join(buildconfig.SYSCONFDIR(), "warewulf/keys") + "/" - - err := os.MkdirAll(path.Join(buildconfig.SYSCONFDIR(), "warewulf/keys"), 0755) + err := os.MkdirAll(path.Join(conf.SYSCONFDIR(), "warewulf/keys"), 0755) if err != nil { wwlog.Error("Could not create base directory: %s", err) os.Exit(1) diff --git a/internal/pkg/configure/tftp.go b/internal/pkg/configure/tftp.go index 808852f2..718a4030 100644 --- a/internal/pkg/configure/tftp.go +++ b/internal/pkg/configure/tftp.go @@ -5,15 +5,14 @@ import ( "os" "path" - "github.com/hpcng/warewulf/internal/pkg/buildconfig" "github.com/hpcng/warewulf/internal/pkg/util" "github.com/hpcng/warewulf/internal/pkg/warewulfconf" "github.com/hpcng/warewulf/internal/pkg/wwlog" ) func TFTP() error { - var tftpdir string = path.Join(buildconfig.TFTPDIR(), "warewulf") controller := warewulfconf.New() + var tftpdir string = path.Join(controller.TFTPDIR(), "warewulf") err := os.MkdirAll(tftpdir, 0755) if err != nil { @@ -28,7 +27,7 @@ func TFTP() error { continue } copyCheck[f] = true - err = util.SafeCopyFile(path.Join(buildconfig.DATADIR(), f), path.Join(tftpdir, f)) + err = util.SafeCopyFile(path.Join(controller.DATADIR(), f), path.Join(tftpdir, f)) if err != nil { wwlog.Warn("ipxe binary could not be copied, booting may not work: %s", err) } diff --git a/internal/pkg/container/config.go b/internal/pkg/container/config.go index 1faf7423..ebbc8569 100644 --- a/internal/pkg/container/config.go +++ b/internal/pkg/container/config.go @@ -3,11 +3,12 @@ package container import ( "path" - "github.com/hpcng/warewulf/internal/pkg/buildconfig" + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" ) func SourceParentDir() string { - return buildconfig.WWCHROOTDIR() + conf := warewulfconf.New() + return conf.WWCHROOTDIR() } func SourceDir(name string) string { @@ -19,7 +20,8 @@ func RootFsDir(name string) string { } func ImageParentDir() string { - return path.Join(buildconfig.WWPROVISIONDIR(), "container/") + conf := warewulfconf.New() + return path.Join(conf.WWPROVISIONDIR(), "container/") } func ImageFile(name string) string { diff --git a/internal/pkg/kernel/kernel.go b/internal/pkg/kernel/kernel.go index 9983572e..14ce78a2 100644 --- a/internal/pkg/kernel/kernel.go +++ b/internal/pkg/kernel/kernel.go @@ -11,8 +11,8 @@ import ( "github.com/pkg/errors" - "github.com/hpcng/warewulf/internal/pkg/buildconfig" "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" "github.com/hpcng/warewulf/internal/pkg/wwlog" ) @@ -28,7 +28,8 @@ var ( ) func KernelImageTopDir() string { - return path.Join(buildconfig.WWPROVISIONDIR(), "kernel") + conf := warewulfconf.New() + return path.Join(conf.WWPROVISIONDIR(), "kernel") } func KernelImage(kernelName string) string { diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index d69bdf74..3d83f8df 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -7,7 +7,7 @@ import ( "sort" "strings" - "github.com/hpcng/warewulf/internal/pkg/buildconfig" + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" "github.com/hpcng/warewulf/internal/pkg/wwlog" "gopkg.in/yaml.v2" @@ -37,11 +37,12 @@ defaultnode: netmask: 255.255.255.0` func init() { + conf := warewulfconf.New() if ConfigFile == "" { - ConfigFile = path.Join(buildconfig.SYSCONFDIR(), "warewulf/nodes.conf") + ConfigFile = path.Join(conf.SYSCONFDIR(), "warewulf/nodes.conf") } if DefaultConfig == "" { - DefaultConfig = path.Join(buildconfig.SYSCONFDIR(), "warewulf/defaults.conf") + DefaultConfig = path.Join(conf.SYSCONFDIR(), "warewulf/defaults.conf") } } diff --git a/internal/pkg/overlay/config.go b/internal/pkg/overlay/config.go index c9b80306..3f5b1bd4 100644 --- a/internal/pkg/overlay/config.go +++ b/internal/pkg/overlay/config.go @@ -5,11 +5,12 @@ import ( "path" "strings" - "github.com/hpcng/warewulf/internal/pkg/buildconfig" + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" ) func OverlaySourceTopDir() string { - return buildconfig.WWOVERLAYDIR() + conf := warewulfconf.New() + return conf.WWOVERLAYDIR() } /* @@ -30,5 +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 { - return path.Join(buildconfig.WWPROVISIONDIR(), "overlays/", nodeName, strings.Join(overlayName, "-")+".img") + conf := warewulfconf.New() + return path.Join(conf.WWPROVISIONDIR(), "overlays/", nodeName, strings.Join(overlayName, "-")+".img") } diff --git a/internal/pkg/overlay/funcmap.go b/internal/pkg/overlay/funcmap.go index 49b50d3d..d3fe05e5 100644 --- a/internal/pkg/overlay/funcmap.go +++ b/internal/pkg/overlay/funcmap.go @@ -6,9 +6,9 @@ import ( "path" "strings" - "github.com/hpcng/warewulf/internal/pkg/buildconfig" "github.com/hpcng/warewulf/internal/pkg/container" "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" "github.com/hpcng/warewulf/internal/pkg/wwlog" ) @@ -18,8 +18,9 @@ the path is relative to SYSCONFDIR. Templates in the file are no evaluated. */ func templateFileInclude(inc string) string { + conf := warewulfconf.New() if !strings.HasPrefix(inc, "/") { - inc = path.Join(buildconfig.SYSCONFDIR(), "warewulf", inc) + inc = path.Join(conf.SYSCONFDIR(), "warewulf", inc) } wwlog.Debug("Including file into template: %s", inc) content, err := os.ReadFile(inc) @@ -35,8 +36,9 @@ 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() if !strings.HasPrefix(inc, "/") { - inc = path.Join(buildconfig.SYSCONFDIR(), "warewulf", inc) + inc = path.Join(conf.SYSCONFDIR(), "warewulf", inc) } wwlog.Debug("Including file block into template: %s", inc) readFile, err := os.Open(inc) diff --git a/internal/pkg/version/version.go b/internal/pkg/version/version.go index f77050f4..5ce00f23 100644 --- a/internal/pkg/version/version.go +++ b/internal/pkg/version/version.go @@ -4,14 +4,15 @@ import ( "fmt" "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" - "github.com/hpcng/warewulf/internal/pkg/buildconfig" + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" ) /* Return the version of wwctl */ func GetVersion() string { - return fmt.Sprintf("%s-%s", buildconfig.VERSION(), buildconfig.RELEASE()) + conf := warewulfconf.New() + return fmt.Sprintf("%s-%s", conf.VERSION(), conf.RELEASE()) } /* diff --git a/internal/pkg/warewulfconf/buildconfig.go.in b/internal/pkg/warewulfconf/buildconfig.go.in new file mode 100644 index 00000000..7bdf96e1 --- /dev/null +++ b/internal/pkg/warewulfconf/buildconfig.go.in @@ -0,0 +1,18 @@ +package warewulfconf + +type BuildConfig struct { + bindir string `default:"@BINDIR@"` + sysconfdir string `default:"@SYSCONFDIR@"` + datadir string `default:"@DATADIR@"` + localstatedir string `default:"@LOCALSTATEDIR@"` + srvdir string `default:"@SRVDIR@"` + tftpdir string `default:"@TFTPDIR@"` + firewallddir string `default:"@FIREWALLDDIR@"` + systemddir string `default:"@SYSTEMDDIR@"` + wwoverlaydir string `default:"@WWOVERLAYDIR@"` + wwchrootdir string `default:"@WWCHROOTDIR@"` + wwprovisiondir string `default:"@WWPROVISIONDIR@"` + version string `default:"@VERSION@"` + release string `default:"@RELEASE@"` + wwclientdir string `default:"@WWCLIENTDIR@"` +} diff --git a/internal/pkg/warewulfconf/constructors.go b/internal/pkg/warewulfconf/constructors.go index 231749c2..0b01e760 100644 --- a/internal/pkg/warewulfconf/constructors.go +++ b/internal/pkg/warewulfconf/constructors.go @@ -5,11 +5,9 @@ import ( "fmt" "net" "os" - "path" "github.com/brotherpowers/ipsubnet" "github.com/creasty/defaults" - "github.com/hpcng/warewulf/internal/pkg/buildconfig" "github.com/hpcng/warewulf/internal/pkg/wwlog" "gopkg.in/yaml.v2" ) @@ -18,18 +16,6 @@ var cachedConf ControllerConf var ConfigFile string -/* -Get the configFile location from the os.ev if set -*/ -func init() { - if ConfigFile == "" { - ConfigFile = os.Getenv("WARWULFCONF") - } - if ConfigFile == "" { - ConfigFile = path.Join(buildconfig.SYSCONFDIR(), "warewulf/warewulf.conf") - } -} - /* Creates a new empty ControllerConf object, returns a cached one if called in a nother context. @@ -42,10 +28,9 @@ func New() (ret ControllerConf) { ret.Dhcp = new(DhcpConf) ret.Tftp = new(TftpConf) ret.Nfs = new(NfsConf) - err := defaults.Set(&ret) - if err != nil { - ret.setDynamicDefaults() - } + ret.Paths = new(BuildConfig) + _ = defaults.Set(&ret) + ret.setDynamicDefaults() cachedConf = ret cachedConf.current = true diff --git a/internal/pkg/warewulfconf/datastructure.go b/internal/pkg/warewulfconf/datastructure.go index ff12e4cb..33d77fe2 100644 --- a/internal/pkg/warewulfconf/datastructure.go +++ b/internal/pkg/warewulfconf/datastructure.go @@ -18,6 +18,7 @@ type ControllerConf struct { 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:"mypaths"` current bool } diff --git a/internal/pkg/warewulfconf/getter.go b/internal/pkg/warewulfconf/getter.go new file mode 100644 index 00000000..6a3f5a01 --- /dev/null +++ b/internal/pkg/warewulfconf/getter.go @@ -0,0 +1,73 @@ +package warewulfconf + +import "github.com/hpcng/warewulf/internal/pkg/wwlog" + +func (conf *ControllerConf) BINDIR() string { + wwlog.Debug("BINDIR = '%s'", conf.Paths.bindir) + return conf.Paths.bindir +} + +func (conf *ControllerConf) DATADIR() string { + wwlog.Debug("DATADIR = '%s'", conf.Paths.datadir) + return conf.Paths.datadir +} + +func (conf *ControllerConf) SYSCONFDIR() string { + wwlog.Debug("SYSCONFDIR = '%s'", conf.Paths.sysconfdir) + return conf.Paths.sysconfdir +} + +func (conf *ControllerConf) LOCALSTATEDIR() string { + wwlog.Debug("LOCALSTATEDIR = '%s'", conf.Paths.localstatedir) + return conf.Paths.localstatedir +} + +func (conf *ControllerConf) SRVDIR() string { + wwlog.Debug("SRVDIR = '%s'", conf.Paths.srvdir) + return conf.Paths.srvdir +} + +func (conf *ControllerConf) TFTPDIR() string { + wwlog.Debug("TFTPDIR = '%s'", conf.Paths.tftpdir) + return conf.Paths.tftpdir +} + +func (conf *ControllerConf) FIREWALLDDIR() string { + wwlog.Debug("FIREWALLDDIR = '%s'", conf.Paths.firewallddir) + return conf.Paths.firewallddir +} + +func (conf *ControllerConf) SYSTEMDDIR() string { + wwlog.Debug("SYSTEMDDIR = '%s'", conf.Paths.systemddir) + return conf.Paths.systemddir +} + +func (conf *ControllerConf) WWOVERLAYDIR() string { + wwlog.Debug("WWOVERLAYDIR = '%s'", conf.Paths.wwoverlaydir) + return conf.Paths.wwoverlaydir +} + +func (conf *ControllerConf) WWCHROOTDIR() string { + wwlog.Debug("WWCHROOTDIR = '%s'", conf.Paths.wwchrootdir) + return conf.Paths.wwchrootdir +} + +func (conf *ControllerConf) WWPROVISIONDIR() string { + wwlog.Debug("WWPROVISIONDIR = '%s'", conf.Paths.wwprovisiondir) + return conf.Paths.wwprovisiondir +} + +func (conf *ControllerConf) VERSION() string { + wwlog.Debug("VERSION = '%s'", conf.Paths.version) + return conf.Paths.version +} + +func (conf *ControllerConf) RELEASE() string { + wwlog.Debug("RELEASE = '%s'", conf.Paths.release) + return conf.Paths.release +} + +func (conf *ControllerConf) WWCLIENTDIR() string { + wwlog.Debug("WWCLIENTDIR = '%s'", conf.Paths.wwclientdir) + return conf.Paths.wwclientdir +} diff --git a/internal/pkg/warewulfd/provision.go b/internal/pkg/warewulfd/provision.go index 67d23ac1..7b96ee6d 100644 --- a/internal/pkg/warewulfd/provision.go +++ b/internal/pkg/warewulfd/provision.go @@ -7,7 +7,6 @@ import ( "strconv" "text/template" - "github.com/hpcng/warewulf/internal/pkg/buildconfig" "github.com/hpcng/warewulf/internal/pkg/container" "github.com/hpcng/warewulf/internal/pkg/kernel" "github.com/hpcng/warewulf/internal/pkg/util" @@ -80,13 +79,13 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { if !node.Id.Defined() { wwlog.Error("%s (unknown/unconfigured node)", rinfo.hwaddr) if rinfo.stage == "ipxe" { - stage_file = path.Join(buildconfig.SYSCONFDIR(), "/warewulf/ipxe/unconfigured.ipxe") + stage_file = path.Join(conf.SYSCONFDIR(), "/warewulf/ipxe/unconfigured.ipxe") tmpl_data = iPxeTemplate{ Hwaddr: rinfo.hwaddr} } } else if rinfo.stage == "ipxe" { - stage_file = path.Join(buildconfig.SYSCONFDIR(), "warewulf/ipxe/"+node.Ipxe.Get()+".ipxe") + stage_file = path.Join(conf.SYSCONFDIR(), "warewulf/ipxe/"+node.Ipxe.Get()+".ipxe") tmpl_data = iPxeTemplate{ Id: node.Id.Get(), Cluster: node.ClusterName.Get(), From e117464f50384654432109e5bf8ce45060d13cb4 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Fri, 3 Mar 2023 16:12:53 +0100 Subject: [PATCH 09/25] paths need to be visibale for marshalling Signed-off-by: Christian Goll --- internal/pkg/overlay/datastructure.go | 1 + internal/pkg/warewulfconf/buildconfig.go.in | 28 +++++------ internal/pkg/warewulfconf/datastructure.go | 2 +- internal/pkg/warewulfconf/getter.go | 56 ++++++++++----------- 4 files changed, 44 insertions(+), 43 deletions(-) diff --git a/internal/pkg/overlay/datastructure.go b/internal/pkg/overlay/datastructure.go index f02bdedc..c7da9a70 100644 --- a/internal/pkg/overlay/datastructure.go +++ b/internal/pkg/overlay/datastructure.go @@ -32,6 +32,7 @@ type TemplateStruct struct { Nfs warewulfconf.NfsConf Warewulf warewulfconf.WarewulfConf Tftp warewulfconf.TftpConf + Paths warewulfconf.BuildConfig AllNodes []node.NodeInfo node.NodeConf // backward compatiblity diff --git a/internal/pkg/warewulfconf/buildconfig.go.in b/internal/pkg/warewulfconf/buildconfig.go.in index 7bdf96e1..3a5a52cd 100644 --- a/internal/pkg/warewulfconf/buildconfig.go.in +++ b/internal/pkg/warewulfconf/buildconfig.go.in @@ -1,18 +1,18 @@ package warewulfconf type BuildConfig struct { - bindir string `default:"@BINDIR@"` - sysconfdir string `default:"@SYSCONFDIR@"` - datadir string `default:"@DATADIR@"` - localstatedir string `default:"@LOCALSTATEDIR@"` - srvdir string `default:"@SRVDIR@"` - tftpdir string `default:"@TFTPDIR@"` - firewallddir string `default:"@FIREWALLDDIR@"` - systemddir string `default:"@SYSTEMDDIR@"` - wwoverlaydir string `default:"@WWOVERLAYDIR@"` - wwchrootdir string `default:"@WWCHROOTDIR@"` - wwprovisiondir string `default:"@WWPROVISIONDIR@"` - version string `default:"@VERSION@"` - release string `default:"@RELEASE@"` - wwclientdir string `default:"@WWCLIENTDIR@"` + Bindir string `default:"@BINDIR@"` + Sysconfdir string `default:"@SYSCONFDIR@"` + Datadir string `default:"@DATADIR@"` + Localstatedir string `default:"@LOCALSTATEDIR@"` + Srvdir string `default:"@SRVDIR@"` + Tftpdir string `default:"@TFTPDIR@"` + Firewallddir string `default:"@FIREWALLDDIR@"` + Systemddir string `default:"@SYSTEMDDIR@"` + Wwoverlaydir string `default:"@WWOVERLAYDIR@"` + Wwchrootdir string `default:"@WWCHROOTDIR@"` + Wwprovisiondir string `default:"@WWPROVISIONDIR@"` + Version string `default:"@VERSION@"` + Release string `default:"@RELEASE@"` + Wwclientdir string `default:"@WWCLIENTDIR@"` } diff --git a/internal/pkg/warewulfconf/datastructure.go b/internal/pkg/warewulfconf/datastructure.go index 33d77fe2..4c4ae71b 100644 --- a/internal/pkg/warewulfconf/datastructure.go +++ b/internal/pkg/warewulfconf/datastructure.go @@ -18,7 +18,7 @@ type ControllerConf struct { 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:"mypaths"` + Paths *BuildConfig `yaml:"paths"` current bool } diff --git a/internal/pkg/warewulfconf/getter.go b/internal/pkg/warewulfconf/getter.go index 6a3f5a01..6d92a2fe 100644 --- a/internal/pkg/warewulfconf/getter.go +++ b/internal/pkg/warewulfconf/getter.go @@ -3,71 +3,71 @@ package warewulfconf import "github.com/hpcng/warewulf/internal/pkg/wwlog" func (conf *ControllerConf) BINDIR() string { - wwlog.Debug("BINDIR = '%s'", conf.Paths.bindir) - return conf.Paths.bindir + wwlog.Debug("BINDIR = '%s'", conf.Paths.Bindir) + return conf.Paths.Bindir } func (conf *ControllerConf) DATADIR() string { - wwlog.Debug("DATADIR = '%s'", conf.Paths.datadir) - return conf.Paths.datadir + wwlog.Debug("DATADIR = '%s'", conf.Paths.Datadir) + return conf.Paths.Datadir } func (conf *ControllerConf) SYSCONFDIR() string { - wwlog.Debug("SYSCONFDIR = '%s'", conf.Paths.sysconfdir) - return conf.Paths.sysconfdir + wwlog.Debug("SYSCONFDIR = '%s'", conf.Paths.Sysconfdir) + return conf.Paths.Sysconfdir } func (conf *ControllerConf) LOCALSTATEDIR() string { - wwlog.Debug("LOCALSTATEDIR = '%s'", conf.Paths.localstatedir) - return conf.Paths.localstatedir + wwlog.Debug("LOCALSTATEDIR = '%s'", conf.Paths.Localstatedir) + return conf.Paths.Localstatedir } func (conf *ControllerConf) SRVDIR() string { - wwlog.Debug("SRVDIR = '%s'", conf.Paths.srvdir) - return conf.Paths.srvdir + wwlog.Debug("SRVDIR = '%s'", conf.Paths.Srvdir) + return conf.Paths.Srvdir } func (conf *ControllerConf) TFTPDIR() string { - wwlog.Debug("TFTPDIR = '%s'", conf.Paths.tftpdir) - return conf.Paths.tftpdir + wwlog.Debug("TFTPDIR = '%s'", conf.Paths.Tftpdir) + return conf.Paths.Tftpdir } func (conf *ControllerConf) FIREWALLDDIR() string { - wwlog.Debug("FIREWALLDDIR = '%s'", conf.Paths.firewallddir) - return conf.Paths.firewallddir + wwlog.Debug("FIREWALLDDIR = '%s'", conf.Paths.Firewallddir) + return conf.Paths.Firewallddir } func (conf *ControllerConf) SYSTEMDDIR() string { - wwlog.Debug("SYSTEMDDIR = '%s'", conf.Paths.systemddir) - return conf.Paths.systemddir + wwlog.Debug("SYSTEMDDIR = '%s'", conf.Paths.Systemddir) + return conf.Paths.Systemddir } func (conf *ControllerConf) WWOVERLAYDIR() string { - wwlog.Debug("WWOVERLAYDIR = '%s'", conf.Paths.wwoverlaydir) - return conf.Paths.wwoverlaydir + wwlog.Debug("WWOVERLAYDIR = '%s'", conf.Paths.Wwoverlaydir) + return conf.Paths.Wwoverlaydir } func (conf *ControllerConf) WWCHROOTDIR() string { - wwlog.Debug("WWCHROOTDIR = '%s'", conf.Paths.wwchrootdir) - return conf.Paths.wwchrootdir + wwlog.Debug("WWCHROOTDIR = '%s'", conf.Paths.Wwchrootdir) + return conf.Paths.Wwchrootdir } func (conf *ControllerConf) WWPROVISIONDIR() string { - wwlog.Debug("WWPROVISIONDIR = '%s'", conf.Paths.wwprovisiondir) - return conf.Paths.wwprovisiondir + wwlog.Debug("WWPROVISIONDIR = '%s'", conf.Paths.Wwprovisiondir) + return conf.Paths.Wwprovisiondir } func (conf *ControllerConf) VERSION() string { - wwlog.Debug("VERSION = '%s'", conf.Paths.version) - return conf.Paths.version + wwlog.Debug("VERSION = '%s'", conf.Paths.Version) + return conf.Paths.Version } func (conf *ControllerConf) RELEASE() string { - wwlog.Debug("RELEASE = '%s'", conf.Paths.release) - return conf.Paths.release + wwlog.Debug("RELEASE = '%s'", conf.Paths.Release) + return conf.Paths.Release } func (conf *ControllerConf) WWCLIENTDIR() string { - wwlog.Debug("WWCLIENTDIR = '%s'", conf.Paths.wwclientdir) - return conf.Paths.wwclientdir + wwlog.Debug("WWCLIENTDIR = '%s'", conf.Paths.Wwclientdir) + return conf.Paths.Wwclientdir } From c708154924699b77fdcbbbf291643efde9012a5a Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Fri, 3 Mar 2023 16:21:44 +0100 Subject: [PATCH 10/25] added new build in vars to rest of binaries Signed-off-by: Christian Goll --- cmd/update_configuration/update_configuration.go | 3 +-- internal/app/api/wwapic/wwapic.go | 5 +++-- internal/app/api/wwapid/wwapid.go | 5 +++-- internal/app/api/wwapird/wwapird.go | 6 +++--- internal/app/wwclient/root.go | 4 ++-- internal/app/wwctl/root.go | 8 ++++---- internal/pkg/warewulfconf/constructors.go | 2 +- 7 files changed, 17 insertions(+), 16 deletions(-) diff --git a/cmd/update_configuration/update_configuration.go b/cmd/update_configuration/update_configuration.go index c64de182..728eaaf6 100644 --- a/cmd/update_configuration/update_configuration.go +++ b/cmd/update_configuration/update_configuration.go @@ -8,7 +8,6 @@ import ( "github.com/hpcng/warewulf/cmd/update_configuration/vers42" "github.com/hpcng/warewulf/cmd/update_configuration/vers43" - "github.com/hpcng/warewulf/internal/pkg/buildconfig" "github.com/hpcng/warewulf/internal/pkg/util" "gopkg.in/yaml.v2" ) @@ -125,7 +124,7 @@ func main() { var endVers int var startVers int flag.StringVar(&confFile, "f", "", "Config file for update") - flag.IntVar(&endVers, "e", buildconfig.WWVer, "Final version of configuration file") + flag.IntVar(&endVers, "e", 0, "Final version of configuration file") flag.IntVar(&startVers, "s", 0, "Start version of configuration file, 0 is for autodetection") flag.BoolVar(&nowrite, "n", false, "Do not write, just print new conf to terminal") flag.BoolVar(&quiet, "q", false, "Do not print what the program is doing") diff --git a/internal/app/api/wwapic/wwapic.go b/internal/app/api/wwapic/wwapic.go index 65cd85b5..bd35a973 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/buildconfig" + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -24,9 +24,10 @@ import ( func main() { log.Println("Client running") + conf := warewulfconf.New() // Read the config file. - config, err := apiconfig.NewClient(path.Join(buildconfig.SYSCONFDIR(), "warewulf/wwapic.conf")) + config, err := apiconfig.NewClient(path.Join(conf.SYSCONFDIR(), "warewulf/wwapic.conf")) if err != nil { log.Fatalf("err: %v", err) } diff --git a/internal/app/api/wwapid/wwapid.go b/internal/app/api/wwapid/wwapid.go index 1cea854f..0678114c 100644 --- a/internal/app/api/wwapid/wwapid.go +++ b/internal/app/api/wwapid/wwapid.go @@ -15,8 +15,8 @@ import ( "github.com/hpcng/warewulf/internal/pkg/api/container" apinode "github.com/hpcng/warewulf/internal/pkg/api/node" "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" - "github.com/hpcng/warewulf/internal/pkg/buildconfig" "github.com/hpcng/warewulf/internal/pkg/version" + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" @@ -34,8 +34,9 @@ var apiVersion string func main() { log.Println("Server running") + conf := warewulfconf.New() // Read the config file. - config, err := apiconfig.NewServer(path.Join(buildconfig.SYSCONFDIR(), "warewulf/wwapid.conf")) + config, err := apiconfig.NewServer(path.Join(conf.SYSCONFDIR(), "warewulf/wwapid.conf")) if err != nil { log.Fatalf("err: %v", err) } diff --git a/internal/app/api/wwapird/wwapird.go b/internal/app/api/wwapird/wwapird.go index dcbaecf1..13f12d4e 100644 --- a/internal/app/api/wwapird/wwapird.go +++ b/internal/app/api/wwapird/wwapird.go @@ -18,20 +18,20 @@ import ( "google.golang.org/grpc/credentials/insecure" "github.com/hpcng/warewulf/internal/pkg/api/apiconfig" + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" gw "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" "path" - - "github.com/hpcng/warewulf/internal/pkg/buildconfig" ) func run() error { log.Println("test0") + conf := warewulfconf.New() // Read the config file. - config, err := apiconfig.NewClientServer(path.Join(buildconfig.SYSCONFDIR(), "warewulf/wwapird.conf")) + config, err := apiconfig.NewClientServer(path.Join(conf.SYSCONFDIR(), "warewulf/wwapird.conf")) if err != nil { glog.Fatalf("Failed to read config file, err: %v", err) } diff --git a/internal/app/wwclient/root.go b/internal/app/wwclient/root.go index e657c21b..72691595 100644 --- a/internal/app/wwclient/root.go +++ b/internal/app/wwclient/root.go @@ -58,7 +58,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { return errors.New("found pidfile " + PIDFile + " not starting") } - if os.Args[0] == path.Join(buildconfig.WWCLIENTDIR(), "wwclient") { + if os.Args[0] == path.Join(conf.WWCLIENTDIR(), "wwclient") { err := os.Chdir("/") if err != nil { wwlog.Error("failed to change dir: %s", err) @@ -70,7 +70,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } else { fmt.Printf("Called via: %s\n", os.Args[0]) fmt.Printf("Runtime overlay is being put in '/warewulf/wwclient-test' rather than '/'\n") - fmt.Printf("For full functionality call with: %s\n", path.Join(buildconfig.WWCLIENTDIR(), "wwclient")) + fmt.Printf("For full functionality call with: %s\n", path.Join(conf.WWCLIENTDIR(), "wwclient")) err := os.MkdirAll("/warewulf/wwclient-test", 0755) if err != nil { wwlog.Error("failed to create dir: %s", err) diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go index f40e859d..a59a9739 100644 --- a/internal/app/wwctl/root.go +++ b/internal/app/wwctl/root.go @@ -63,7 +63,7 @@ func GetRootCommand() *cobra.Command { return rootCmd } -func rootPersistentPreRunE(cmd *cobra.Command, args []string) error { +func rootPersistentPreRunE(cmd *cobra.Command, args []string) (err error) { if DebugFlag { wwlog.SetLogLevel(wwlog.DEBUG) } else if verboseArg { @@ -76,9 +76,9 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) error { } conf := warewulfconf.New() if WarewulfConfArg != "" { - conf.ReadConf(WarewulfConfArg) + err = conf.ReadConf(WarewulfConfArg) } else if os.Getenv("WAREWULFCONF") != "" { - conf.ReadConf(os.Getenv("WAREWULFCONF")) + err = conf.ReadConf(os.Getenv("WAREWULFCONF")) } - return nil + return } diff --git a/internal/pkg/warewulfconf/constructors.go b/internal/pkg/warewulfconf/constructors.go index 0b01e760..f7cfc325 100644 --- a/internal/pkg/warewulfconf/constructors.go +++ b/internal/pkg/warewulfconf/constructors.go @@ -30,7 +30,7 @@ func New() (ret ControllerConf) { ret.Nfs = new(NfsConf) ret.Paths = new(BuildConfig) _ = defaults.Set(&ret) - ret.setDynamicDefaults() + _ = ret.setDynamicDefaults() cachedConf = ret cachedConf.current = true From 290c6449ddf05848ddcb7a276bf683b38743a122 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Mon, 6 Mar 2023 09:59:25 +0100 Subject: [PATCH 11/25] removed getters as configs must exportable Signed-off-by: Christian Goll --- .github/workflows/lint.yaml | 3 +- Makefile | 3 +- internal/app/api/wwapic/wwapic.go | 2 +- internal/app/api/wwapid/wwapid.go | 2 +- internal/app/api/wwapird/wwapird.go | 2 +- internal/app/wwclient/root.go | 4 +- internal/pkg/configure/ssh.go | 4 +- internal/pkg/configure/tftp.go | 4 +- internal/pkg/container/config.go | 4 +- internal/pkg/kernel/kernel.go | 2 +- internal/pkg/node/constructors.go | 4 +- internal/pkg/overlay/config.go | 4 +- internal/pkg/overlay/funcmap.go | 7 +- internal/pkg/version/version.go | 2 +- internal/pkg/warewulfconf/buildconfig.go.in | 8 +-- internal/pkg/warewulfconf/getter.go | 73 --------------------- internal/pkg/warewulfd/provision.go | 4 +- 17 files changed, 29 insertions(+), 103 deletions(-) delete mode 100644 internal/pkg/warewulfconf/getter.go diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 53901d02..3c6eafee 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -21,7 +21,8 @@ jobs: go: [ '1.17' ] steps: - uses: actions/checkout@v3 - + - name: Create config + run: make config - name: Setup go ${{ matrix.go }} uses: actions/setup-go@v3 with: diff --git a/Makefile b/Makefile index b8a8db93..10212305 100644 --- a/Makefile +++ b/Makefile @@ -101,7 +101,7 @@ export GOPROXY WW_GO_BUILD_TAGS := containers_image_openpgp containers_image_ostree # Default target -all: config vendor wwctl wwclient man_pages config_defaults wwapid wwapic wwapird +all: config vendor wwctl wwclient man_pages wwapid wwapic wwapird # Validate source and build all packages build: lint test-it vet all @@ -287,7 +287,6 @@ contclean: rm -f config rm -f Defaults.mk rm -rf $(TOOLS_DIR) - rm -f config_defaults rm -f update_configuration rm -f etc/wwapi{c,d,rd}.conf diff --git a/internal/app/api/wwapic/wwapic.go b/internal/app/api/wwapic/wwapic.go index bd35a973..36cfb002 100644 --- a/internal/app/api/wwapic/wwapic.go +++ b/internal/app/api/wwapic/wwapic.go @@ -27,7 +27,7 @@ func main() { conf := warewulfconf.New() // Read the config file. - config, err := apiconfig.NewClient(path.Join(conf.SYSCONFDIR(), "warewulf/wwapic.conf")) + config, err := apiconfig.NewClient(path.Join(conf.Paths.Sysconfdir, "warewulf/wwapic.conf")) if err != nil { log.Fatalf("err: %v", err) } diff --git a/internal/app/api/wwapid/wwapid.go b/internal/app/api/wwapid/wwapid.go index 0678114c..ce7457e5 100644 --- a/internal/app/api/wwapid/wwapid.go +++ b/internal/app/api/wwapid/wwapid.go @@ -36,7 +36,7 @@ func main() { conf := warewulfconf.New() // Read the config file. - config, err := apiconfig.NewServer(path.Join(conf.SYSCONFDIR(), "warewulf/wwapid.conf")) + config, err := apiconfig.NewServer(path.Join(conf.Paths.Sysconfdir, "warewulf/wwapid.conf")) if err != nil { log.Fatalf("err: %v", err) } diff --git a/internal/app/api/wwapird/wwapird.go b/internal/app/api/wwapird/wwapird.go index 13f12d4e..e9b210ac 100644 --- a/internal/app/api/wwapird/wwapird.go +++ b/internal/app/api/wwapird/wwapird.go @@ -31,7 +31,7 @@ func run() error { conf := warewulfconf.New() // Read the config file. - config, err := apiconfig.NewClientServer(path.Join(conf.SYSCONFDIR(), "warewulf/wwapird.conf")) + config, err := apiconfig.NewClientServer(path.Join(conf.Paths.Sysconfdir, "warewulf/wwapird.conf")) if err != nil { glog.Fatalf("Failed to read config file, err: %v", err) } diff --git a/internal/app/wwclient/root.go b/internal/app/wwclient/root.go index 72691595..b63f3f41 100644 --- a/internal/app/wwclient/root.go +++ b/internal/app/wwclient/root.go @@ -58,7 +58,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { return errors.New("found pidfile " + PIDFile + " not starting") } - if os.Args[0] == path.Join(conf.WWCLIENTDIR(), "wwclient") { + if os.Args[0] == path.Join(conf.Paths.WWClientdir, "wwclient") { err := os.Chdir("/") if err != nil { wwlog.Error("failed to change dir: %s", err) @@ -70,7 +70,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } else { fmt.Printf("Called via: %s\n", os.Args[0]) fmt.Printf("Runtime overlay is being put in '/warewulf/wwclient-test' rather than '/'\n") - fmt.Printf("For full functionality call with: %s\n", path.Join(conf.WWCLIENTDIR(), "wwclient")) + fmt.Printf("For full functionality call with: %s\n", path.Join(conf.Paths.WWClientdir, "wwclient")) err := os.MkdirAll("/warewulf/wwclient-test", 0755) if err != nil { wwlog.Error("failed to create dir: %s", err) diff --git a/internal/pkg/configure/ssh.go b/internal/pkg/configure/ssh.go index 0c60e053..10186bc2 100644 --- a/internal/pkg/configure/ssh.go +++ b/internal/pkg/configure/ssh.go @@ -15,9 +15,9 @@ func SSH() error { if os.Getuid() == 0 { fmt.Printf("Updating system keys\n") conf := warewulfconf.New() - wwkeydir := path.Join(conf.SYSCONFDIR(), "warewulf/keys") + "/" + wwkeydir := path.Join(conf.Paths.Sysconfdir, "warewulf/keys") + "/" - err := os.MkdirAll(path.Join(conf.SYSCONFDIR(), "warewulf/keys"), 0755) + err := os.MkdirAll(path.Join(conf.Paths.Sysconfdir, "warewulf/keys"), 0755) if err != nil { wwlog.Error("Could not create base directory: %s", err) os.Exit(1) diff --git a/internal/pkg/configure/tftp.go b/internal/pkg/configure/tftp.go index 718a4030..74bed889 100644 --- a/internal/pkg/configure/tftp.go +++ b/internal/pkg/configure/tftp.go @@ -12,7 +12,7 @@ import ( func TFTP() error { controller := warewulfconf.New() - var tftpdir string = path.Join(controller.TFTPDIR(), "warewulf") + var tftpdir string = path.Join(controller.Paths.Tftpdir, "warewulf") err := os.MkdirAll(tftpdir, 0755) if err != nil { @@ -27,7 +27,7 @@ func TFTP() error { continue } copyCheck[f] = true - err = util.SafeCopyFile(path.Join(controller.DATADIR(), f), path.Join(tftpdir, f)) + err = util.SafeCopyFile(path.Join(controller.Paths.Datadir, f), path.Join(tftpdir, f)) if err != nil { wwlog.Warn("ipxe binary could not be copied, booting may not work: %s", err) } diff --git a/internal/pkg/container/config.go b/internal/pkg/container/config.go index ebbc8569..ecd751fa 100644 --- a/internal/pkg/container/config.go +++ b/internal/pkg/container/config.go @@ -8,7 +8,7 @@ import ( func SourceParentDir() string { conf := warewulfconf.New() - return conf.WWCHROOTDIR() + return conf.Paths.WWChrootdir } func SourceDir(name string) string { @@ -21,7 +21,7 @@ func RootFsDir(name string) string { func ImageParentDir() string { conf := warewulfconf.New() - return path.Join(conf.WWPROVISIONDIR(), "container/") + return path.Join(conf.Paths.WWProvisiondir, "container/") } func ImageFile(name string) string { diff --git a/internal/pkg/kernel/kernel.go b/internal/pkg/kernel/kernel.go index 14ce78a2..32a1da47 100644 --- a/internal/pkg/kernel/kernel.go +++ b/internal/pkg/kernel/kernel.go @@ -29,7 +29,7 @@ var ( func KernelImageTopDir() string { conf := warewulfconf.New() - return path.Join(conf.WWPROVISIONDIR(), "kernel") + return path.Join(conf.Paths.WWProvisiondir, "kernel") } func KernelImage(kernelName string) string { diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index 3d83f8df..bcf57d0b 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -39,10 +39,10 @@ defaultnode: func init() { conf := warewulfconf.New() if ConfigFile == "" { - ConfigFile = path.Join(conf.SYSCONFDIR(), "warewulf/nodes.conf") + ConfigFile = path.Join(conf.Paths.Sysconfdir, "warewulf/nodes.conf") } if DefaultConfig == "" { - DefaultConfig = path.Join(conf.SYSCONFDIR(), "warewulf/defaults.conf") + DefaultConfig = path.Join(conf.Paths.Sysconfdir, "warewulf/defaults.conf") } } diff --git a/internal/pkg/overlay/config.go b/internal/pkg/overlay/config.go index 3f5b1bd4..91452e4a 100644 --- a/internal/pkg/overlay/config.go +++ b/internal/pkg/overlay/config.go @@ -10,7 +10,7 @@ import ( func OverlaySourceTopDir() string { conf := warewulfconf.New() - return conf.WWOVERLAYDIR() + return conf.Paths.WWOverlaydir } /* @@ -32,5 +32,5 @@ Returns the overlay name of the image for a given node */ func OverlayImage(nodeName string, overlayName []string) string { conf := warewulfconf.New() - return path.Join(conf.WWPROVISIONDIR(), "overlays/", nodeName, strings.Join(overlayName, "-")+".img") + return path.Join(conf.Paths.WWProvisiondir, "overlays/", nodeName, strings.Join(overlayName, "-")+".img") } diff --git a/internal/pkg/overlay/funcmap.go b/internal/pkg/overlay/funcmap.go index d3fe05e5..ebdb1b1a 100644 --- a/internal/pkg/overlay/funcmap.go +++ b/internal/pkg/overlay/funcmap.go @@ -14,13 +14,12 @@ import ( /* Reads a file file from the host fs. If the file has nor '/' prefix -the path is relative to SYSCONFDIR. -Templates in the file are no evaluated. +the path is relative to Paths.SysconfdirTemplates in the file are no evaluated. */ func templateFileInclude(inc string) string { conf := warewulfconf.New() if !strings.HasPrefix(inc, "/") { - inc = path.Join(conf.SYSCONFDIR(), "warewulf", inc) + inc = path.Join(conf.Paths.Sysconfdir, "warewulf", inc) } wwlog.Debug("Including file into template: %s", inc) content, err := os.ReadFile(inc) @@ -38,7 +37,7 @@ Templates in the file are no evaluated. func templateFileBlock(inc string, abortStr string) (string, error) { conf := warewulfconf.New() if !strings.HasPrefix(inc, "/") { - inc = path.Join(conf.SYSCONFDIR(), "warewulf", inc) + inc = path.Join(conf.Paths.Sysconfdir, "warewulf", inc) } wwlog.Debug("Including file block into template: %s", inc) readFile, err := os.Open(inc) diff --git a/internal/pkg/version/version.go b/internal/pkg/version/version.go index 5ce00f23..6b4771bb 100644 --- a/internal/pkg/version/version.go +++ b/internal/pkg/version/version.go @@ -12,7 +12,7 @@ Return the version of wwctl */ func GetVersion() string { conf := warewulfconf.New() - return fmt.Sprintf("%s-%s", conf.VERSION(), conf.RELEASE()) + return fmt.Sprintf("%s-%s", conf.Paths.Version, conf.Paths.Release) } /* diff --git a/internal/pkg/warewulfconf/buildconfig.go.in b/internal/pkg/warewulfconf/buildconfig.go.in index 3a5a52cd..cd7ac2f4 100644 --- a/internal/pkg/warewulfconf/buildconfig.go.in +++ b/internal/pkg/warewulfconf/buildconfig.go.in @@ -9,10 +9,10 @@ type BuildConfig struct { Tftpdir string `default:"@TFTPDIR@"` Firewallddir string `default:"@FIREWALLDDIR@"` Systemddir string `default:"@SYSTEMDDIR@"` - Wwoverlaydir string `default:"@WWOVERLAYDIR@"` - Wwchrootdir string `default:"@WWCHROOTDIR@"` - Wwprovisiondir string `default:"@WWPROVISIONDIR@"` + WWOverlaydir string `default:"@WWOVERLAYDIR@"` + WWChrootdir string `default:"@WWCHROOTDIR@"` + WWProvisiondir string `default:"@WWPROVISIONDIR@"` Version string `default:"@VERSION@"` Release string `default:"@RELEASE@"` - Wwclientdir string `default:"@WWCLIENTDIR@"` + WWClientdir string `default:"@WWCLIENTDIR@"` } diff --git a/internal/pkg/warewulfconf/getter.go b/internal/pkg/warewulfconf/getter.go deleted file mode 100644 index 6d92a2fe..00000000 --- a/internal/pkg/warewulfconf/getter.go +++ /dev/null @@ -1,73 +0,0 @@ -package warewulfconf - -import "github.com/hpcng/warewulf/internal/pkg/wwlog" - -func (conf *ControllerConf) BINDIR() string { - wwlog.Debug("BINDIR = '%s'", conf.Paths.Bindir) - return conf.Paths.Bindir -} - -func (conf *ControllerConf) DATADIR() string { - wwlog.Debug("DATADIR = '%s'", conf.Paths.Datadir) - return conf.Paths.Datadir -} - -func (conf *ControllerConf) SYSCONFDIR() string { - wwlog.Debug("SYSCONFDIR = '%s'", conf.Paths.Sysconfdir) - return conf.Paths.Sysconfdir -} - -func (conf *ControllerConf) LOCALSTATEDIR() string { - wwlog.Debug("LOCALSTATEDIR = '%s'", conf.Paths.Localstatedir) - return conf.Paths.Localstatedir -} - -func (conf *ControllerConf) SRVDIR() string { - wwlog.Debug("SRVDIR = '%s'", conf.Paths.Srvdir) - return conf.Paths.Srvdir -} - -func (conf *ControllerConf) TFTPDIR() string { - wwlog.Debug("TFTPDIR = '%s'", conf.Paths.Tftpdir) - return conf.Paths.Tftpdir -} - -func (conf *ControllerConf) FIREWALLDDIR() string { - wwlog.Debug("FIREWALLDDIR = '%s'", conf.Paths.Firewallddir) - return conf.Paths.Firewallddir -} - -func (conf *ControllerConf) SYSTEMDDIR() string { - wwlog.Debug("SYSTEMDDIR = '%s'", conf.Paths.Systemddir) - return conf.Paths.Systemddir -} - -func (conf *ControllerConf) WWOVERLAYDIR() string { - wwlog.Debug("WWOVERLAYDIR = '%s'", conf.Paths.Wwoverlaydir) - return conf.Paths.Wwoverlaydir -} - -func (conf *ControllerConf) WWCHROOTDIR() string { - wwlog.Debug("WWCHROOTDIR = '%s'", conf.Paths.Wwchrootdir) - return conf.Paths.Wwchrootdir -} - -func (conf *ControllerConf) WWPROVISIONDIR() string { - wwlog.Debug("WWPROVISIONDIR = '%s'", conf.Paths.Wwprovisiondir) - return conf.Paths.Wwprovisiondir -} - -func (conf *ControllerConf) VERSION() string { - wwlog.Debug("VERSION = '%s'", conf.Paths.Version) - return conf.Paths.Version -} - -func (conf *ControllerConf) RELEASE() string { - wwlog.Debug("RELEASE = '%s'", conf.Paths.Release) - return conf.Paths.Release -} - -func (conf *ControllerConf) WWCLIENTDIR() string { - wwlog.Debug("WWCLIENTDIR = '%s'", conf.Paths.Wwclientdir) - return conf.Paths.Wwclientdir -} diff --git a/internal/pkg/warewulfd/provision.go b/internal/pkg/warewulfd/provision.go index 7b96ee6d..c96267cc 100644 --- a/internal/pkg/warewulfd/provision.go +++ b/internal/pkg/warewulfd/provision.go @@ -79,13 +79,13 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { if !node.Id.Defined() { wwlog.Error("%s (unknown/unconfigured node)", rinfo.hwaddr) if rinfo.stage == "ipxe" { - stage_file = path.Join(conf.SYSCONFDIR(), "/warewulf/ipxe/unconfigured.ipxe") + stage_file = path.Join(conf.Paths.Sysconfdir, "/warewulf/ipxe/unconfigured.ipxe") tmpl_data = iPxeTemplate{ Hwaddr: rinfo.hwaddr} } } else if rinfo.stage == "ipxe" { - stage_file = path.Join(conf.SYSCONFDIR(), "warewulf/ipxe/"+node.Ipxe.Get()+".ipxe") + stage_file = path.Join(conf.Paths.Sysconfdir, "warewulf/ipxe/"+node.Ipxe.Get()+".ipxe") tmpl_data = iPxeTemplate{ Id: node.Id.Get(), Cluster: node.ClusterName.Get(), From 4a8384dbf3fe39acfbbdd6ec237e28a63ade382a Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Mon, 6 Mar 2023 15:51:34 +0100 Subject: [PATCH 12/25] added Changelog Signed-off-by: Christian Goll --- CHANGELOG.md | 11 +++ Makefile | 13 +-- internal/app/wwctl/root.go | 18 ++-- internal/pkg/warewulfconf/constructors.go | 102 ++++++++++++++-------- 4 files changed, 97 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ab78966..bf73cb33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `/etc/hosts` - Added experimental dnsmasq support. +- new subcommand `wwctl genconf` is available with following subcommands: + * `completions` which will create the files used for bash-completion. Also + fish an zsh completions can be generated + * `defaults` which will generate a valid `defaults.conf` + * `man` which will generate the man pages in the specified directory + * `reference` which will generate a reference documentation for the wwctl commands + * `warwulfconf print` which will print the used `warewulf.conf`. If there is no valid + `warewulf.conf` a valid configuration is provided, prefilled with default values + and an IP configuration derived from the network configuration of the host +- All paths can now be configured in `warewulf.conf`, check the paths section of of + `wwctl --emptyconf genconfig warewulfconf print` for the available paths. ## [4.4.0] 2023-01-18 ### Added diff --git a/Makefile b/Makefile index 10212305..397611dd 100644 --- a/Makefile +++ b/Makefile @@ -218,7 +218,9 @@ init: wwctl: $(WWCTL_DEPS) @echo Building "$@" - @cd cmd/wwctl; GOOS=linux go build -mod vendor -tags "$(WW_GO_BUILD_TAGS)" -o ../../wwctl + @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 "$@" @@ -226,10 +228,11 @@ wwclient: $(WWCLIENT_DEPS) -X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=/etc/warewulf/warewulf.conf'" -o ../../wwclient man_pages: wwctl - install -d man_pages - ./wwctl genconfig man man_pages - cp docs/man/man5/*.5 ./man_pages/ - cd man_pages; for i in wwctl*1 *.5; do echo "Compressing manpage: $$i"; gzip --force $$i; done + @install -d man_pages + @./wwctl --emptyconf genconfig man man_pages + @cp docs/man/man5/*.5 ./man_pages/ + @echo -n "Compressing manpage: " + @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'\ diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go index a59a9739..395c4b4c 100644 --- a/internal/app/wwctl/root.go +++ b/internal/app/wwctl/root.go @@ -34,6 +34,7 @@ var ( DebugFlag bool LogLevel int WarewulfConfArg string + AllowEmptyConf bool ) func init() { @@ -42,9 +43,10 @@ func init() { rootCmd.PersistentFlags().IntVar(&LogLevel, "loglevel", wwlog.INFO, "Set log level to given string") _ = rootCmd.PersistentFlags().MarkHidden("loglevel") rootCmd.PersistentFlags().StringVar(&WarewulfConfArg, "warewulfconf", "", "Set the warewulf configuration file") + rootCmd.PersistentFlags().BoolVar(&AllowEmptyConf, "emptyconf", false, "Allow empty configuration") + _ = rootCmd.PersistentFlags().MarkHidden("emptyconf") rootCmd.SetUsageTemplate(help.UsageTemplate) rootCmd.SetHelpTemplate(help.HelpTemplate) - rootCmd.AddCommand(overlay.GetCommand()) rootCmd.AddCommand(container.GetCommand()) rootCmd.AddCommand(node.GetCommand()) @@ -75,10 +77,16 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) (err error) { wwlog.SetLogLevel(LogLevel) } conf := warewulfconf.New() - if WarewulfConfArg != "" { - err = conf.ReadConf(WarewulfConfArg) - } else if os.Getenv("WAREWULFCONF") != "" { - err = conf.ReadConf(os.Getenv("WAREWULFCONF")) + if !AllowEmptyConf { + if WarewulfConfArg != "" { + err = conf.ReadConf(WarewulfConfArg) + } else if os.Getenv("WAREWULFCONF") != "" { + err = conf.ReadConf(os.Getenv("WAREWULFCONF")) + } else { + err = conf.ReadConf(warewulfconf.ConfigFile) + } + } else { + conf.SetDynamicDefaults() } return } diff --git a/internal/pkg/warewulfconf/constructors.go b/internal/pkg/warewulfconf/constructors.go index f7cfc325..a85d070b 100644 --- a/internal/pkg/warewulfconf/constructors.go +++ b/internal/pkg/warewulfconf/constructors.go @@ -1,12 +1,12 @@ package warewulfconf import ( - "errors" "fmt" "net" "os" - "github.com/brotherpowers/ipsubnet" + "github.com/pkg/errors" + "github.com/creasty/defaults" "github.com/hpcng/warewulf/internal/pkg/wwlog" "gopkg.in/yaml.v2" @@ -20,32 +20,32 @@ var ConfigFile string Creates a new empty ControllerConf object, returns a cached one if called in a nother context. */ -func New() (ret ControllerConf) { +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 { - ret.Warewulf = new(WarewulfConf) - ret.Dhcp = new(DhcpConf) - ret.Tftp = new(TftpConf) - ret.Nfs = new(NfsConf) - ret.Paths = new(BuildConfig) - _ = defaults.Set(&ret) - _ = ret.setDynamicDefaults() + conf.Warewulf = new(WarewulfConf) + conf.Dhcp = new(DhcpConf) + conf.Tftp = new(TftpConf) + conf.Nfs = new(NfsConf) + conf.Paths = new(BuildConfig) + _ = defaults.Set(&conf) - cachedConf = ret + cachedConf = conf cachedConf.current = true } else { // If cached struct isn't empty, use it as the return value - ret = cachedConf + conf = cachedConf } - return ret + 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 @@ -67,6 +67,10 @@ func (conf *ControllerConf) Read(data []byte) (err error) { if err != nil { return } + err = conf.SetDynamicDefaults() + if err != nil { + return + } if len(conf.Tftp.IpxeBinaries) == 0 { conf.Tftp.IpxeBinaries = defIpxe } @@ -78,38 +82,60 @@ func (conf *ControllerConf) Read(data []byte) (err error) { /* Set the runtime defaults like IP address of running system to the config */ -func (ret *ControllerConf) setDynamicDefaults() (err error) { - if ret.Ipaddr == "" || ret.Netmask == "" { - conn, error := net.Dial("udp", "8.8.8.8:80") - if error != nil { - return err +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) } - defer conn.Close() - localIp := conn.LocalAddr().(*net.UDPAddr) - if ret.Ipaddr == "" { - ret.Ipaddr = localIp.IP.String() - wwlog.Verbose("IP address is not configured in warewulfd.conf, using %s", ret.Ipaddr) + _, network, err = net.ParseCIDR(conf.Ipaddr) + if err == nil { + mask = network.Mask + } else { + return errors.Wrap(err, "Couldn't parse IP address") } - if ret.Netmask == "" { - mask := localIp.IP.DefaultMask() - ret.Netmask = fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]) - wwlog.Verbose("Netmask address is not configured in warewulfd.conf, using %s", ret.Netmask) + 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) - if ret.Network == "" { - mask := net.IPMask(net.ParseIP(ret.Netmask).To4()) - size, _ := mask.Size() - - sub := ipsubnet.SubnetCalculator(ret.Ipaddr, size) - - ret.Network = sub.GetNetworkPortion() } // check validity of ipv6 net - if ret.Ipaddr6 != "" { - _, ipv6net, err := net.ParseCIDR(ret.Ipaddr6) + if conf.Ipaddr6 != "" { + _, ipv6net, err := net.ParseCIDR(conf.Ipaddr6) if err != nil { - wwlog.Error("Invalid ipv6 address specified, mut be CIDR notation: %s", ret.Ipaddr6) + 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 { @@ -117,5 +143,7 @@ func (ret *ControllerConf) setDynamicDefaults() (err error) { return errors.New("invalid ipv6 network size") } } + cachedConf = *conf + cachedConf.current = true return } From c1e2100322183fbfd52ef022f5ba8e4decc2d889 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Tue, 7 Mar 2023 08:58:29 +0100 Subject: [PATCH 13/25] 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 a85d070b..47fd89c3 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 } @@ -147,3 +148,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") } From b00a619746d3a8d05c5dcd28e997aabb773f312a Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Tue, 7 Mar 2023 11:49:09 +0100 Subject: [PATCH 14/25] moved location of defaults.conf Signed-off-by: Christian Goll --- Makefile | 4 ++-- go.mod | 1 - go.sum | 2 -- internal/pkg/node/constructors.go | 8 +------- internal/pkg/warewulfconf/constructors.go | 1 - 5 files changed, 3 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 397611dd..b0b19c88 100644 --- a/Makefile +++ b/Makefile @@ -183,7 +183,7 @@ files: all test -f $(DESTDIR)$(WWCONFIGDIR)/wwapic.conf || install -m 644 etc/wwapic.conf $(DESTDIR)$(WWCONFIGDIR) test -f $(DESTDIR)$(WWCONFIGDIR)/wwapid.conf || install -m 644 etc/wwapid.conf $(DESTDIR)$(WWCONFIGDIR) test -f $(DESTDIR)$(WWCONFIGDIR)/wwapird.conf || install -m 644 etc/wwapird.conf $(DESTDIR)$(WWCONFIGDIR) - test -f $(DESTDIR)$(WWCONFIGDIR)/defaults.conf || ./wwctl genconfig defaults > $(DESTDIR)$(WWCONFIGDIR)/defaults.conf + test -f $(DESTDIR)$(DATADIR)/warewulf/defaults.conf || ./wwctl --emptyconf genconfig defaults > $(DESTDIR)$(DATADIR)/warewulf/defaults.conf cp -r etc/examples $(DESTDIR)$(WWCONFIGDIR)/ cp -r etc/ipxe $(DESTDIR)$(WWCONFIGDIR)/ cp -r overlays/* $(DESTDIR)$(WWOVERLAYDIR)/ @@ -247,7 +247,7 @@ dist: vendor config rm -rf .dist reference: wwctl - ./wwctl genconfig reference userdocs/reference/ + ./wwctl --emptyconf genconfig reference userdocs/reference/ latexpdf: reference make -C userdocs latexpdf diff --git a/go.mod b/go.mod index b9997019..7c791191 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/hpcng/warewulf go 1.16 require ( - github.com/brotherpowers/ipsubnet v0.0.0-20170914094241-30bc98f0a5b1 github.com/containers/image/v5 v5.7.0 github.com/containers/storage v1.30.0 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e diff --git a/go.sum b/go.sum index 415618da..940c14de 100644 --- a/go.sum +++ b/go.sum @@ -98,8 +98,6 @@ github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngE github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/brotherpowers/ipsubnet v0.0.0-20170914094241-30bc98f0a5b1 h1:80OMQcGycPqKFFabSaxOagCh7mgspnMl/X3YGmNi+j0= -github.com/brotherpowers/ipsubnet v0.0.0-20170914094241-30bc98f0a5b1/go.mod h1:mm9ZF6W76SwZtJpYzrVmTMuzmIhPX0SIuEosW/OTFd4= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index 89079144..10ee86ad 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -44,7 +44,7 @@ func init() { ConfigFile = path.Join(conf.Paths.Sysconfdir, "warewulf/nodes.conf") } if DefaultConfig == "" { - DefaultConfig = path.Join(conf.Paths.Sysconfdir, "warewulf/defaults.conf") + DefaultConfig = path.Join(conf.Paths.Datadir, "warewulf/defaults.conf") } cachedDB.current = false cachedDB.persist = true @@ -114,12 +114,6 @@ for every node */ func (config *NodeYaml) FindAllNodes() ([]NodeInfo, error) { var ret []NodeInfo - /* - wwconfig, err := warewulfconf.New() - if err != nil { - return ret, err - } - */ var defConf map[string]*NodeConf wwlog.Verbose("Opening defaults from file failed %s\n", DefaultConfig) defData, err := os.ReadFile(DefaultConfig) diff --git a/internal/pkg/warewulfconf/constructors.go b/internal/pkg/warewulfconf/constructors.go index 47fd89c3..2a1e0ff0 100644 --- a/internal/pkg/warewulfconf/constructors.go +++ b/internal/pkg/warewulfconf/constructors.go @@ -33,7 +33,6 @@ func New() (conf ControllerConf) { cachedConf = conf cachedConf.readConf = false cachedConf.current = true - } else { // If cached struct isn't empty, use it as the return value conf = cachedConf From 4750f96cc6bd84c3c0187c0de2824aba1b578135 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Tue, 7 Mar 2023 17:01:57 +0100 Subject: [PATCH 15/25] main_tests are not working as expected The global values which store the flags are not regenerated during test Signed-off-by: Christian Goll --- internal/app/wwctl/node/add/main.go | 9 +- internal/app/wwctl/node/add/main_test.go | 155 ++++++++++++++++++++++- internal/pkg/node/methods.go | 27 ++++ internal/pkg/node/methods_test.go | 32 +++++ 4 files changed, 213 insertions(+), 10 deletions(-) create mode 100644 internal/pkg/node/methods_test.go diff --git a/internal/app/wwctl/node/add/main.go b/internal/app/wwctl/node/add/main.go index 762907bf..dc24299e 100644 --- a/internal/app/wwctl/node/add/main.go +++ b/internal/app/wwctl/node/add/main.go @@ -12,13 +12,16 @@ import ( ) func CobraRunE(cmd *cobra.Command, args []string) error { - // remove the default network as the all network values are assigned + // remove the default network as all network values are assigned // to this network - if NetName != "" { + if _, ok := NodeConf.NetDevs["default"]; ok && NetName != "" { netDev := *NodeConf.NetDevs["default"] NodeConf.NetDevs[NetName] = &netDev delete(NodeConf.NetDevs, "default") - + } else { + if NodeConf.NetDevs["default"].Empty() { + delete(NodeConf.NetDevs, "default") + } } buffer, err := yaml.Marshal(NodeConf) if err != nil { diff --git a/internal/app/wwctl/node/add/main_test.go b/internal/app/wwctl/node/add/main_test.go index 29750a47..d6935790 100644 --- a/internal/app/wwctl/node/add/main_test.go +++ b/internal/app/wwctl/node/add/main_test.go @@ -2,6 +2,7 @@ package add import ( "bytes" + "fmt" "testing" "github.com/hpcng/warewulf/internal/pkg/node" @@ -14,7 +15,7 @@ func Test_Add(t *testing.T) { t.Helper() conf_yml := ` WW_INTERNAL: 0 - ` + ` nodes_yml := ` WW_INTERNAL: 43 ` @@ -33,6 +34,7 @@ WW_INTERNAL: 43 wantErr bool stdout string outDb string + flags map[string]string }{ {name: "single node add", args: []string{"n01"}, @@ -44,11 +46,50 @@ nodes: n01: profiles: - default - network devices: - default: {} `}, - {name: "double node add", - args: []string{"n0[1-2]"}, + {name: "single node add, profile foo", + args: []string{"n01"}, + flags: map[string]string{"profile": "foo"}, + wantErr: false, + stdout: "", + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: + n01: + profiles: + - foo +`}, + {name: "single node add with Kernel args", + args: []string{"n01"}, + flags: map[string]string{"kernelargs": "foo"}, + wantErr: false, + stdout: "", + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: + n01: + kernel: + args: foo + profiles: + - default +`}, + {name: "double node add explicit", + args: []string{"n01", "n02"}, + wantErr: false, + stdout: "", + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: + n01: + profiles: + - default + n02: + profiles: + - default +`}, + {name: "single node with ipaddr", + args: []string{"n01"}, + flags: map[string]string{"ipaddr": "10.10.0.1"}, wantErr: false, stdout: "", outDb: `WW_INTERNAL: 43 @@ -58,19 +99,111 @@ nodes: profiles: - default network devices: - default: {} + default: + ipaddr: 10.10.0.1 +`}, + {name: "three nodes with ipaddr", + args: []string{"n[01-02,03]"}, + flags: map[string]string{"ipaddr": "10.10.0.1"}, + wantErr: false, + stdout: "", + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: + n01: + profiles: + - default + network devices: + default: + ipaddr: 10.10.0.1 n02: profiles: - default network devices: - default: {} + default: + ipaddr: 10.10.0.2 + n03: + profiles: + - default + network devices: + default: + ipaddr: 10.10.0.3 +`}, + {name: "three nodes with ipaddr different network", + args: []string{"n[01-03]"}, + flags: map[string]string{"ipaddr": "10.10.0.1", "netname": "foo"}, + wantErr: false, + stdout: "", + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: + n01: + profiles: + - default + network devices: + foo: + ipaddr: 10.10.0.1 + n02: + profiles: + - default + network devices: + foo: + ipaddr: 10.10.0.2 + n03: + profiles: + - default + network devices: + foo: + ipaddr: 10.10.0.3 +`}, + {name: "three nodes with ipaddr different network, with ipmiaddr", + args: []string{"n[01-03]"}, + flags: map[string]string{"ipaddr": "10.10.0.1", "netname": "foo", "ipmiaddr": "10.20.0.1"}, + wantErr: false, + stdout: "", + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: + n01: + ipmi: + ipaddr: 10.20.0.1 + profiles: + - default + network devices: + foo: + ipaddr: 10.10.0.1 + n02: + ipmi: + ipaddr: 10.20.0.2 + profiles: + - default + network devices: + foo: + ipaddr: 10.10.0.2 + n03: + ipmi: + ipaddr: 10.20.0.3 + profiles: + - default + network devices: + foo: + ipaddr: 10.10.0.3 `}, } for _, tt := range tests { db, err = node.TestNew([]byte(nodes_yml)) assert.NoError(t, err) + fmt.Printf("Running test: %s\n", tt.name) t.Run(tt.name, func(t *testing.T) { + // store global NodeConf as the NodeConf.NetDevs["default"] will be delete + // in the main.go + tmpConfNet := NodeConf.NetDevs["default"] + //tmpConf := NodeConf + //tmpKernel := NodeConf.Kernel baseCmd.SetArgs(tt.args) + for key, val := range tt.flags { + baseCmd.Flags().Set(key, val) + } err = baseCmd.Execute() if (err != nil) != tt.wantErr { t.Errorf("Got unwanted error: %s", err) @@ -83,7 +216,15 @@ nodes: } if buf.String() != tt.stdout { t.Errorf("Got wrong output, got:'%s'\nwant:'%s'", buf.String(), tt.stdout) + return } + NodeConf.NetDevs["default"] = tmpConfNet + for key, _ := range tt.flags { + baseCmd.Flags().Set(key, "hark") + } + + //NodeConf = tmpConf + //NodeConf.Kernel = node.NewConf().Kernel }) } } diff --git a/internal/pkg/node/methods.go b/internal/pkg/node/methods.go index 4ee8deca..9f89255a 100644 --- a/internal/pkg/node/methods.go +++ b/internal/pkg/node/methods.go @@ -382,3 +382,30 @@ func GetByName(node interface{}, name string) (string, error) { myEntry := entryField.Interface().(Entry) return myEntry.Get(), nil } + +/* +Check if the Netdev is empty, so has no values set +*/ +func (dev *NetDevs) Empty() bool { + if dev == nil { + return true + } + varType := reflect.TypeOf(*dev) + varVal := reflect.ValueOf(*dev) + if varVal.IsZero() { + return true + } + for i := 0; i < varType.NumField(); i++ { + if varType.Field(i).Type.Kind() == reflect.String && !varVal.Field(i).IsZero() { + val := varVal.Field(i).Interface().(string) + if val != "" { + return false + } + } else if varType.Field(i).Type == reflect.TypeOf(map[string]string{}) { + if len(varVal.Field(i).Interface().(map[string]string)) != 0 { + return false + } + } + } + return true +} diff --git a/internal/pkg/node/methods_test.go b/internal/pkg/node/methods_test.go new file mode 100644 index 00000000..d17f1529 --- /dev/null +++ b/internal/pkg/node/methods_test.go @@ -0,0 +1,32 @@ +package node + +import "testing" + +func Test_Empty(t *testing.T) { + var netdev NetDevs + var netdevPtr *NetDevs + + t.Run("test for empty", func(t *testing.T) { + if netdev.Empty() != true { + t.Errorf("netdev must be empty") + } + }) + t.Run("test for non empty", func(t *testing.T) { + netdev.Device = "foo" + if netdev.Empty() == true { + t.Errorf("netdev must be non empty") + } + }) + t.Run("test for nil pointer", func(t *testing.T) { + if netdevPtr.Empty() != true { + t.Errorf("netdev must be empty") + } + }) + t.Run("test for pointer assigned", func(t *testing.T) { + netdev.Ipaddr = "10.10.10.1" + netdevPtr = &netdev + if netdevPtr.Empty() == true { + t.Errorf("netdev must be empty") + } + }) +} From 2410a8194c12ea69b0b4fbf4017c8a10eb606275 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Wed, 8 Mar 2023 11:56:46 +0100 Subject: [PATCH 16/25] wwctl node add without global variables Signed-off-by: Christian Goll --- internal/app/wwctl/node/add/main.go | 50 +++++++------ internal/app/wwctl/node/add/main_test.go | 93 +++++++++++------------- internal/app/wwctl/node/add/root.go | 30 ++++---- 3 files changed, 84 insertions(+), 89 deletions(-) diff --git a/internal/app/wwctl/node/add/main.go b/internal/app/wwctl/node/add/main.go index dc24299e..d0d919a8 100644 --- a/internal/app/wwctl/node/add/main.go +++ b/internal/app/wwctl/node/add/main.go @@ -1,8 +1,6 @@ package add import ( - "os" - "gopkg.in/yaml.v2" apinode "github.com/hpcng/warewulf/internal/pkg/api/node" @@ -11,27 +9,33 @@ import ( "github.com/spf13/cobra" ) -func CobraRunE(cmd *cobra.Command, args []string) error { - // remove the default network as all network values are assigned - // to this network - if _, ok := NodeConf.NetDevs["default"]; ok && NetName != "" { - netDev := *NodeConf.NetDevs["default"] - NodeConf.NetDevs[NetName] = &netDev - delete(NodeConf.NetDevs, "default") - } else { - if NodeConf.NetDevs["default"].Empty() { - delete(NodeConf.NetDevs, "default") +/* +RunE needs a function of type func(*cobraCommand,[]string) err, but +in order to avoid global variables which mess up testing a function of +the required type is returned +*/ +func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + // remove the default network as all network values are assigned + // to this network + if _, ok := vars.nodeConf.NetDevs["default"]; ok && vars.netName != "" { + netDev := *vars.nodeConf.NetDevs["default"] + vars.nodeConf.NetDevs[vars.netName] = &netDev + delete(vars.nodeConf.NetDevs, "default") + } else { + if vars.nodeConf.NetDevs["default"].Empty() { + delete(vars.nodeConf.NetDevs, "default") + } } + buffer, err := yaml.Marshal(vars.nodeConf) + if err != nil { + wwlog.Error("Cant marshall nodeInfo", err) + return err + } + set := wwapiv1.NodeAddParameter{ + NodeConfYaml: string(buffer[:]), + NodeNames: args, + } + return apinode.NodeAdd(&set) } - buffer, err := yaml.Marshal(NodeConf) - if err != nil { - wwlog.Error("Cant marshall nodeInfo", err) - os.Exit(1) - } - set := wwapiv1.NodeAddParameter{ - NodeConfYaml: string(buffer[:]), - NodeNames: args, - } - - return apinode.NodeAdd(&set) } diff --git a/internal/app/wwctl/node/add/main_test.go b/internal/app/wwctl/node/add/main_test.go index d6935790..98c8e47e 100644 --- a/internal/app/wwctl/node/add/main_test.go +++ b/internal/app/wwctl/node/add/main_test.go @@ -2,7 +2,6 @@ package add import ( "bytes" - "fmt" "testing" "github.com/hpcng/warewulf/internal/pkg/node" @@ -12,29 +11,12 @@ import ( ) 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 - flags map[string]string }{ {name: "single node add", args: []string{"n01"}, @@ -48,8 +30,7 @@ nodes: - default `}, {name: "single node add, profile foo", - args: []string{"n01"}, - flags: map[string]string{"profile": "foo"}, + args: []string{"--profile=foo", "n01"}, wantErr: false, stdout: "", outDb: `WW_INTERNAL: 43 @@ -60,8 +41,7 @@ nodes: - foo `}, {name: "single node add with Kernel args", - args: []string{"n01"}, - flags: map[string]string{"kernelargs": "foo"}, + args: []string{"--kernelargs=foo", "n01"}, wantErr: false, stdout: "", outDb: `WW_INTERNAL: 43 @@ -87,9 +67,8 @@ nodes: profiles: - default `}, - {name: "single node with ipaddr", - args: []string{"n01"}, - flags: map[string]string{"ipaddr": "10.10.0.1"}, + {name: "single node with ipaddr6", + args: []string{"--ipaddr6=fdaa::1", "n01"}, wantErr: false, stdout: "", outDb: `WW_INTERNAL: 43 @@ -100,11 +79,24 @@ nodes: - default network devices: default: - ipaddr: 10.10.0.1 + ip6addr: fdaa::1 +`}, + {name: "single node with ipaddr", + args: []string{"--ipaddr=10.0.0.1", "n01"}, + wantErr: false, + stdout: "", + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: + n01: + profiles: + - default + network devices: + default: + ipaddr: 10.0.0.1 `}, {name: "three nodes with ipaddr", - args: []string{"n[01-02,03]"}, - flags: map[string]string{"ipaddr": "10.10.0.1"}, + args: []string{"--ipaddr=10.10.0.1", "n[01-02,03]"}, wantErr: false, stdout: "", outDb: `WW_INTERNAL: 43 @@ -130,8 +122,7 @@ nodes: ipaddr: 10.10.0.3 `}, {name: "three nodes with ipaddr different network", - args: []string{"n[01-03]"}, - flags: map[string]string{"ipaddr": "10.10.0.1", "netname": "foo"}, + args: []string{"--ipaddr=10.10.0.1", "--netname=foo", "n[01-03]"}, wantErr: false, stdout: "", outDb: `WW_INTERNAL: 43 @@ -157,8 +148,7 @@ nodes: ipaddr: 10.10.0.3 `}, {name: "three nodes with ipaddr different network, with ipmiaddr", - args: []string{"n[01-03]"}, - flags: map[string]string{"ipaddr": "10.10.0.1", "netname": "foo", "ipmiaddr": "10.20.0.1"}, + args: []string{"--ipaddr=10.10.0.1", "--netname=foo", "--ipmiaddr=10.20.0.1", "n[01-03]"}, wantErr: false, stdout: "", outDb: `WW_INTERNAL: 43 @@ -190,41 +180,42 @@ nodes: ipaddr: 10.10.0.3 `}, } + 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() for _, tt := range tests { db, err = node.TestNew([]byte(nodes_yml)) assert.NoError(t, err) - fmt.Printf("Running test: %s\n", tt.name) + t.Logf("Running test: %s\n", tt.name) t.Run(tt.name, func(t *testing.T) { - // store global NodeConf as the NodeConf.NetDevs["default"] will be delete - // in the main.go - tmpConfNet := NodeConf.NetDevs["default"] - //tmpConf := NodeConf - //tmpKernel := NodeConf.Kernel + baseCmd := GetCommand() baseCmd.SetArgs(tt.args) - for key, val := range tt.flags { - baseCmd.Flags().Set(key, val) - } + buf := new(bytes.Buffer) + baseCmd.SetOut(buf) + baseCmd.SetErr(buf) err = baseCmd.Execute() if (err != nil) != tt.wantErr { t.Errorf("Got unwanted error: %s", err) - return + t.FailNow() } dump := string(db.DBDump()) if dump != tt.outDb { t.Errorf("DB dump is wrong, got:'%s'\nwant:'%s'", dump, tt.outDb) - return + t.FailNow() } if buf.String() != tt.stdout { t.Errorf("Got wrong output, got:'%s'\nwant:'%s'", buf.String(), tt.stdout) - return + t.FailNow() } - NodeConf.NetDevs["default"] = tmpConfNet - for key, _ := range tt.flags { - baseCmd.Flags().Set(key, "hark") - } - - //NodeConf = tmpConf - //NodeConf.Kernel = node.NewConf().Kernel }) } } diff --git a/internal/app/wwctl/node/add/root.go b/internal/app/wwctl/node/add/root.go index 76297f9a..c498187f 100644 --- a/internal/app/wwctl/node/add/root.go +++ b/internal/app/wwctl/node/add/root.go @@ -10,23 +10,26 @@ import ( "github.com/spf13/cobra" ) -var ( - baseCmd = &cobra.Command{ +// Holds the variables which are needed in CobraRunE +type variables struct { + netName string + nodeConf node.NodeConf +} + +// Returns the newly created command +func GetCommand() *cobra.Command { + vars := variables{} + vars.nodeConf = node.NewConf() + baseCmd := &cobra.Command{ DisableFlagsInUseLine: true, Use: "add [OPTIONS] NODENAME", Short: "Add new node to Warewulf", Long: "This command will add a new node named NODENAME to Warewulf.", - RunE: CobraRunE, + RunE: CobraRunE(&vars), Args: cobra.MinimumNArgs(1), } - NodeConf node.NodeConf - NetName string -) - -func init() { - NodeConf = node.NewConf() - NodeConf.CreateFlags(baseCmd, []string{}) - baseCmd.PersistentFlags().StringVar(&NetName, "netname", "", "Set network name for network options") + vars.nodeConf.CreateFlags(baseCmd, []string{"tagdel", "nettagdel", "ipmitagdel"}) + baseCmd.PersistentFlags().StringVar(&vars.netName, "netname", "", "Set network name for network options") // register the command line completions if err := baseCmd.RegisterFlagCompletionFunc("container", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { @@ -65,9 +68,6 @@ func init() { log.Println(err) } -} - -// GetRootCommand returns the root cobra.Command for the application. -func GetCommand() *cobra.Command { + // GetRootCommand returns the root cobra.Command for the application. return baseCmd } From 9c77d7b50c3820b3361fd20ac645d9086b41bbd4 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Wed, 22 Mar 2023 15:30:33 -0600 Subject: [PATCH 17/25] Fixes to build process * Fixed a misspelling of genconfig in Makefile * Updated completions path in warewulf.spec.in Signed-off-by: Jonathon Anderson --- Makefile | 2 +- warewulf.spec.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index b0b19c88..36e36d98 100644 --- a/Makefile +++ b/Makefile @@ -203,7 +203,7 @@ files: all install -m 0644 include/firewalld/warewulf.xml $(DESTDIR)$(FIREWALLDDIR) install -m 0644 include/systemd/warewulfd.service $(DESTDIR)$(SYSTEMDDIR) install -m 0644 LICENSE.md $(DESTDIR)$(WWDOCDIR) - ./wwctl genconf completions > $(DESTDIR)$(BASHCOMPDIR)/wwctl + ./wwctl genconfig completions > $(DESTDIR)$(BASHCOMPDIR)/wwctl cp man_pages/*.1* $(DESTDIR)$(MANDIR)/man1/ cp man_pages/*.5* $(DESTDIR)$(MANDIR)/man5/ install -m 0644 staticfiles/README-ipxe.md $(DESTDIR)$(WWDATADIR)/ipxe diff --git a/warewulf.spec.in b/warewulf.spec.in index 5f57ffb2..fd0c32c0 100644 --- a/warewulf.spec.in +++ b/warewulf.spec.in @@ -118,7 +118,7 @@ getent group %{wwgroup} >/dev/null || groupadd -r %{wwgroup} %dir %{_sysconfdir}/warewulf %config(noreplace) %{_sysconfdir}/warewulf/* %config(noreplace) %attr(0640,-,-) %{_sysconfdir}/warewulf/nodes.conf -%{_sysconfdir}/bash_completion.d/warewulf +%{_sysconfdir}/bash_completion.d/wwctl %dir %{_sharedstatedir}/warewulf %{_sharedstatedir}/warewulf/chroots From 70361425ff0a5bc6bdaa167b2a542a81062093ff Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Thu, 2 Mar 2023 11:01:11 +0100 Subject: [PATCH 18/25] generate man pages via sub command Signed-off-by: Christian Goll --- internal/app/wwctl/genconf/root.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/app/wwctl/genconf/root.go b/internal/app/wwctl/genconf/root.go index 1a979be4..b208f4ae 100644 --- a/internal/app/wwctl/genconf/root.go +++ b/internal/app/wwctl/genconf/root.go @@ -6,6 +6,7 @@ import ( "github.com/hpcng/warewulf/internal/app/wwctl/genconf/man" "github.com/hpcng/warewulf/internal/app/wwctl/genconf/reference" "github.com/hpcng/warewulf/internal/app/wwctl/genconf/warewulfconf" + "github.com/hpcng/warewulf/internal/app/wwctl/genconf/man" "github.com/spf13/cobra" ) From 09d14039b097d3eb92805483398451851bef082f Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Mon, 6 Mar 2023 15:51:34 +0100 Subject: [PATCH 19/25] added Changelog Signed-off-by: Christian Goll --- CHANGELOG.md | 13 +++++++++++++ internal/app/wwctl/genconf/root.go | 1 - 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf73cb33..4fd7d4b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `/etc/hosts` - Added experimental dnsmasq support. +- new subcommand `wwctl genconf` is available with following subcommands: + * `completions` which will create the files used for bash-completion. Also + fish an zsh completions can be generated + * `defaults` which will generate a valid `defaults.conf` + * `man` which will generate the man pages in the specified directory + * `reference` which will generate a reference documentation for the wwctl commands + * `warwulfconf print` which will print the used `warewulf.conf`. If there is no valid + `warewulf.conf` a valid configuration is provided, prefilled with default values + and an IP configuration derived from the network configuration of the host +- All paths can now be configured in `warewulf.conf`, check the paths section of of + `wwctl --emptyconf genconfig warewulfconf print` for the available paths. +- Added experimental dnsmasq support. + - new subcommand `wwctl genconf` is available with following subcommands: * `completions` which will create the files used for bash-completion. Also fish an zsh completions can be generated diff --git a/internal/app/wwctl/genconf/root.go b/internal/app/wwctl/genconf/root.go index b208f4ae..1a979be4 100644 --- a/internal/app/wwctl/genconf/root.go +++ b/internal/app/wwctl/genconf/root.go @@ -6,7 +6,6 @@ import ( "github.com/hpcng/warewulf/internal/app/wwctl/genconf/man" "github.com/hpcng/warewulf/internal/app/wwctl/genconf/reference" "github.com/hpcng/warewulf/internal/app/wwctl/genconf/warewulfconf" - "github.com/hpcng/warewulf/internal/app/wwctl/genconf/man" "github.com/spf13/cobra" ) From 13bc378a6be16d32d6189cd7785a88ac1498b3a5 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Thu, 2 Feb 2023 17:06:03 +0100 Subject: [PATCH 20/25] Refactor the CreateFlags command * added the additional type flag, which can be bool,IP * UNDEF is recognized for the bool flag * 0.0.0.0 must be used as UNDEF for IP flag Signed-off-by: Christian Goll --- internal/app/wwctl/node/add/main.go | 5 + internal/app/wwctl/node/add/root.go | 4 +- internal/app/wwctl/node/set/main.go | 4 + internal/app/wwctl/node/set/root.go | 5 +- internal/app/wwctl/profile/add/main.go | 4 + internal/app/wwctl/profile/add/root.go | 3 +- internal/app/wwctl/profile/set/root.go | 5 +- internal/pkg/node/datastructure.go | 22 +-- internal/pkg/node/flags.go | 200 +++++++++++++++++++++++++ internal/pkg/node/methods.go | 8 +- internal/pkg/node/transformers.go | 97 ------------ 11 files changed, 241 insertions(+), 116 deletions(-) create mode 100644 internal/pkg/node/flags.go diff --git a/internal/app/wwctl/node/add/main.go b/internal/app/wwctl/node/add/main.go index d0d919a8..a3dec5aa 100644 --- a/internal/app/wwctl/node/add/main.go +++ b/internal/app/wwctl/node/add/main.go @@ -9,6 +9,7 @@ import ( "github.com/spf13/cobra" ) +<<<<<<< HEAD /* RunE needs a function of type func(*cobraCommand,[]string) err, but in order to avoid global variables which mess up testing a function of @@ -16,6 +17,10 @@ the required type is returned */ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { + // run converters for different types + for _, c := range Converters { + c() + } // remove the default network as all network values are assigned // to this network if _, ok := vars.nodeConf.NetDevs["default"]; ok && vars.netName != "" { diff --git a/internal/app/wwctl/node/add/root.go b/internal/app/wwctl/node/add/root.go index c498187f..f36241ac 100644 --- a/internal/app/wwctl/node/add/root.go +++ b/internal/app/wwctl/node/add/root.go @@ -14,6 +14,7 @@ import ( type variables struct { netName string nodeConf node.NodeConf + converters []func() } // Returns the newly created command @@ -28,9 +29,8 @@ func GetCommand() *cobra.Command { RunE: CobraRunE(&vars), Args: cobra.MinimumNArgs(1), } - vars.nodeConf.CreateFlags(baseCmd, []string{"tagdel", "nettagdel", "ipmitagdel"}) + vars.converters = vars.nodeConf.CreateFlags(baseCmd, []string{"tagdel", "nettagdel", "ipmitagdel"}) baseCmd.PersistentFlags().StringVar(&vars.netName, "netname", "", "Set network name for network options") - // register the command line completions if err := baseCmd.RegisterFlagCompletionFunc("container", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { list, _ := container.ListSources() diff --git a/internal/app/wwctl/node/set/main.go b/internal/app/wwctl/node/set/main.go index 63dad15f..e95e77a2 100644 --- a/internal/app/wwctl/node/set/main.go +++ b/internal/app/wwctl/node/set/main.go @@ -13,6 +13,10 @@ import ( ) func CobraRunE(cmd *cobra.Command, args []string) (err error) { + // run converters for different types + for _, c := range Converters { + c() + } // remove the default network as the all network values are assigned // to this network if NetName != "default" { diff --git a/internal/app/wwctl/node/set/root.go b/internal/app/wwctl/node/set/root.go index 0e5e81cf..e9cc48e9 100644 --- a/internal/app/wwctl/node/set/root.go +++ b/internal/app/wwctl/node/set/root.go @@ -15,7 +15,7 @@ var ( DisableFlagsInUseLine: true, Use: "set [OPTIONS] PATTERN [PATTERN ...]", Short: "Configure node properties", - Long: "This command sets configuration properties for nodes matching PATTERN.\n\nNote: use the string 'UNSET' to remove a configuration", + Long: "This command sets configuration properties for nodes matching PATTERN.\n\nNote: use the string 'UNSET'/'0.0.0.0' to remove a configuration", Args: cobra.MinimumNArgs(0), RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { @@ -38,11 +38,12 @@ var ( SetYes bool SetForce bool NodeConf node.NodeConf + Converters []func() ) func init() { NodeConf = node.NewConf() - NodeConf.CreateFlags(baseCmd, []string{}) + Converters = NodeConf.CreateFlags(baseCmd, []string{}) baseCmd.PersistentFlags().StringVarP(&SetNetDevDel, "netdel", "D", "", "Delete the node's network device") baseCmd.PersistentFlags().StringVar(&NetName, "netname", "default", "Set network name for network options") baseCmd.PersistentFlags().BoolVarP(&SetNodeAll, "all", "a", false, "Set all nodes") diff --git a/internal/app/wwctl/profile/add/main.go b/internal/app/wwctl/profile/add/main.go index 0260f232..e509e9d9 100644 --- a/internal/app/wwctl/profile/add/main.go +++ b/internal/app/wwctl/profile/add/main.go @@ -14,6 +14,10 @@ import ( ) func CobraRunE(cmd *cobra.Command, args []string) (err error) { + // run converters for different types + for _, c := range Converters { + c() + } // remove the default network as the all network values are assigned // to this network if NetName != "" { diff --git a/internal/app/wwctl/profile/add/root.go b/internal/app/wwctl/profile/add/root.go index e3ad6d24..2cfec736 100644 --- a/internal/app/wwctl/profile/add/root.go +++ b/internal/app/wwctl/profile/add/root.go @@ -25,12 +25,13 @@ var ( SetForce bool NetName string ProfileConf node.NodeConf + Converters []func() ) // GetRootCommand returns the root cobra.Command for the application. func GetCommand() *cobra.Command { ProfileConf = node.NewConf() - ProfileConf.CreateFlags(baseCmd, + Converters = ProfileConf.CreateFlags(baseCmd, []string{"ipaddr", "ipaddr6", "ipmiaddr", "profile"}) baseCmd.PersistentFlags().StringVar(&NetName, "netname", "", "Set network name for network options") // register the command line completions diff --git a/internal/app/wwctl/profile/set/root.go b/internal/app/wwctl/profile/set/root.go index f95cbb6c..c9af0599 100644 --- a/internal/app/wwctl/profile/set/root.go +++ b/internal/app/wwctl/profile/set/root.go @@ -15,7 +15,7 @@ var ( Use: "set [OPTIONS] [PROFILE ...]", Short: "Configure node profile properties", Long: "This command sets configuration properties for the node PROFILE(s).\n\n" + - "Note: use the string 'UNSET' to remove a configuration", + "Note: use the string 'UNSET'/'0.0.0.0' to remove a configuration", Args: cobra.MinimumNArgs(0), RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { @@ -38,11 +38,12 @@ var ( SetForce bool NetName string ProfileConf node.NodeConf + Converters []func() ) func init() { ProfileConf = node.NewConf() - ProfileConf.CreateFlags(baseCmd, + Converters = ProfileConf.CreateFlags(baseCmd, []string{"ipaddr", "ipaddr6", "ipmiaddr", "profile"}) baseCmd.PersistentFlags().StringVar(&NetName, "netname", "default", "Set network name for network options") baseCmd.PersistentFlags().StringVarP(&SetNetDevDel, "netdel", "D", "", "Delete the node's network device") diff --git a/internal/pkg/node/datastructure.go b/internal/pkg/node/datastructure.go index cecd1db5..a4dab9c2 100644 --- a/internal/pkg/node/datastructure.go +++ b/internal/pkg/node/datastructure.go @@ -44,7 +44,7 @@ type NodeConf struct { Init string `yaml:"init,omitempty" lopt:"init" sopt:"i" comment:"Define the init process to boot the container"` Root string `yaml:"root,omitempty" lopt:"root" comment:"Define the rootfs" ` AssetKey string `yaml:"asset key,omitempty" lopt:"asset" comment:"Set the node's Asset tag (key)"` - Discoverable string `yaml:"discoverable,omitempty" lopt:"discoverable" comment:"Make discoverable in given network (yes/no)"` + Discoverable string `yaml:"discoverable,omitempty" lopt:"discoverable" sopt:"e" comment:"Make discoverable in given network (true/false)" type:"bool"` Profiles []string `yaml:"profiles,omitempty" lopt:"profile" sopt:"P" comment:"Set the node's profile members (comma separated)"` NetDevs map[string]*NetDevs `yaml:"network devices,omitempty"` Tags map[string]string `yaml:"tags,omitempty" lopt:"tagadd" comment:"base key"` @@ -56,12 +56,12 @@ type NodeConf struct { type IpmiConf struct { UserName string `yaml:"username,omitempty" lopt:"ipmiuser" comment:"Set the IPMI username"` Password string `yaml:"password,omitempty" lopt:"ipmipass" comment:"Set the IPMI password"` - Ipaddr string `yaml:"ipaddr,omitempty" lopt:"ipmiaddr" comment:"Set the IPMI IP address"` - Netmask string `yaml:"netmask,omitempty" lopt:"ipminetmask" comment:"Set the IPMI netmask"` + Ipaddr string `yaml:"ipaddr,omitempty" lopt:"ipmiaddr" comment:"Set the IPMI IP address" type:"IP"` + Netmask string `yaml:"netmask,omitempty" lopt:"ipminetmask" comment:"Set the IPMI netmask" type:"IP"` Port string `yaml:"port,omitempty" lopt:"ipmiport" comment:"Set the IPMI port"` - Gateway string `yaml:"gateway,omitempty" lopt:"ipmigateway" comment:"Set the IPMI gateway"` + Gateway string `yaml:"gateway,omitempty" lopt:"ipmigateway" comment:"Set the IPMI gateway" type:"IP"` Interface string `yaml:"interface,omitempty" lopt:"ipmiinterface" comment:"Set the node's IPMI interface (defaults: 'lan')"` - Write string `yaml:"write,omitempty" lopt:"ipmiwrite" comment:"Enable the write of impi configuration (yes/no)"` + Write string `yaml:"write,omitempty" lopt:"ipmiwrite" comment:"Enable the write of impi configuration (true/false)" type:"bool"` Tags map[string]string `yaml:"tags,omitempty" lopt:"ipmitagadd" comment:"add ipmitags"` TagsDel []string `yaml:"tagsdel,omitempty" lopt:"ipmitagdel" comment:"remove ipmitags"` // should not go to disk only to wire } @@ -73,16 +73,18 @@ type KernelConf struct { type NetDevs struct { Type string `yaml:"type,omitempty" lopt:"type" sopt:"T" comment:"Set device type of given network"` - OnBoot string `yaml:"onboot,omitempty" lopt:"onboot" comment:"Enable/disable network device (yes/no)"` + OnBoot string `yaml:"onboot,omitempty" lopt:"onboot" comment:"Enable/disable network device (true/false)" type:"bool"` Device string `yaml:"device,omitempty" lopt:"netdev" sopt:"N" comment:"Set the device for given network"` Hwaddr string `yaml:"hwaddr,omitempty" lopt:"hwaddr" sopt:"H" comment:"Set the device's HW address for given network"` - Ipaddr string `yaml:"ipaddr,omitempty" comment:"IPv4 address in given network" sopt:"I" lopt:"ipaddr"` + Ipaddr string `yaml:"ipaddr,omitempty" comment:"IPv4 address in given network" sopt:"I" lopt:"ipaddr" type:"IP"` IpCIDR string `yaml:"ipcidr,omitempty"` - Ipaddr6 string `yaml:"ip6addr,omitempty" lopt:"ipaddr6" comment:"IPv6 address"` + Ipaddr6 string `yaml:"ip6addr,omitempty" lopt:"ipaddr6" comment:"IPv6 address" type:"IP"` Prefix string `yaml:"prefix,omitempty"` - Netmask string `yaml:"netmask,omitempty" lopt:"netmask" sopt:"M" comment:"Set the networks netmask"` - Gateway string `yaml:"gateway,omitempty" lopt:"gateway" sopt:"G" comment:"Set the node's network device gateway"` + Netmask string `yaml:"netmask,omitempty" lopt:"netmask" sopt:"M" comment:"Set the networks netmask" type:"IP"` + Gateway string `yaml:"gateway,omitempty" lopt:"gateway" sopt:"G" comment:"Set the node's network device gateway" type:"IP"` MTU string `yaml:"mtu,omitempty" lopt:"mtu" comment:"Set the mtu"` + Primary string `yaml:"primary,omitempty" lopt:"primary" comment:"Enable/disable network device as primary (true/false)" type:"bool"` + Default string `yaml:"default,omitempty"` /* backward compatibility */ Tags map[string]string `yaml:"tags,omitempty" lopt:"nettagadd" comment:"network tags"` TagsDel []string `yaml:"tagsdel,omitempty" lopt:"nettagdel" comment:"delete network tags"` // should not go to disk only to wire } diff --git a/internal/pkg/node/flags.go b/internal/pkg/node/flags.go new file mode 100644 index 00000000..8f42f91d --- /dev/null +++ b/internal/pkg/node/flags.go @@ -0,0 +1,200 @@ +package node + +import ( + "net" + "os" + "reflect" + "strconv" + "strings" + + "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/spf13/cobra" +) + +/* +Create cmd line flags from the NodeConf fields +*/ +func (nodeConf *NodeConf) CreateFlags(baseCmd *cobra.Command, excludeList []string) (converters []func()) { + nodeInfoType := reflect.TypeOf(nodeConf) + nodeInfoVal := reflect.ValueOf(nodeConf) + // now iterate of every field + for i := 0; i < nodeInfoVal.Elem().NumField(); i++ { + if nodeInfoType.Elem().Field(i).Tag.Get("comment") != "" && + !util.InSlice(excludeList, nodeInfoType.Elem().Field(i).Tag.Get("lopt")) { + field := nodeInfoVal.Elem().Field(i) + converters = append(converters, createFlags(baseCmd, excludeList, nodeInfoType.Elem().Field(i), &field)...) + } else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Ptr { + nestType := reflect.TypeOf(nodeInfoVal.Elem().Field(i).Interface()) + nestVal := reflect.ValueOf(nodeInfoVal.Elem().Field(i).Interface()) + for j := 0; j < nestType.Elem().NumField(); j++ { + field := nestVal.Elem().Field(j) + converters = append(converters, createFlags(baseCmd, excludeList, nestType.Elem().Field(j), &field)...) + } + } else if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf(map[string]*NetDevs(nil)) { + netMap := nodeInfoVal.Elem().Field(i).Interface().(map[string]*NetDevs) + // add a default network so that it can hold values + key := "default" + if len(netMap) == 0 { + netMap[key] = new(NetDevs) + } else { + for keyIt := range netMap { + key = keyIt + break + } + } + netType := reflect.TypeOf(netMap[key]) + netVal := reflect.ValueOf(netMap[key]) + for j := 0; j < netType.Elem().NumField(); j++ { + field := netVal.Elem().Field(j) + converters = append(converters, createFlags(baseCmd, excludeList, netType.Elem().Field(j), &field)...) + } + } + } + return converters +} + +/* +Helper function to create the different PerisitantFlags() for different types. +*/ +func createFlags(baseCmd *cobra.Command, excludeList []string, + myType reflect.StructField, myVal *reflect.Value) (converters []func()) { + if myType.Tag.Get("lopt") != "" { + if myType.Type.Kind() == reflect.String { + ptr := myVal.Addr().Interface().(*string) + switch myType.Tag.Get("type") { + case "uint": + defaultConv, _ := strconv.ParseUint(myType.Tag.Get("default"), 10, 32) + var valueRaw uint + converters = append(converters, func() { *ptr = strconv.FormatUint(uint64(valueRaw), 10) }) + if myType.Tag.Get("sopt") != "" { + baseCmd.PersistentFlags().UintVarP(&valueRaw, + myType.Tag.Get("lopt"), + myType.Tag.Get("sopt"), + uint(defaultConv), + myType.Tag.Get("comment")) + } else { + baseCmd.PersistentFlags().UintVar(&valueRaw, + myType.Tag.Get("lopt"), + uint(defaultConv), + myType.Tag.Get("comment")) + } + case "bool": + /* + Can't use the bool var from pflag as we need the UNSET verbs to be passwd correctly + */ + converters = append(converters, func() { + if !util.InSlice(GetUnsetVerbs(), *ptr) && *ptr != "" { + if strings.ToLower(*ptr) != "yes" { + *ptr = "true" + return + } + if strings.ToLower(*ptr) != "no" { + *ptr = "false" + return + } + val, err := strconv.ParseBool(*ptr) + if err != nil { + wwlog.Error("commandline option %s needs to be bool", myType.Tag.Get("lopt")) + os.Exit(1) + } + *ptr = strconv.FormatBool(val) + } + }) + if myType.Tag.Get("sopt") != "" { + baseCmd.PersistentFlags().StringVarP(ptr, + myType.Tag.Get("lopt"), + myType.Tag.Get("sopt"), + "", + myType.Tag.Get("comment")) + } else { + baseCmd.PersistentFlags().StringVar(ptr, + myType.Tag.Get("lopt"), + "", + myType.Tag.Get("comment")) + } + baseCmd.PersistentFlags().Lookup(myType.Tag.Get("lopt")).NoOptDefVal = "true" + case "IP": + defaultConv := net.ParseIP(myType.Tag.Get("default")) + var valueRaw net.IP + converters = append(converters, func() { + if valueRaw != nil { + *ptr = valueRaw.String() + } + }) + if myType.Tag.Get("sopt") != "" { + baseCmd.PersistentFlags().IPVarP(&valueRaw, + myType.Tag.Get("lopt"), + myType.Tag.Get("sopt"), + defaultConv, + myType.Tag.Get("comment")) + } else { + baseCmd.PersistentFlags().IPVar(&valueRaw, + myType.Tag.Get("lopt"), + defaultConv, + myType.Tag.Get("comment")) + } + case "IPMask": + defaultConv := net.ParseIP(myType.Tag.Get("default")).DefaultMask() + var valueRaw net.IPMask + converters = append(converters, func() { *ptr = valueRaw.String() }) + if myType.Tag.Get("sopt") != "" { + baseCmd.PersistentFlags().IPMaskVarP(&valueRaw, + myType.Tag.Get("lopt"), + myType.Tag.Get("sopt"), + defaultConv, + myType.Tag.Get("comment")) + } else { + baseCmd.PersistentFlags().IPMaskVar(&valueRaw, + myType.Tag.Get("lopt"), + defaultConv, + myType.Tag.Get("comment")) + } + default: + if myType.Tag.Get("sopt") != "" { + baseCmd.PersistentFlags().StringVarP(ptr, + myType.Tag.Get("lopt"), + myType.Tag.Get("sopt"), + myType.Tag.Get("default"), + myType.Tag.Get("comment")) + } else { + baseCmd.PersistentFlags().StringVar(ptr, + myType.Tag.Get("lopt"), + myType.Tag.Get("default"), + myType.Tag.Get("comment")) + } + } + } else if myType.Type == reflect.TypeOf([]string{}) { + ptr := myVal.Addr().Interface().(*[]string) + if myType.Tag.Get("sopt") != "" { + baseCmd.PersistentFlags().StringSliceVarP(ptr, + myType.Tag.Get("lopt"), + myType.Tag.Get("sopt"), + []string{myType.Tag.Get("default")}, + myType.Tag.Get("comment")) + } else if !util.InSlice(excludeList, myType.Tag.Get("lopt")) { + baseCmd.PersistentFlags().StringSliceVar(ptr, + myType.Tag.Get("lopt"), + []string{myType.Tag.Get("default")}, + myType.Tag.Get("comment")) + + } + } else if myType.Type == reflect.TypeOf(map[string]string{}) { + ptr := myVal.Addr().Interface().(*map[string]string) + if myType.Tag.Get("sopt") != "" { + baseCmd.PersistentFlags().StringToStringVarP(ptr, + myType.Tag.Get("lopt"), + myType.Tag.Get("sopt"), + map[string]string{}, // empty default! + myType.Tag.Get("comment")) + } else if !util.InSlice(excludeList, myType.Tag.Get("lopt")) { + baseCmd.PersistentFlags().StringToStringVar(ptr, + myType.Tag.Get("lopt"), + map[string]string{}, // empty default! + myType.Tag.Get("comment")) + + } + } + } + return converters +} diff --git a/internal/pkg/node/methods.go b/internal/pkg/node/methods.go index 9f89255a..f9deb492 100644 --- a/internal/pkg/node/methods.go +++ b/internal/pkg/node/methods.go @@ -10,6 +10,10 @@ import ( "github.com/hpcng/warewulf/internal/pkg/wwlog" ) +func GetUnsetVerbs() []string { + return []string{"UNSET", "DELETE", "UNDEF", "undef", "--", "nil", "0.0.0.0"} +} + /********** * * Filters @@ -77,7 +81,7 @@ func (ent *Entry) Set(val string) { return } - if val == "UNDEF" || val == "DELETE" || val == "UNSET" || val == "--" || val == "nil" { + if util.InSlice(GetUnsetVerbs(), val) { wwlog.Debug("Removing value for %v", *ent) ent.value = []string{""} } else { @@ -102,7 +106,7 @@ func (ent *Entry) SetSlice(val []string) { } else if len(val) == 1 && val[0] == "" { // check also for an "empty" slice return } - if val[0] == "UNDEF" || val[0] == "DELETE" || val[0] == "UNSET" || val[0] == "--" { + if util.InSlice(GetUnsetVerbs(), val[0]) { ent.value = []string{} } else { ent.value = val diff --git a/internal/pkg/node/transformers.go b/internal/pkg/node/transformers.go index 75960b25..4acaa859 100644 --- a/internal/pkg/node/transformers.go +++ b/internal/pkg/node/transformers.go @@ -6,7 +6,6 @@ import ( "github.com/hpcng/warewulf/internal/pkg/util" "github.com/hpcng/warewulf/internal/pkg/wwlog" - "github.com/spf13/cobra" ) /* @@ -194,102 +193,6 @@ func (nodeConf *NodeConf) getterFrom(nodeInfo NodeInfo, } } -/* -Create cmd line flags from the NodeConf fields -*/ -func (nodeConf *NodeConf) CreateFlags(baseCmd *cobra.Command, excludeList []string) { - nodeInfoType := reflect.TypeOf(nodeConf) - nodeInfoVal := reflect.ValueOf(nodeConf) - // now iterate of every field - for i := 0; i < nodeInfoVal.Elem().NumField(); i++ { - if nodeInfoType.Elem().Field(i).Tag.Get("comment") != "" && - !util.InSlice(excludeList, nodeInfoType.Elem().Field(i).Tag.Get("lopt")) { - field := nodeInfoVal.Elem().Field(i) - createFlags(baseCmd, excludeList, nodeInfoType.Elem().Field(i), &field) - } else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Ptr { - nestType := reflect.TypeOf(nodeInfoVal.Elem().Field(i).Interface()) - nestVal := reflect.ValueOf(nodeInfoVal.Elem().Field(i).Interface()) - for j := 0; j < nestType.Elem().NumField(); j++ { - field := nestVal.Elem().Field(j) - createFlags(baseCmd, excludeList, nestType.Elem().Field(j), &field) - } - } else if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf(map[string]*NetDevs(nil)) { - netMap := nodeInfoVal.Elem().Field(i).Interface().(map[string]*NetDevs) - // add a default network so that it can hold values - key := "default" - if len(netMap) == 0 { - netMap[key] = new(NetDevs) - } else { - for keyIt := range netMap { - key = keyIt - break - } - } - netType := reflect.TypeOf(netMap[key]) - netVal := reflect.ValueOf(netMap[key]) - for j := 0; j < netType.Elem().NumField(); j++ { - field := netVal.Elem().Field(j) - createFlags(baseCmd, excludeList, netType.Elem().Field(j), &field) - } - } - } -} - -/* -Helper function to create the different PerisitantFlags() for different types. -*/ -func createFlags(baseCmd *cobra.Command, excludeList []string, - myType reflect.StructField, myVal *reflect.Value) { - if myType.Tag.Get("lopt") != "" { - if myType.Type.Kind() == reflect.String { - ptr := myVal.Addr().Interface().(*string) - if myType.Tag.Get("sopt") != "" { - baseCmd.PersistentFlags().StringVarP(ptr, - myType.Tag.Get("lopt"), - myType.Tag.Get("sopt"), - myType.Tag.Get("default"), - myType.Tag.Get("comment")) - } else if !util.InSlice(excludeList, myType.Tag.Get("lopt")) { - baseCmd.PersistentFlags().StringVar(ptr, - myType.Tag.Get("lopt"), - myType.Tag.Get("default"), - myType.Tag.Get("comment")) - - } - } else if myType.Type == reflect.TypeOf([]string{}) { - ptr := myVal.Addr().Interface().(*[]string) - if myType.Tag.Get("sopt") != "" { - baseCmd.PersistentFlags().StringSliceVarP(ptr, - myType.Tag.Get("lopt"), - myType.Tag.Get("sopt"), - []string{myType.Tag.Get("default")}, - myType.Tag.Get("comment")) - } else if !util.InSlice(excludeList, myType.Tag.Get("lopt")) { - baseCmd.PersistentFlags().StringSliceVar(ptr, - myType.Tag.Get("lopt"), - []string{myType.Tag.Get("default")}, - myType.Tag.Get("comment")) - - } - } else if myType.Type == reflect.TypeOf(map[string]string{}) { - ptr := myVal.Addr().Interface().(*map[string]string) - if myType.Tag.Get("sopt") != "" { - baseCmd.PersistentFlags().StringToStringVarP(ptr, - myType.Tag.Get("lopt"), - myType.Tag.Get("sopt"), - map[string]string{}, // empty default! - myType.Tag.Get("comment")) - } else if !util.InSlice(excludeList, myType.Tag.Get("lopt")) { - baseCmd.PersistentFlags().StringToStringVar(ptr, - myType.Tag.Get("lopt"), - map[string]string{}, // empty default! - myType.Tag.Get("comment")) - - } - } - } -} - /* Populates all fields of NodeInfo with Set from the values of NodeConf. From c14dd3c6d122b5a9ea1acade479c881db9c8e291 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Mon, 6 Feb 2023 17:00:24 +0100 Subject: [PATCH 21/25] check the yaml direclty after unmarshalling --- internal/app/wwctl/node/edit/main.go | 44 +++++++++----- internal/app/wwctl/profile/edit/main.go | 47 +++++++++------ internal/pkg/api/node/edit.go | 4 ++ internal/pkg/api/node/node.go | 12 ++++ internal/pkg/node/checkconf.go | 76 +++++++++++++++++++++++++ internal/pkg/node/constructors.go | 20 +++++++ internal/pkg/node/flags.go | 4 +- 7 files changed, 176 insertions(+), 31 deletions(-) create mode 100644 internal/pkg/node/checkconf.go diff --git a/internal/app/wwctl/node/edit/main.go b/internal/app/wwctl/node/edit/main.go index 0683f931..95d7cb8b 100644 --- a/internal/app/wwctl/node/edit/main.go +++ b/internal/app/wwctl/node/edit/main.go @@ -77,17 +77,38 @@ func CobraRunE(cmd *cobra.Command, args []string) error { // ignore error as only may occurs under strange circumstances buffer, _ := io.ReadAll(file) err = yaml.Unmarshal(buffer, modifiedNodeMap) - if err == nil { - nodeList := make([]string, len(nodeMap)) - i := 0 - for key := range nodeMap { - nodeList[i] = key - i++ - } - yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to modify %d nodes", len(modifiedNodeMap))) - if !yes { + if err != nil { + yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Got following error on parsing: %s, Retry", err)) + if yes { + continue + } else { break } + } + var checkErrors []error + for nodeName, node := range modifiedNodeMap { + err = node.Check() + if err != nil { + checkErrors = append(checkErrors, fmt.Errorf("node: %s parse error: %s", nodeName, err)) + } + } + if len(checkErrors) != 0 { + yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Got following error on parsing: %s, Retry", checkErrors)) + if yes { + continue + } else { + break + } + } + + nodeList := make([]string, len(nodeMap)) + i := 0 + for key := range nodeMap { + nodeList[i] = key + i++ + } + yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to modify %d nodes", len(modifiedNodeMap))) + if yes { err = apinode.NodeDelete(&wwapiv1.NodeDeleteParameter{NodeNames: nodeList, Force: true}) if err != nil { wwlog.Verbose("Problem deleting nodes before modification %s") @@ -99,11 +120,6 @@ func CobraRunE(cmd *cobra.Command, args []string) error { os.Exit(1) } break - } else { - yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Got following error on parsing: %s, Retry", err)) - if !yes { - break - } } } else { break diff --git a/internal/app/wwctl/profile/edit/main.go b/internal/app/wwctl/profile/edit/main.go index 97d5a3ad..9ec085a7 100644 --- a/internal/app/wwctl/profile/edit/main.go +++ b/internal/app/wwctl/profile/edit/main.go @@ -71,24 +71,44 @@ func CobraRunE(cmd *cobra.Command, args []string) error { sum2 := hex.EncodeToString(hasher.Sum(nil)) wwlog.Debug("Hashes are before %s and after %s\n", sum1, sum2) if sum1 != sum2 { - wwlog.Debug("Nodes were modified") + wwlog.Debug("Profiles were modified") modifiedProfileMap := make(map[string]*node.NodeConf) _, _ = file.Seek(0, 0) // ignore error as only may occurs under strange circumstances buffer, _ := io.ReadAll(file) err = yaml.Unmarshal(buffer, modifiedProfileMap) - if err == nil { - nodeList := make([]string, len(profileMap)) - i := 0 - for key := range profileMap { - nodeList[i] = key - i++ - } - yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to modify %d nodes", len(modifiedProfileMap))) - if !yes { + if err != nil { + yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Got following error on parsing: %s, Retry", err)) + if yes { + continue + } else { break } - err = apiprofile.ProfileDelete(&wwapiv1.NodeDeleteParameter{NodeNames: nodeList, Force: true}) + } + var checkErrors []error + for nodeName, node := range modifiedProfileMap { + err = node.Check() + if err != nil { + checkErrors = append(checkErrors, fmt.Errorf("profile: %s parse error: %s", nodeName, err)) + } + } + if len(checkErrors) != 0 { + yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Got following error on parsing: %s, Retry", checkErrors)) + if yes { + continue + } else { + break + } + } + pList := make([]string, len(profileMap)) + i := 0 + for key := range profileMap { + pList[i] = key + i++ + } + yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to modify %d nodes", len(modifiedProfileMap))) + if yes { + err = apiprofile.ProfileDelete(&wwapiv1.NodeDeleteParameter{NodeNames: pList, Force: true}) if err != nil { wwlog.Verbose("Problem deleting nodes before modification %s") } @@ -99,11 +119,6 @@ func CobraRunE(cmd *cobra.Command, args []string) error { os.Exit(1) } break - } else { - yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Got following error on parsing: %s, Retry", err)) - if !yes { - break - } } } else { break diff --git a/internal/pkg/api/node/edit.go b/internal/pkg/api/node/edit.go index 6cfa8768..f6653d0f 100644 --- a/internal/pkg/api/node/edit.go +++ b/internal/pkg/api/node/edit.go @@ -60,6 +60,10 @@ func NodeAddFromYaml(nodeList *wwapiv1.NodeYaml) (err error) { return errors.Wrap(err, "Could not unmarshall Yaml: %s\n") } for nodeName, node := range nodeMap { + err = node.Check() + if err != nil { + return errors.Errorf("error on node %s: %s", nodeName, err) + } nodeDB.Nodes[nodeName] = node } err = nodeDB.Persist() diff --git a/internal/pkg/api/node/node.go b/internal/pkg/api/node/node.go index 69ad38ff..b6a7e75d 100644 --- a/internal/pkg/api/node/node.go +++ b/internal/pkg/api/node/node.go @@ -50,6 +50,12 @@ func NodeAdd(nap *wwapiv1.NodeAddParameter) (err error) { // only key } // setting node from the received yaml + err = nodeConf.Check() + if err != nil { + err = fmt.Errorf("error on check of node %s: %s", n.Id.Get(), err) + return + + } n.SetFrom(&nodeConf) if netName != "" && nodeConf.NetDevs[netName].Ipaddr != "" { // if more nodes are added increment IPv4 address @@ -236,6 +242,12 @@ func NodeSetParameterCheck(set *wwapiv1.NodeSetParameter, console bool) (nodeDB wwlog.Error(fmt.Sprintf("%v", err.Error())) return } + err = nodeConf.Check() + if err != nil { + err = fmt.Errorf("error on check of node %s: %s", n.Id.Get(), err) + return + + } n.SetFrom(&nodeConf) if set.NetdevDelete != "" { if _, ok := n.NetDevs[set.NetdevDelete]; !ok { diff --git a/internal/pkg/node/checkconf.go b/internal/pkg/node/checkconf.go new file mode 100644 index 00000000..1fa052b5 --- /dev/null +++ b/internal/pkg/node/checkconf.go @@ -0,0 +1,76 @@ +package node + +import ( + "fmt" + "net/netip" + "reflect" + "strconv" + "strings" +) + +/* +Checks if for NodeConf all values can be parsed according to their type. +*/ +func (nodeConf *NodeConf) Check() (err error) { + nodeInfoType := reflect.TypeOf(nodeConf) + nodeInfoVal := reflect.ValueOf(nodeConf) + // now iterate of every field + for i := 0; i < nodeInfoVal.Elem().NumField(); i++ { + //wwlog.Debug("checking field: %s type: %s", nodeInfoType.Elem().Field(i).Name, nodeInfoVal.Elem().Field(i).Type()) + if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.String { + err = checker(nodeInfoVal.Elem().Field(i).Interface().(string), nodeInfoType.Elem().Field(i).Tag.Get("type")) + if err != nil { + return fmt.Errorf("field: %s value:%s err: %s", nodeInfoType.Elem().Field(i).Name, nodeInfoVal.Elem().Field(i).String(), err) + } + } else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Ptr && !nodeInfoVal.Elem().Field(i).IsNil() { + nestType := reflect.TypeOf(nodeInfoVal.Elem().Field(i).Interface()) + nestVal := reflect.ValueOf(nodeInfoVal.Elem().Field(i).Interface()) + for j := 0; j < nestType.Elem().NumField(); j++ { + if nestType.Elem().Field(j).Type.Kind() == reflect.String { + //wwlog.Debug("checking field: %s type: %s", nestType.Elem().Field(j).Name, nestType.Elem().Field(j).Tag.Get("type")) + err = checker(nestVal.Elem().Field(j).Interface().(string), nestType.Elem().Field(j).Tag.Get("type")) + if err != nil { + return fmt.Errorf("field: %s value:%s err: %s", nestType.Elem().Field(j).Name, nestVal.Elem().Field(j).String(), err) + } + } + } + } else if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf(map[string]*NetDevs(nil)) { + netMap := nodeInfoVal.Elem().Field(i).Interface().(map[string]*NetDevs) + for _, val := range netMap { + netType := reflect.TypeOf(val) + netVal := reflect.ValueOf(val) + for j := 0; j < netType.Elem().NumField(); j++ { + err = checker(netVal.Elem().Field(j).String(), netType.Elem().Field(j).Tag.Get("type")) + if err != nil { + return fmt.Errorf("field: %s value:%s err: %s", netType.Elem().Field(j).Name, netVal.Elem().Field(j).String(), err) + } + } + } + } + } + return nil +} + +func checker(value string, valType string) (err error) { + if valType == "" || value == "" { + return nil + } + //wwlog.Debug("checker: %s is %s", value, valType) + switch valType { + case "": + return nil + case "bool": + if strings.ToLower(value) == "yes" { + return nil + } + if strings.ToLower(value) == "no" { + return nil + } + _, err = strconv.ParseBool(value) + return err + case "IP": + _, err = netip.ParseAddr(value) + return err + } + return nil +} diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index 10ee86ad..06fc4c05 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -2,6 +2,7 @@ package node import ( "errors" + "fmt" "os" "path" "sort" @@ -71,6 +72,21 @@ func New() (NodeYaml, error) { if err != nil { return ret, err } + wwlog.Debug("Checking nodes for types") + for nodeName, node := range ret.Nodes { + err = node.Check() + if err != nil { + wwlog.Warn("node: %s parsing error: %s", nodeName, err) + return ret, err + } + } + for profileName, profile := range ret.NodeProfiles { + err = profile.Check() + if err != nil { + wwlog.Warn("node: %s parsing error: %s", profileName, err) + return ret, err + } + } wwlog.Debug("Returning node object") cachedDB = ret @@ -165,6 +181,10 @@ func (config *NodeYaml) FindAllNodes() ([]NodeInfo, error) { node.Tags[keyname] = key delete(node.Keys, keyname) } + err = node.Check() + if err != nil { + return nil, fmt.Errorf("node: %s check error: %s", nodename, err) + } n.SetFrom(node) // only now the netdevs start to exist so that default values can be set for _, netdev := range n.NetDevs { diff --git a/internal/pkg/node/flags.go b/internal/pkg/node/flags.go index 8f42f91d..6a12b914 100644 --- a/internal/pkg/node/flags.go +++ b/internal/pkg/node/flags.go @@ -13,7 +13,9 @@ import ( ) /* -Create cmd line flags from the NodeConf fields +Create cmd line flags from the NodeConf fields. Returns a []func() where every function +must be called, as the commandline parser returns e.g. netip.IP objects which must be parsedf +back to strings. */ func (nodeConf *NodeConf) CreateFlags(baseCmd *cobra.Command, excludeList []string) (converters []func()) { nodeInfoType := reflect.TypeOf(nodeConf) From b0602133ec8200ffb2fa064b553a490d6d76bf4a Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Tue, 7 Feb 2023 09:16:55 +0100 Subject: [PATCH 22/25] use netParseIP and ParseMAC check also MAC addresses and common formating Signed-off-by: Christian Goll reformat ipaddr for test coverage fixed processing of default values updated transformer tests correct behavior for tags add tests for tags new tests for primary network Signed-off-by: Christian Goll --- internal/app/wwctl/node/add/main.go | 11 +- internal/app/wwctl/node/add/root.go | 2 +- internal/app/wwctl/node/set/main.go | 4 +- internal/app/wwctl/node/set/root.go | 2 +- internal/app/wwctl/profile/add/main.go | 4 +- internal/app/wwctl/profile/add/root.go | 2 +- internal/app/wwctl/profile/set/root.go | 2 +- internal/pkg/node/checkconf.go | 47 ++++-- internal/pkg/node/constructors.go | 3 +- internal/pkg/node/datastructure.go | 4 +- internal/pkg/node/flags.go | 71 ++++++--- internal/pkg/node/transformer_test.go | 208 +++++++++++++++++++++++-- internal/pkg/node/transformers.go | 40 +++-- internal/pkg/node/util.go | 5 +- internal/pkg/node/util_test.go | 6 +- 15 files changed, 335 insertions(+), 76 deletions(-) diff --git a/internal/app/wwctl/node/add/main.go b/internal/app/wwctl/node/add/main.go index a3dec5aa..266e6690 100644 --- a/internal/app/wwctl/node/add/main.go +++ b/internal/app/wwctl/node/add/main.go @@ -9,7 +9,6 @@ import ( "github.com/spf13/cobra" ) -<<<<<<< HEAD /* RunE needs a function of type func(*cobraCommand,[]string) err, but in order to avoid global variables which mess up testing a function of @@ -17,10 +16,12 @@ the required type is returned */ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { - // run converters for different types - for _, c := range Converters { - c() - } + // run converters for different types + for _, c := range vars.converters { + if err := c(); err != nil { + return err + } + } // remove the default network as all network values are assigned // to this network if _, ok := vars.nodeConf.NetDevs["default"]; ok && vars.netName != "" { diff --git a/internal/app/wwctl/node/add/root.go b/internal/app/wwctl/node/add/root.go index f36241ac..83a0b172 100644 --- a/internal/app/wwctl/node/add/root.go +++ b/internal/app/wwctl/node/add/root.go @@ -14,7 +14,7 @@ import ( type variables struct { netName string nodeConf node.NodeConf - converters []func() + converters []func() error } // Returns the newly created command diff --git a/internal/app/wwctl/node/set/main.go b/internal/app/wwctl/node/set/main.go index e95e77a2..4bc44f21 100644 --- a/internal/app/wwctl/node/set/main.go +++ b/internal/app/wwctl/node/set/main.go @@ -15,7 +15,9 @@ import ( func CobraRunE(cmd *cobra.Command, args []string) (err error) { // run converters for different types for _, c := range Converters { - c() + if err := c(); err != nil { + return err + } } // remove the default network as the all network values are assigned // to this network diff --git a/internal/app/wwctl/node/set/root.go b/internal/app/wwctl/node/set/root.go index e9cc48e9..a5ab4df4 100644 --- a/internal/app/wwctl/node/set/root.go +++ b/internal/app/wwctl/node/set/root.go @@ -38,7 +38,7 @@ var ( SetYes bool SetForce bool NodeConf node.NodeConf - Converters []func() + Converters []func() error ) func init() { diff --git a/internal/app/wwctl/profile/add/main.go b/internal/app/wwctl/profile/add/main.go index e509e9d9..dc23d8fc 100644 --- a/internal/app/wwctl/profile/add/main.go +++ b/internal/app/wwctl/profile/add/main.go @@ -16,7 +16,9 @@ import ( func CobraRunE(cmd *cobra.Command, args []string) (err error) { // run converters for different types for _, c := range Converters { - c() + if err := c(); err != nil { + return err + } } // remove the default network as the all network values are assigned // to this network diff --git a/internal/app/wwctl/profile/add/root.go b/internal/app/wwctl/profile/add/root.go index 2cfec736..6edf403d 100644 --- a/internal/app/wwctl/profile/add/root.go +++ b/internal/app/wwctl/profile/add/root.go @@ -25,7 +25,7 @@ var ( SetForce bool NetName string ProfileConf node.NodeConf - Converters []func() + Converters []func() error ) // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/app/wwctl/profile/set/root.go b/internal/app/wwctl/profile/set/root.go index c9af0599..9fc70f86 100644 --- a/internal/app/wwctl/profile/set/root.go +++ b/internal/app/wwctl/profile/set/root.go @@ -38,7 +38,7 @@ var ( SetForce bool NetName string ProfileConf node.NodeConf - Converters []func() + Converters []func() error ) func init() { diff --git a/internal/pkg/node/checkconf.go b/internal/pkg/node/checkconf.go index 1fa052b5..a595a1c5 100644 --- a/internal/pkg/node/checkconf.go +++ b/internal/pkg/node/checkconf.go @@ -2,7 +2,7 @@ package node import ( "fmt" - "net/netip" + "net" "reflect" "strconv" "strings" @@ -18,9 +18,11 @@ func (nodeConf *NodeConf) Check() (err error) { for i := 0; i < nodeInfoVal.Elem().NumField(); i++ { //wwlog.Debug("checking field: %s type: %s", nodeInfoType.Elem().Field(i).Name, nodeInfoVal.Elem().Field(i).Type()) if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.String { - err = checker(nodeInfoVal.Elem().Field(i).Interface().(string), nodeInfoType.Elem().Field(i).Tag.Get("type")) + newFmt, err := checker(nodeInfoVal.Elem().Field(i).Interface().(string), nodeInfoType.Elem().Field(i).Tag.Get("type")) if err != nil { return fmt.Errorf("field: %s value:%s err: %s", nodeInfoType.Elem().Field(i).Name, nodeInfoVal.Elem().Field(i).String(), err) + } else if newFmt != "" { + nodeInfoVal.Elem().Field(i).SetString(newFmt) } } else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Ptr && !nodeInfoVal.Elem().Field(i).IsNil() { nestType := reflect.TypeOf(nodeInfoVal.Elem().Field(i).Interface()) @@ -28,9 +30,11 @@ func (nodeConf *NodeConf) Check() (err error) { for j := 0; j < nestType.Elem().NumField(); j++ { if nestType.Elem().Field(j).Type.Kind() == reflect.String { //wwlog.Debug("checking field: %s type: %s", nestType.Elem().Field(j).Name, nestType.Elem().Field(j).Tag.Get("type")) - err = checker(nestVal.Elem().Field(j).Interface().(string), nestType.Elem().Field(j).Tag.Get("type")) + newFmt, err := checker(nestVal.Elem().Field(j).Interface().(string), nestType.Elem().Field(j).Tag.Get("type")) if err != nil { return fmt.Errorf("field: %s value:%s err: %s", nestType.Elem().Field(j).Name, nestVal.Elem().Field(j).String(), err) + } else if newFmt != "" { + nestVal.Elem().Field(j).SetString(newFmt) } } } @@ -40,9 +44,11 @@ func (nodeConf *NodeConf) Check() (err error) { netType := reflect.TypeOf(val) netVal := reflect.ValueOf(val) for j := 0; j < netType.Elem().NumField(); j++ { - err = checker(netVal.Elem().Field(j).String(), netType.Elem().Field(j).Tag.Get("type")) + newFmt, err := checker(netVal.Elem().Field(j).String(), netType.Elem().Field(j).Tag.Get("type")) if err != nil { return fmt.Errorf("field: %s value:%s err: %s", netType.Elem().Field(j).Name, netVal.Elem().Field(j).String(), err) + } else if newFmt != "" { + netVal.Elem().Field(j).SetString(newFmt) } } } @@ -51,26 +57,39 @@ func (nodeConf *NodeConf) Check() (err error) { return nil } -func checker(value string, valType string) (err error) { +func checker(value string, valType string) (niceValue string, err error) { if valType == "" || value == "" { - return nil + return "", nil } //wwlog.Debug("checker: %s is %s", value, valType) switch valType { case "": - return nil + return "", nil case "bool": if strings.ToLower(value) == "yes" { - return nil + return "true", nil } if strings.ToLower(value) == "no" { - return nil + return "false", nil } - _, err = strconv.ParseBool(value) - return err + myBool, err := strconv.ParseBool(value) + return strconv.FormatBool(myBool), err case "IP": - _, err = netip.ParseAddr(value) - return err + if addr := net.ParseIP(value); addr == nil { + return "", fmt.Errorf("%s can't be parsed to ip address", value) + } else { + return addr.String(), nil + } + case "MAC": + if mac, err := net.ParseMAC(value); err != nil { + return "", fmt.Errorf("%s can't be parsed to MAC address: %s", value, err) + } else { + return mac.String(), nil + } + case "uint": + if _, err := strconv.ParseUint(value, 10, 64); err != nil { + return "", fmt.Errorf("%s is not a uint: %s", value, err) + } } - return nil + return "", nil } diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index 06fc4c05..578cb094 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -135,12 +135,13 @@ func (config *NodeYaml) FindAllNodes() ([]NodeInfo, error) { defData, err := os.ReadFile(DefaultConfig) if err != nil { wwlog.Verbose("Couldn't read DefaultConfig :%s\n", err) + wwlog.Verbose("Using building defaults") + defData = []byte(FallBackConf) } wwlog.Debug("Unmarshalling default config\n") err = yaml.Unmarshal(defData, &defConf) if err != nil { wwlog.Verbose("Couldn't unmarshall defaults from file :%s\n", err) - wwlog.Verbose("Using building defaults") err = yaml.Unmarshal([]byte(FallBackConf), &defConf) if err != nil { wwlog.Warn("Could not get any defaults") diff --git a/internal/pkg/node/datastructure.go b/internal/pkg/node/datastructure.go index a4dab9c2..2c27c289 100644 --- a/internal/pkg/node/datastructure.go +++ b/internal/pkg/node/datastructure.go @@ -75,14 +75,14 @@ type NetDevs struct { Type string `yaml:"type,omitempty" lopt:"type" sopt:"T" comment:"Set device type of given network"` OnBoot string `yaml:"onboot,omitempty" lopt:"onboot" comment:"Enable/disable network device (true/false)" type:"bool"` Device string `yaml:"device,omitempty" lopt:"netdev" sopt:"N" comment:"Set the device for given network"` - Hwaddr string `yaml:"hwaddr,omitempty" lopt:"hwaddr" sopt:"H" comment:"Set the device's HW address for given network"` + Hwaddr string `yaml:"hwaddr,omitempty" lopt:"hwaddr" sopt:"H" comment:"Set the device's HW address for given network" type:"MAC"` Ipaddr string `yaml:"ipaddr,omitempty" comment:"IPv4 address in given network" sopt:"I" lopt:"ipaddr" type:"IP"` IpCIDR string `yaml:"ipcidr,omitempty"` Ipaddr6 string `yaml:"ip6addr,omitempty" lopt:"ipaddr6" comment:"IPv6 address" type:"IP"` Prefix string `yaml:"prefix,omitempty"` Netmask string `yaml:"netmask,omitempty" lopt:"netmask" sopt:"M" comment:"Set the networks netmask" type:"IP"` Gateway string `yaml:"gateway,omitempty" lopt:"gateway" sopt:"G" comment:"Set the node's network device gateway" type:"IP"` - MTU string `yaml:"mtu,omitempty" lopt:"mtu" comment:"Set the mtu"` + MTU string `yaml:"mtu,omitempty" lopt:"mtu" comment:"Set the mtu" type:"uint"` Primary string `yaml:"primary,omitempty" lopt:"primary" comment:"Enable/disable network device as primary (true/false)" type:"bool"` Default string `yaml:"default,omitempty"` /* backward compatibility */ Tags map[string]string `yaml:"tags,omitempty" lopt:"nettagadd" comment:"network tags"` diff --git a/internal/pkg/node/flags.go b/internal/pkg/node/flags.go index 6a12b914..4804f7c9 100644 --- a/internal/pkg/node/flags.go +++ b/internal/pkg/node/flags.go @@ -1,14 +1,13 @@ package node import ( + "fmt" "net" - "os" "reflect" "strconv" "strings" "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/spf13/cobra" ) @@ -17,7 +16,7 @@ Create cmd line flags from the NodeConf fields. Returns a []func() where every f must be called, as the commandline parser returns e.g. netip.IP objects which must be parsedf back to strings. */ -func (nodeConf *NodeConf) CreateFlags(baseCmd *cobra.Command, excludeList []string) (converters []func()) { +func (nodeConf *NodeConf) CreateFlags(baseCmd *cobra.Command, excludeList []string) (converters []func() error) { nodeInfoType := reflect.TypeOf(nodeConf) nodeInfoVal := reflect.ValueOf(nodeConf) // now iterate of every field @@ -60,48 +59,54 @@ func (nodeConf *NodeConf) CreateFlags(baseCmd *cobra.Command, excludeList []stri Helper function to create the different PerisitantFlags() for different types. */ func createFlags(baseCmd *cobra.Command, excludeList []string, - myType reflect.StructField, myVal *reflect.Value) (converters []func()) { + myType reflect.StructField, myVal *reflect.Value) (converters []func() error) { if myType.Tag.Get("lopt") != "" { if myType.Type.Kind() == reflect.String { ptr := myVal.Addr().Interface().(*string) switch myType.Tag.Get("type") { case "uint": - defaultConv, _ := strconv.ParseUint(myType.Tag.Get("default"), 10, 32) - var valueRaw uint - converters = append(converters, func() { *ptr = strconv.FormatUint(uint64(valueRaw), 10) }) + converters = append(converters, func() error { + if !util.InSlice(GetUnsetVerbs(), *ptr) && *ptr != "" { + _, err := strconv.ParseUint(myType.Tag.Get(*ptr), 10, 32) + if err != nil { + return err + } + } + return nil + }) if myType.Tag.Get("sopt") != "" { - baseCmd.PersistentFlags().UintVarP(&valueRaw, + baseCmd.PersistentFlags().StringVarP(ptr, myType.Tag.Get("lopt"), myType.Tag.Get("sopt"), - uint(defaultConv), + myType.Tag.Get("default"), myType.Tag.Get("comment")) } else { - baseCmd.PersistentFlags().UintVar(&valueRaw, + baseCmd.PersistentFlags().StringVar(ptr, myType.Tag.Get("lopt"), - uint(defaultConv), + myType.Tag.Get("default"), myType.Tag.Get("comment")) } case "bool": /* Can't use the bool var from pflag as we need the UNSET verbs to be passwd correctly */ - converters = append(converters, func() { + converters = append(converters, func() error { if !util.InSlice(GetUnsetVerbs(), *ptr) && *ptr != "" { if strings.ToLower(*ptr) != "yes" { *ptr = "true" - return + return nil } if strings.ToLower(*ptr) != "no" { *ptr = "false" - return + return nil } val, err := strconv.ParseBool(*ptr) if err != nil { - wwlog.Error("commandline option %s needs to be bool", myType.Tag.Get("lopt")) - os.Exit(1) + return fmt.Errorf("commandline option %s needs to be bool", myType.Tag.Get("lopt")) } *ptr = strconv.FormatBool(val) } + return nil }) if myType.Tag.Get("sopt") != "" { baseCmd.PersistentFlags().StringVarP(ptr, @@ -119,10 +124,12 @@ func createFlags(baseCmd *cobra.Command, excludeList []string, case "IP": defaultConv := net.ParseIP(myType.Tag.Get("default")) var valueRaw net.IP - converters = append(converters, func() { + converters = append(converters, func() error { if valueRaw != nil { + // will always get a IP, not a string *ptr = valueRaw.String() } + return nil }) if myType.Tag.Get("sopt") != "" { baseCmd.PersistentFlags().IPVarP(&valueRaw, @@ -139,7 +146,14 @@ func createFlags(baseCmd *cobra.Command, excludeList []string, case "IPMask": defaultConv := net.ParseIP(myType.Tag.Get("default")).DefaultMask() var valueRaw net.IPMask - converters = append(converters, func() { *ptr = valueRaw.String() }) + converters = append(converters, func() error { + if valueRaw != nil { + *ptr = valueRaw.String() + return nil + } else { + return fmt.Errorf("could not parse %s to IP", valueRaw.String()) + } + }) if myType.Tag.Get("sopt") != "" { baseCmd.PersistentFlags().IPMaskVarP(&valueRaw, myType.Tag.Get("lopt"), @@ -152,6 +166,27 @@ func createFlags(baseCmd *cobra.Command, excludeList []string, defaultConv, myType.Tag.Get("comment")) } + case "MAC": + converters = append(converters, func() error { + myMac, err := net.ParseMAC(*ptr) + if err != nil { + return err + } + *ptr = myMac.String() + return nil + }) + if myType.Tag.Get("sopt") != "" { + baseCmd.PersistentFlags().StringVarP(ptr, + myType.Tag.Get("lopt"), + myType.Tag.Get("sopt"), + "", + myType.Tag.Get("comment")) + } else { + baseCmd.PersistentFlags().StringVar(ptr, + myType.Tag.Get("lopt"), + "", + myType.Tag.Get("comment")) + } default: if myType.Tag.Get("sopt") != "" { baseCmd.PersistentFlags().StringVarP(ptr, diff --git a/internal/pkg/node/transformer_test.go b/internal/pkg/node/transformer_test.go index 8742f2a3..c09c2843 100644 --- a/internal/pkg/node/transformer_test.go +++ b/internal/pkg/node/transformer_test.go @@ -1,37 +1,223 @@ package node import ( + "fmt" "reflect" + "strconv" "testing" + + "gopkg.in/yaml.v2" ) +func NewTransformerTestNode() NodeYaml { + var data = ` +nodeprofiles: + default: + comment: This profile is automatically included for each node + ipmi: + username: greg + profile2: + tags: + foo: foo profile2 + comment: Comment profile2 + ipmi: + tags: + foo: foo ipmi profile +nodes: + test_node1: + comment: Node Comment + profiles: + - default + network devices: + net0: + device: eth1 + discoverable: true + ipmi: + username: chris + tags: + baar: baar node1 + test_node2: + primary: net0 + profiles: + - default + - profile2 + network devices: + net0: + netmask: 1.1.1.1 + net1: + ipaddr: 1.2.3.4 + tags: + baar: baar node2 + test_node3: + profiles: + - profile2 + tags: + foo: foo node3 + foobaar: foobaar node3 + ipmi: + ipaddr: 1.1.1.1 + tags: + foo: foo ipmi node3 + ` + var ret NodeYaml + _ = yaml.Unmarshal([]byte(data), &ret) + return ret +} func Test_nodeYaml_SetFrom(t *testing.T) { - c, _ := NewTestNode() - singleNodeConf := c.Nodes["test_node"] - singleNodeInfo := NewInfo() - singleNodeInfo.SetFrom(singleNodeConf) - tests := []struct { + c := NewTransformerTestNode() + nodes, _ := c.FindAllNodes() + test_node1 := NewInfo() + test_node2 := NewInfo() + test_node3 := NewInfo() + for _, n := range nodes { + if n.Id.Get() == "test_node1" { + test_node1 = n + } + if n.Id.Get() == "test_node2" { + test_node2 = n + } + if n.Id.Get() == "test_node3" { + test_node3 = n + } + } + getByNametests := []struct { name string arg string want string wantErr bool }{ - {"Right comment", "Comment", "Node Comment", false}, - {"FieldName", "comment", "NodeComment", true}, + {"GetByName: FieldValue", "Comment", "Node Comment", false}, + {"GetByName: FieldName", "comment", "NodeComment", true}, } - for _, tt := range tests { + for _, tt := range getByNametests { t.Run(tt.name, func(t *testing.T) { - got, err := GetByName(&singleNodeInfo, tt.arg) + got, err := GetByName(&test_node1, tt.arg) if (err != nil) != tt.wantErr { t.Errorf("GetByName(%s,%s) error = %v, wantErr %v", - reflect.TypeOf(singleNodeConf), tt.arg, err, tt.wantErr) + reflect.TypeOf(test_node1), tt.arg, err, tt.wantErr) return } if (got != tt.want) != tt.wantErr { t.Errorf("GetByName(%s,%s) got = %v, want = %v", - reflect.TypeOf(singleNodeConf), tt.arg, got, tt.want) + reflect.TypeOf(test_node1), tt.arg, got, tt.want) return } }) } + t.Run("Get() comment", func(t *testing.T) { + comment := test_node1.Comment.Get() + if comment != "Node Comment" { + t.Errorf("Get() returned wrong comment: %s", comment) + } + }) + t.Run("Get() profile comment", func(t *testing.T) { + comment := test_node2.Comment.Get() + if comment != "Comment profile2" { + t.Errorf("Get() returned wrong comment: %s", comment) + } + }) + t.Run("Get() default ipxe", func(t *testing.T) { + value := test_node1.Ipxe.Get() + if value != "default" { + t.Errorf("Get() returned wrong ipxe template: %s", value) + } + }) + t.Run("GetSlice() default profile", func(t *testing.T) { + value := test_node1.Profiles.GetSlice()[0] + if value != "default" { + t.Errorf("GetSlice() returned wrong profile: %s", value) + } + }) + t.Run("Get() default kernel args", func(t *testing.T) { + value := test_node1.Kernel.Args.Get() + if value != "quiet crashkernel=no vga=791 net.naming-scheme=v238" { + t.Errorf("Get() returned wrong kernel args: %s", value) + } + }) + t.Run("Get() default network mask", func(t *testing.T) { + value := test_node1.NetDevs["net0"].Netmask.Get() + if value != "255.255.255.0" { + t.Errorf("Get() returned wrong default netmask: %s", value) + } + }) + t.Run("Get() default network mask", func(t *testing.T) { + value := test_node2.NetDevs["net0"].Netmask.Get() + if value != "1.1.1.1" { + t.Errorf("Get() returned wrong default netmask: %s", value) + } + }) + t.Run("GetB() primary for single network", func(t *testing.T) { + value := test_node1.NetDevs["net0"].Primary.GetB() + if !value { + t.Errorf("GetB() returned wrong: %s", strconv.FormatBool(value)) + } + }) + t.Run("GetB() for primary with two networks", func(t *testing.T) { + value := test_node2.NetDevs["net0"].Primary.GetB() + if !value { + t.Errorf("GetB() returned wrong: %s", strconv.FormatBool(value)) + } + }) + t.Run("GetB() for primary with two networks, get secondary network", func(t *testing.T) { + value := test_node2.NetDevs["net1"].Primary.GetB() + if value { + t.Errorf("GetB() returned wrong: %s", strconv.FormatBool(value)) + } + }) + t.Run("GetB() default discoverable", func(t *testing.T) { + value := test_node1.Discoverable.GetB() + if !value { + t.Errorf("GetB() returned wrong: %s", strconv.FormatBool(value)) + } + }) + t.Run("GetB() default discoverable", func(t *testing.T) { + value := test_node2.Discoverable.GetB() + if value { + t.Errorf("GetB() returned wrong: %s", strconv.FormatBool(value)) + } + }) + t.Run("Get() ipmi user from profile", func(t *testing.T) { + value := test_node2.Ipmi.UserName.Get() + if value != "greg" { + t.Errorf("Get() returned wrong ipmi username: %s", value) + } + }) + t.Run("Get() ipmi user from node", func(t *testing.T) { + value := test_node1.Ipmi.UserName.Get() + if value != "chris" { + t.Errorf("Get() returned wrong ipmi username: %s", value) + } + }) + t.Run("Get() tag foo from profile, node does not have this tag", func(t *testing.T) { + value := test_node2.Tags["foo"].Get() + if value != "foo profile2" { + t.Errorf("Get() returned wrong tag for foo: %s", value) + } + }) + t.Run("Get() tag baar from node, node tag map is not overwritten", func(t *testing.T) { + value := test_node2.Tags["baar"].Get() + if value != "baar node2" { + t.Errorf("Get() returned wrong tag for foo: %s", value) + } + }) + t.Run("Get() tag foo from node, tag present in profile", func(t *testing.T) { + value := test_node3.Tags["foo"].Get() + if value != "foo node3" { + t.Errorf("Get() returned wrong tag for foo: %s", value) + } + }) + t.Run("Get() tag foobaar from node", func(t *testing.T) { + value := test_node3.Tags["foobaar"].Get() + if value != "foobaar node3" { + t.Errorf("Get() returned wrong tag for foo: %s", value) + } + }) + t.Run("Get() ipmitag foo from profile, node does not have this tag", func(t *testing.T) { + fmt.Println("ipmi tags", test_node3.Ipmi.Tags) + fmt.Println(c.Nodes["test_node3"].Ipmi) + value := test_node3.Ipmi.Tags["foo"].Get() + if value != "foo ipmi node3" { + t.Errorf("Get() returned wrong tag for foo: %s", value) + } + }) } diff --git a/internal/pkg/node/transformers.go b/internal/pkg/node/transformers.go index 4acaa859..9eceac5d 100644 --- a/internal/pkg/node/transformers.go +++ b/internal/pkg/node/transformers.go @@ -266,24 +266,28 @@ func (node *NodeInfo) setterFrom(n *NodeConf, nameArg string, } } else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Ptr && !valField.IsZero() { nestedInfoType := reflect.TypeOf(nodeInfoVal.Elem().Field(i).Interface()) - netstedInfoVal := reflect.ValueOf(nodeInfoVal.Elem().Field(i).Interface()) + nestedInfoVal := reflect.ValueOf(nodeInfoVal.Elem().Field(i).Interface()) nestedConfVal := reflect.ValueOf(valField.Interface()) for j := 0; j < nestedInfoType.Elem().NumField(); j++ { nestedVal := nestedConfVal.Elem().FieldByName(nestedInfoType.Elem().Field(j).Name) if nestedVal.IsValid() { - if netstedInfoVal.Elem().Field(j).Type() == reflect.TypeOf(Entry{}) { - setter(netstedInfoVal.Elem().Field(j).Addr().Interface().(*Entry), nestedVal.String(), nameArg) - } else { + if nestedInfoVal.Elem().Field(j).Type() == reflect.TypeOf(Entry{}) { + setter(nestedInfoVal.Elem().Field(j).Addr().Interface().(*Entry), nestedVal.String(), nameArg) + } else if nestedInfoVal.Elem().Field(j).Type() == reflect.TypeOf(map[string](*Entry){}) { confMap := nestedVal.Interface().(map[string]string) - if netstedInfoVal.Elem().Field(j).IsNil() { - newMap := make(map[string]*Entry) - mapPtr := (netstedInfoVal.Elem().Field(j).Addr().Interface()).(*map[string](*Entry)) - *mapPtr = newMap + if nestedInfoVal.Elem().Field(j).IsNil() { + ptr := nestedInfoVal.Elem().Field(j).Addr().Interface().(*map[string](*Entry)) + *ptr = make(map[string]*Entry) } + tagMap := nestedInfoVal.Elem().Field(j).Interface().(map[string](*Entry)) for key, val := range confMap { - entr := new(Entry) - setter(entr, val, nameArg) - (netstedInfoVal.Elem().Field(j).Interface()).(map[string](*Entry))[key] = entr + if entr, ok := tagMap[key]; ok { + setter(entr, val, nameArg) + } else { + entr := new(Entry) + tagMap[key] = entr + setter(entr, val, nameArg) + } } } } @@ -291,9 +295,17 @@ func (node *NodeInfo) setterFrom(n *NodeConf, nameArg string, } else if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf(map[string](*Entry)(nil)) { confMap := valField.Interface().(map[string]string) for key, val := range confMap { - entr := new(Entry) - setter(entr, val, nameArg) - (nodeInfoVal.Elem().Field(i).Interface()).(map[string](*Entry))[key] = entr + tagMap := nodeInfoVal.Elem().Field(i).Interface().(map[string](*Entry)) + if nodeInfoVal.Elem().Field(i).IsNil() { + tagMap = make(map[string]*Entry) + } + if entr, ok := tagMap[key]; ok { + setter(entr, val, nameArg) + } else { + entr := new(Entry) + tagMap[key] = entr + setter(entr, val, nameArg) + } } } else if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf(map[string](*NetDevEntry)(nil)) { netValMap := valField.Interface().(map[string](*NetDevs)) diff --git a/internal/pkg/node/util.go b/internal/pkg/node/util.go index c641376c..8470a82b 100644 --- a/internal/pkg/node/util.go +++ b/internal/pkg/node/util.go @@ -27,10 +27,11 @@ func (config *NodeYaml) FindByHwaddr(hwa string) (NodeInfo, error) { } func (config *NodeYaml) FindByIpaddr(ipaddr string) (NodeInfo, error) { - if net.ParseIP(ipaddr) == nil { + if addr := net.ParseIP(ipaddr); addr == nil { return NodeInfo{}, errors.New("invalid IP:" + ipaddr) + } else { + ipaddr = addr.String() } - var ret NodeInfo n, _ := config.FindAllNodes() diff --git a/internal/pkg/node/util_test.go b/internal/pkg/node/util_test.go index bdce3bf7..2519105e 100644 --- a/internal/pkg/node/util_test.go +++ b/internal/pkg/node/util_test.go @@ -6,7 +6,7 @@ import ( "gopkg.in/yaml.v2" ) -func NewTestNode() (NodeYaml, error) { +func NewUtilTestNode() (NodeYaml, error) { var data = ` nodeprofiles: default: @@ -49,7 +49,7 @@ nodes: } func Test_nodeYaml_FindByHwaddr(t *testing.T) { - c, _ := NewTestNode() + c, _ := NewUtilTestNode() //type fields struct { // NodeProfiles map[string]*NodeConf // Nodes map[string]*NodeConf @@ -90,7 +90,7 @@ func Test_nodeYaml_FindByHwaddr(t *testing.T) { } func Test_nodeYaml_FindByIpaddr(t *testing.T) { - c, _ := NewTestNode() + c, _ := NewUtilTestNode() type args struct { ipaddr string } From 96644be62197e8bf2c85e71b27e8ba85cb39ef5a Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Fri, 24 Feb 2023 16:16:21 +0100 Subject: [PATCH 23/25] allow UNSET for IP addresses added more tests for GetFrom allow UNSET and handle empty hwaddr Signed-off-by: Christian Goll --- CHANGELOG.md | 4 + internal/app/wwctl/node/set/root.go | 2 +- internal/app/wwctl/profile/set/root.go | 2 +- internal/pkg/node/checkconf.go | 4 +- internal/pkg/node/flags.go | 29 +++--- internal/pkg/node/methods.go | 9 ++ internal/pkg/node/transformer_test.go | 135 ++++++++++++++++++++++++- 7 files changed, 166 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fd7d4b4..09ae5c9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 and an IP configuration derived from the network configuration of the host - All paths can now be configured in `warewulf.conf`, check the paths section of of `wwctl --emptyconf genconfig warewulfconf print` for the available paths. +- Added experimental dnsmasq support. +- Check for formal correct IP and MAC addresses for command line options and + when reading in the configurations + ## [4.4.0] 2023-01-18 ### Added diff --git a/internal/app/wwctl/node/set/root.go b/internal/app/wwctl/node/set/root.go index a5ab4df4..ac4c1f14 100644 --- a/internal/app/wwctl/node/set/root.go +++ b/internal/app/wwctl/node/set/root.go @@ -15,7 +15,7 @@ var ( DisableFlagsInUseLine: true, Use: "set [OPTIONS] PATTERN [PATTERN ...]", Short: "Configure node properties", - Long: "This command sets configuration properties for nodes matching PATTERN.\n\nNote: use the string 'UNSET'/'0.0.0.0' to remove a configuration", + Long: "This command sets configuration properties for nodes matching PATTERN.\n\nNote: use the string 'UNSET' to remove a configuration", Args: cobra.MinimumNArgs(0), RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { diff --git a/internal/app/wwctl/profile/set/root.go b/internal/app/wwctl/profile/set/root.go index 9fc70f86..0c1fd56b 100644 --- a/internal/app/wwctl/profile/set/root.go +++ b/internal/app/wwctl/profile/set/root.go @@ -15,7 +15,7 @@ var ( Use: "set [OPTIONS] [PROFILE ...]", Short: "Configure node profile properties", Long: "This command sets configuration properties for the node PROFILE(s).\n\n" + - "Note: use the string 'UNSET'/'0.0.0.0' to remove a configuration", + "Note: use the string 'UNSET' to remove a configuration", Args: cobra.MinimumNArgs(0), RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { diff --git a/internal/pkg/node/checkconf.go b/internal/pkg/node/checkconf.go index a595a1c5..a60acdf0 100644 --- a/internal/pkg/node/checkconf.go +++ b/internal/pkg/node/checkconf.go @@ -6,6 +6,8 @@ import ( "reflect" "strconv" "strings" + + "github.com/hpcng/warewulf/internal/pkg/util" ) /* @@ -58,7 +60,7 @@ func (nodeConf *NodeConf) Check() (err error) { } func checker(value string, valType string) (niceValue string, err error) { - if valType == "" || value == "" { + if valType == "" || value == "" || util.InSlice(GetUnsetVerbs(), value) { return "", nil } //wwlog.Debug("checker: %s is %s", value, valType) diff --git a/internal/pkg/node/flags.go b/internal/pkg/node/flags.go index 4804f7c9..5196135f 100644 --- a/internal/pkg/node/flags.go +++ b/internal/pkg/node/flags.go @@ -122,25 +122,26 @@ func createFlags(baseCmd *cobra.Command, excludeList []string, } baseCmd.PersistentFlags().Lookup(myType.Tag.Get("lopt")).NoOptDefVal = "true" case "IP": - defaultConv := net.ParseIP(myType.Tag.Get("default")) - var valueRaw net.IP converters = append(converters, func() error { - if valueRaw != nil { - // will always get a IP, not a string - *ptr = valueRaw.String() + if !util.InSlice(GetUnsetVerbs(), *ptr) && *ptr != "" { + ipval := net.ParseIP(*ptr) + if ipval == nil { + return fmt.Errorf("commandline option %s needs to be an IP address", myType.Tag.Get("lopt")) + } + *ptr = ipval.String() } return nil }) if myType.Tag.Get("sopt") != "" { - baseCmd.PersistentFlags().IPVarP(&valueRaw, + baseCmd.PersistentFlags().StringVarP(ptr, myType.Tag.Get("lopt"), myType.Tag.Get("sopt"), - defaultConv, + myType.Tag.Get("default"), myType.Tag.Get("comment")) } else { - baseCmd.PersistentFlags().IPVar(&valueRaw, + baseCmd.PersistentFlags().StringVar(ptr, myType.Tag.Get("lopt"), - defaultConv, + myType.Tag.Get("default"), myType.Tag.Get("comment")) } case "IPMask": @@ -168,11 +169,13 @@ func createFlags(baseCmd *cobra.Command, excludeList []string, } case "MAC": converters = append(converters, func() error { - myMac, err := net.ParseMAC(*ptr) - if err != nil { - return err + if !util.InSlice(GetUnsetVerbs(), *ptr) && *ptr != "" { + myMac, err := net.ParseMAC(*ptr) + if err != nil { + return err + } + *ptr = myMac.String() } - *ptr = myMac.String() return nil }) if myType.Tag.Get("sopt") != "" { diff --git a/internal/pkg/node/methods.go b/internal/pkg/node/methods.go index f9deb492..c3ed61e2 100644 --- a/internal/pkg/node/methods.go +++ b/internal/pkg/node/methods.go @@ -356,8 +356,10 @@ Create an empty node NodeConf */ func NewConf() (nodeconf NodeConf) { nodeconf.Ipmi = new(IpmiConf) + nodeconf.Ipmi.Tags = map[string]string{} nodeconf.Kernel = new(KernelConf) nodeconf.NetDevs = make(map[string]*NetDevs) + nodeconf.Tags = map[string]string{} return nodeconf } @@ -366,11 +368,18 @@ Create an empty node NodeInfo */ func NewInfo() (nodeInfo NodeInfo) { nodeInfo.Ipmi = new(IpmiEntry) + nodeInfo.Ipmi.Tags = map[string]*Entry{} nodeInfo.Kernel = new(KernelEntry) nodeInfo.NetDevs = make(map[string]*NetDevEntry) + nodeInfo.Tags = make(map[string]*Entry) return nodeInfo } +func NewNetDevEntry() (netdev NetDevEntry) { + netdev.Tags = make(map[string]*Entry) + return +} + /* Get a entry by its name */ diff --git a/internal/pkg/node/transformer_test.go b/internal/pkg/node/transformer_test.go index c09c2843..204b44a4 100644 --- a/internal/pkg/node/transformer_test.go +++ b/internal/pkg/node/transformer_test.go @@ -1,7 +1,6 @@ package node import ( - "fmt" "reflect" "strconv" "testing" @@ -69,6 +68,7 @@ func Test_nodeYaml_SetFrom(t *testing.T) { test_node1 := NewInfo() test_node2 := NewInfo() test_node3 := NewInfo() + test_node4 := NewInfo() for _, n := range nodes { if n.Id.Get() == "test_node1" { test_node1 = n @@ -213,11 +213,140 @@ func Test_nodeYaml_SetFrom(t *testing.T) { } }) t.Run("Get() ipmitag foo from profile, node does not have this tag", func(t *testing.T) { - fmt.Println("ipmi tags", test_node3.Ipmi.Tags) - fmt.Println(c.Nodes["test_node3"].Ipmi) value := test_node3.Ipmi.Tags["foo"].Get() if value != "foo ipmi node3" { t.Errorf("Get() returned wrong tag for foo: %s", value) } }) + t.Run("Set() comment foo for empty node", func(t *testing.T) { + test_node4.Comment.Set("foo") + nodeConf := NewConf() + nodeConf.GetFrom(test_node4) + ymlByte, _ := yaml.Marshal(nodeConf) + wanted := `comment: foo +kernel: {} +ipmi: {} +` + if !(wanted == string(ymlByte)) { + t.Errorf("Got wrong yml, wanted:\n'%s'\nGot:\n'%s'", wanted, string(ymlByte)) + } + // have to remove the comment for further tests, as vscode + // can test single functions + test_node4.Comment.Set("UNDEF") + nodeConf.GetFrom(test_node4) + nodeConf.Flatten() + ymlByte, _ = yaml.Marshal(nodeConf) + wanted = `{} +` + if string(ymlByte) != wanted { + t.Errorf("Couldn't unset comment:\n'%s'\nwanted:\n'%s'", string(ymlByte), wanted) + } + }) + + t.Run("Set() ipmiuser foo for flattened empty node", func(t *testing.T) { + test_node4.Ipmi.UserName.Set("foo") + nodeConf := NewConf() + nodeConf.GetFrom(test_node4) + nodeConf.Flatten() + ymlByte, _ := yaml.Marshal(nodeConf) + wanted := `ipmi: + username: foo +` + if !(wanted == string(ymlByte)) { + t.Errorf("Got wrong yml, wanted:\n'%s'\nGot:\n'%s'", wanted, string(ymlByte)) + } + test_node4.Ipmi.Tags["foo"] = &Entry{} + test_node4.Ipmi.Tags["foo"].Set("baar") + nodeConf.GetFrom(test_node4) + nodeConf.Flatten() + ymlByte, _ = yaml.Marshal(nodeConf) + wanted = `ipmi: + username: foo + tags: + foo: baar +` + if !(wanted == string(ymlByte)) { + t.Errorf("Got wrong yml, wanted:\n'%s'\nGot:\n'%s'", wanted, string(ymlByte)) + } + test_node4.Ipmi.UserName.Set("UNSET") + delete(test_node4.Ipmi.Tags, "foo") + }) + t.Run("Set() kernelargs foo for flattened empty node", func(t *testing.T) { + test_node4.Kernel.Args.Set("foo") + nodeConf := NewConf() + nodeConf.GetFrom(test_node4) + nodeConf.Flatten() + ymlByte, _ := yaml.Marshal(nodeConf) + wanted := `kernel: + args: foo +` + if !(wanted == string(ymlByte)) { + t.Errorf("Got wrong yml, wanted:\n'%s'\nGot:\n'%s'", wanted, string(ymlByte)) + } + test_node4.Kernel.Args.Set("--") + }) + t.Run("Set() tag foo to bar for flattened empty node", func(t *testing.T) { + test_node4.Tags["foo"] = &Entry{} + test_node4.Tags["foo"].Set("baar") + nodeConf := NewConf() + nodeConf.GetFrom(test_node4) + nodeConf.Flatten() + ymlByte, _ := yaml.Marshal(nodeConf) + wanted := `tags: + foo: baar +` + if !(wanted == string(ymlByte)) { + t.Errorf("Got wrong yml, wanted:\n'%s'\nGot:\n'%s'", wanted, string(ymlByte)) + } + delete(test_node4.Tags, "foo") + nodeConf.GetFrom(test_node4) + nodeConf.Flatten() + ymlByte, _ = yaml.Marshal(nodeConf) + wanted = `{} +` + if string(ymlByte) != wanted { + t.Errorf("Couldn't remove tag'%s'", string(ymlByte)) + } + + }) + t.Run("Set() netdev foo with device name baar for flattened empty node", func(t *testing.T) { + netdev := NewNetDevEntry() + test_node4.NetDevs["foo"] = &netdev + test_node4.NetDevs["foo"].Device.Set("baar") + nodeConf := NewConf() + nodeConf.GetFrom(test_node4) + nodeConf.Flatten() + ymlByte, _ := yaml.Marshal(nodeConf) + wanted := `network devices: + foo: + device: baar +` + if !(wanted == string(ymlByte)) { + t.Errorf("Got wrong yml, wanted:\n'%s'\nGot:\n'%s'", wanted, string(ymlByte)) + } + test_node4.NetDevs["foo"].Tags["netfoo"] = &Entry{} + test_node4.NetDevs["foo"].Tags["netfoo"].Set("netbaar") + nodeConf.GetFrom(test_node4) + nodeConf.Flatten() + wanted = `network devices: + foo: + device: baar + tags: + netfoo: netbaar +` + ymlByte, _ = yaml.Marshal(nodeConf) + if string(ymlByte) != wanted { + t.Errorf("Couldn set nettag: '%s' got: '%s'", wanted, string(ymlByte)) + } + + delete(test_node4.NetDevs, "foo") + nodeConf.GetFrom(test_node4) + nodeConf.Flatten() + ymlByte, _ = yaml.Marshal(nodeConf) + wanted = `{} +` + if string(ymlByte) != wanted { + t.Errorf("Couldn't remove tag'%s'", string(ymlByte)) + } + }) } From e27ee11f166bc880bd84b1ba1d8c3d000125fc6a Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Wed, 22 Mar 2023 09:10:23 +0100 Subject: [PATCH 24/25] fixed wrong converter for bools Signed-off-by: Christian Goll --- internal/pkg/node/flags.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/pkg/node/flags.go b/internal/pkg/node/flags.go index 5196135f..e15b769c 100644 --- a/internal/pkg/node/flags.go +++ b/internal/pkg/node/flags.go @@ -92,11 +92,11 @@ func createFlags(baseCmd *cobra.Command, excludeList []string, */ converters = append(converters, func() error { if !util.InSlice(GetUnsetVerbs(), *ptr) && *ptr != "" { - if strings.ToLower(*ptr) != "yes" { + if strings.ToLower(*ptr) == "yes" { *ptr = "true" return nil } - if strings.ToLower(*ptr) != "no" { + if strings.ToLower(*ptr) == "no" { *ptr = "false" return nil } From 12e462afa12685b7de702f58e74b3809c55aa7c1 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Thu, 23 Mar 2023 10:32:33 +0100 Subject: [PATCH 25/25] added parsing tests Signed-off-by: Christian Goll --- internal/app/wwctl/node/add/main_test.go | 57 +++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/internal/app/wwctl/node/add/main_test.go b/internal/app/wwctl/node/add/main_test.go index 98c8e47e..8fa8ed35 100644 --- a/internal/app/wwctl/node/add/main_test.go +++ b/internal/app/wwctl/node/add/main_test.go @@ -16,6 +16,7 @@ func Test_Add(t *testing.T) { args []string wantErr bool stdout string + chkout bool outDb string }{ {name: "single node add", @@ -39,6 +40,51 @@ nodes: n01: profiles: - foo +`}, + {name: "single node add, discoverable true, explicit", + args: []string{"--discoverable=true", "n01"}, + wantErr: false, + stdout: "", + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: + n01: + discoverable: "true" + profiles: + - default +`}, + {name: "single node add, discoverable true with yes", + args: []string{"--discoverable=yes", "n01"}, + wantErr: false, + stdout: "", + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: + n01: + discoverable: "true" + profiles: + - default +`}, + {name: "single node add, discoverable wrong argument", + args: []string{"--discoverable=maybe", "n01"}, + wantErr: true, + stdout: "", + chkout: false, + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: {} +`}, + {name: "single node add, discoverable false", + args: []string{"--discoverable=false", "n01"}, + wantErr: false, + stdout: "", + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: + n01: + discoverable: "false" + profiles: + - default `}, {name: "single node add with Kernel args", args: []string{"--kernelargs=foo", "n01"}, @@ -94,6 +140,15 @@ nodes: network devices: default: ipaddr: 10.0.0.1 +`}, + {name: "single node with malformed ipaddr", + args: []string{"--ipaddr=10.0.1", "n01"}, + wantErr: true, + stdout: "", + chkout: false, + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: {} `}, {name: "three nodes with ipaddr", args: []string{"--ipaddr=10.10.0.1", "n[01-02,03]"}, @@ -212,7 +267,7 @@ WW_INTERNAL: 43 t.Errorf("DB dump is wrong, got:'%s'\nwant:'%s'", dump, tt.outDb) t.FailNow() } - if buf.String() != tt.stdout { + if tt.chkout && buf.String() != tt.stdout { t.Errorf("Got wrong output, got:'%s'\nwant:'%s'", buf.String(), tt.stdout) t.FailNow() }