diff --git a/.github/workflows/releasebuild.yml b/.github/workflows/releasebuild.yml new file mode 100644 index 00000000..92d41c77 --- /dev/null +++ b/.github/workflows/releasebuild.yml @@ -0,0 +1,88 @@ +--- +name: releasebuild + +on: + release: + types: + - published + +jobs: + + distbuild: + name: Checkout code and build tar.gz + runs-on: ubuntu-latest + steps: + - name: Set the version + id: version_number + run: | + echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV + - name: Set the git source location + id: source_loc + run: | + echo "SRC=${GITHUB_REF}" >> $GITHUB_ENV + - name: Checkout Code + uses: actions/checkout@v2 + - name: Install Go + uses: actions/setup-go@v2 + - uses: actions/cache@v2 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + - name: Build tar.gz + run: make dist + - name: Upload mock script + uses: actions/upload-artifact@v2 + with: + name: mockbuild.sh + path: ci/mockbuild.sh + - name: Upload tar.gz + uses: actions/upload-artifact@v2 + with: + name: warewulf-${{ env.VERSION }}.tar.gz + path: warewulf-${{ env.VERSION }}.tar.gz + + rpmbuild: + name: Build using Rocky Linux + needs: distbuild + runs-on: ubuntu-latest + container: + image: rockylinux/rockylinux + options: --privileged + steps: + - name: Set the version + id: version_number + run: | + echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV + - name: Download mock build script + uses: actions/download-artifact@v2 + with: + name: mockbuild.sh + - name: Download tar.gz + uses: actions/download-artifact@v2 + with: + name: warewulf-${{ env.VERSION }}.tar.gz + - name: Set execute bit + run: chmod +x mockbuild.sh + - name: Run rpm build script + run: ./mockbuild.sh ${{ env.VERSION }} + - name: Upload EL8 RPM + uses: actions/upload-artifact@v2 + with: + name: warewulf-${{ env.VERSION }}-${{ env.RELEASE }}.el8.x86_64.rpm + path: warewulf-${{ env.VERSION }}-${{ env.RELEASE }}.el8.x86_64.rpm + - name: Upload EL7 RPM + uses: actions/upload-artifact@v2 + with: + name: warewulf-${{ env.VERSION }}-${{ env.RELEASE }}.el7.x86_64.rpm + path: warewulf-${{ env.VERSION }}-${{ env.RELEASE }}.el7.x86_64.rpm + - name: Upload Assets To Release + uses: xresloader/upload-to-github-release@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + file: 'warewulf-${{ env.VERSION }}.tar.gz;warewulf-${{ env.VERSION }}-${{ env.RELEASE }}.*.rpm' + draft: false + update_latest_release: true + tags: true diff --git a/.gitignore b/.gitignore index 9bfa6313..e1845cba 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,7 @@ /wwctl /bash_completion /man_page + +# other created files +/usr/share/man/man1/ +/etc/bash_completion.d/ diff --git a/Makefile b/Makefile index 0babd543..53efc9f1 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,20 @@ .PHONY: all -VERSION := 4.2.0 +VERSION ?= 4.2.0 +RELEASE ?= 1 + +SRC ?= main + +VERSION_FULL ?= $(shell test -e .git && git describe --tags --long --first-parent) +ifeq ($(VERSION_FULL),) +VERSION_FULL := $(VERSION) +endif + # auto installed tooling TOOLS_DIR := .tools TOOLS_BIN := $(TOOLS_DIR)/bin +CONFIG := $(shell pwd) # tools GO_TOOLS_BIN := $(addprefix $(TOOLS_BIN)/, $(notdir $(GO_TOOLS))) @@ -19,8 +29,14 @@ export GOPROXY # built tags needed for wwbuild binary WW_BUILD_GO_BUILD_TAGS := containers_image_openpgp containers_image_ostree +# system-overlay directories for install_wwclient +OVERLAY_DIR ?= $(DESTDIR)/var/warewulf/overlays/system/ +overlays = ${dir ${wildcard ${OVERLAY_DIR}*/}} + all: vendor wwctl wwclient bash_completion man_page +build: lint test-it vet all + # set the go tools into the tools bin. setup_tools: $(GO_TOOLS_BIN) $(GOLANGCI_LINT) @@ -48,6 +64,22 @@ lint: setup_tools @echo Running golangci-lint... @$(GOLANGCI_LINT) run --build-tags "$(WW_BUILD_GO_BUILD_TAGS)" --skip-dirs internal/pkg/staticfiles ./... +vet: + go vet ./... + +test-it: + go test -v ./... + +# Generate test coverage +test-cover: ## Run test coverage and generate html report + rm -fr coverage + mkdir coverage + go list -f '{{if gt (len .TestGoFiles) 0}}"go test -covermode count -coverprofile {{.Name}}.coverprofile -coverpkg ./... {{.ImportPath}}"{{end}}' ./... | xargs -I {} bash -c {} + echo "mode: count" > coverage/cover.out + grep -h -v "^mode:" *.coverprofile >> "coverage/cover.out" + rm *.coverprofile + go tool cover -html=coverage/cover.out -o=coverage/cover.html + debian: all files: all @@ -61,6 +93,7 @@ files: all install -d -m 0755 $(DESTDIR)/usr/share/man/man1 test -f $(DESTDIR)/etc/warewulf/warewulf.conf || install -m 644 etc/warewulf.conf $(DESTDIR)/etc/warewulf/ test -f $(DESTDIR)/etc/warewulf/hosts.tmpl || install -m 644 etc/hosts.tmpl $(DESTDIR)/etc/warewulf/ + test -f $(DESTDIR)/etc/warewulf/nodes.conf || install -m 640 etc/nodes.conf $(DESTDIR)/etc/warewulf/ cp -r etc/dhcp $(DESTDIR)/etc/warewulf/ cp -r etc/ipxe $(DESTDIR)/etc/warewulf/ cp -r overlays $(DESTDIR)/var/warewulf/ @@ -68,18 +101,18 @@ files: all chmod 600 $(DESTDIR)/var/warewulf/overlays/system/default/etc/ssh/ssh* chmod 644 $(DESTDIR)/var/warewulf/overlays/system/default/etc/ssh/ssh*.pub.ww mkdir -p $(DESTDIR)/var/warewulf/overlays/system/default/warewulf/bin/ - cp wwclient $(DESTDIR)/var/warewulf/overlays/system/default/warewulf/bin/ cp wwctl $(DESTDIR)/usr/bin/ mkdir -p $(DESTDIR)/usr/lib/firewalld/services install -c -m 0644 include/firewalld/warewulf.xml $(DESTDIR)/usr/lib/firewalld/services mkdir -p $(DESTDIR)/usr/lib/systemd/system install -c -m 0644 include/systemd/warewulfd.service $(DESTDIR)/usr/lib/systemd/system - ./bash_completion $(DESTDIR)/etc/bash_completion.d/warewulf - ./man_page $(DESTDIR)/usr/share/man/man1 - gzip --force $(DESTDIR)/usr/share/man/man1/wwctl*1 -# systemctl daemon-reload -# cp -r tftpboot/* /var/lib/tftpboot/warewulf/ipxe/ -# restorecon -r /var/lib/tftpboot/warewulf + cp etc/bash_completion.d/warewulf $(DESTDIR)/etc/bash_completion.d/ + cp usr/share/man/man1/* $(DESTDIR)/usr/share/man/man1/ + +init: + systemctl daemon-reload + cp -r tftpboot/* /var/lib/tftpboot/warewulf/ipxe/ + restorecon -r /var/lib/tftpboot/warewulf debfiles: debian chmod +x $(DESTDIR)/var/warewulf/overlays/system/debian/init @@ -89,23 +122,35 @@ debfiles: debian cp wwclient $(DESTDIR)/var/warewulf/overlays/system/debian/warewulf/bin/ wwctl: - cd cmd/wwctl; GOOS=linux go build -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../wwctl + cd cmd/wwctl; GOOS=linux go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/version.Version=$(VERSION_FULL)'" -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../wwctl wwclient: cd cmd/wwclient; CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -ldflags '-extldflags -static' -o ../../wwclient +install_wwclient: wwclient + $(foreach overlay, ${overlays}, install -D -m 0755 wwclient ${overlay}warewulf/bin/wwclient;) + bash_completion: - cd cmd/bash_completion; go build -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../bash_completion + cd cmd/bash_completion && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=$(CONFIG)/etc/warewulf.conf'\ + -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=$(CONFIG)/etc/nodes.conf'"\ + -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../bash_completion + install -d -m 0755 ./etc/bash_completion.d/ + ./bash_completion ./etc/bash_completion.d/warewulf man_page: - cd cmd/man_page; go build -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../man_page + cd cmd/man_page && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=$(CONFIG)/etc/warewulf.conf'\ + -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=$(CONFIG)/etc/nodes.conf'"\ + -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../man_page + install -d -m 0755 ./usr/share/man/man1 + ./man_page ./usr/share/man/man1 + gzip --force ./usr/share/man/man1/wwctl*1 dist: vendor rm -rf _dist/warewulf-$(VERSION) mkdir -p _dist/warewulf-$(VERSION) - git archive --format=tar main | tar -xf - -C _dist/warewulf-$(VERSION) + git archive --format=tar $(SRC) | tar -xf - -C _dist/warewulf-$(VERSION) cp -r vendor _dist/warewulf-$(VERSION)/ - sed -e 's/@VERSION@/$(VERSION)/g' warewulf.spec.in > _dist/warewulf-$(VERSION)/warewulf.spec + sed -e 's/@VERSION@/$(VERSION)/g; s/@RELEASE@/$(RELEASE)/g' warewulf.spec.in > _dist/warewulf-$(VERSION)/warewulf.spec cd _dist; tar -czf ../warewulf-$(VERSION).tar.gz warewulf-$(VERSION) clean: @@ -116,7 +161,7 @@ clean: rm -f bash_completion rm -f man_page -install: files +install: files install_wwclient debinstall: files debfiles diff --git a/README.md b/README.md index c5f2b056..696d80c9 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,10 @@ #### Quick Links: -* [Documentation](https://warewulf.hpcng.org) -* [GitHub](http://github.com/hpcng/warewulf) +* [Website](https://warewulf.org) +* [Documentation](https://warewulf.org/docs) +* [Download / Releases](http://github.com/hpcng/warewulf/releases) +* [Support](http://www.ciq.co/warewulf) ## About Warewulf diff --git a/ci/mockbuild.sh b/ci/mockbuild.sh new file mode 100644 index 00000000..1547310a --- /dev/null +++ b/ci/mockbuild.sh @@ -0,0 +1,17 @@ +#!/usr/bin/bash + +VERSION=$1 + +dnf install -y epel-release +dnf install -y mock + +tar -xf warewulf-${VERSION}.tar.gz warewulf-${VERSION}/warewulf.spec + +RELEASE=$(grep 'Release: ' warewulf-${VERSION}/warewulf.spec | cut -d ':' -f2 | awk -F'%' '{print $1}' | tr -d ' ') +echo RELEASE=${RELEASE} >> $GITHUB_ENV + +mock -r epel-8-x86_64 --rebuild --spec=warewulf-${VERSION}/warewulf.spec --sources=. +mv /var/lib/mock/epel-8-x86_64/result/warewulf-${VERSION}-${RELEASE}.el8.x86_64.rpm . + +mock -r epel-7-x86_64 --rebuild --spec=warewulf-${VERSION}/warewulf.spec --sources=. +mv /var/lib/mock/epel-7-x86_64/result/warewulf-${VERSION}-${RELEASE}.el7.x86_64.rpm . \ No newline at end of file diff --git a/cmd/man_page/man_page.go b/cmd/man_page/man_page.go index 369026fa..8a35a3be 100644 --- a/cmd/man_page/man_page.go +++ b/cmd/man_page/man_page.go @@ -1,4 +1,5 @@ -// usage: ./bash_completion +// Generate man pages for wwctl command. +// usage: ./man_page package main import ( diff --git a/etc/nodes.conf b/etc/nodes.conf new file mode 100644 index 00000000..3999a8cd --- /dev/null +++ b/etc/nodes.conf @@ -0,0 +1,4 @@ +nodeprofiles: + default: + comment: This profile is automatically included for each node +nodes: {} diff --git a/internal/app/wwctl/configure/dhcp/root.go b/internal/app/wwctl/configure/dhcp/root.go index 76ec9e97..d27f8ad3 100644 --- a/internal/app/wwctl/configure/dhcp/root.go +++ b/internal/app/wwctl/configure/dhcp/root.go @@ -6,7 +6,8 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "dhcp", + DisableFlagsInUseLine: true, + Use: "dhcp [OPTIONS]", Short: "Manage and initialize DHCP", Long: "DHCP is a dependent service to Warewulf. This command will configure DHCP as defined\n" + "in the warewulf.conf file.", diff --git a/internal/app/wwctl/configure/hosts/root.go b/internal/app/wwctl/configure/hosts/root.go index a8294453..84877a40 100644 --- a/internal/app/wwctl/configure/hosts/root.go +++ b/internal/app/wwctl/configure/hosts/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "hosts", + DisableFlagsInUseLine: true, + Use: "hosts [OPTIONS]", Short: "Update the /etc/hosts file", Long: "Write out the /etc/hosts file based on the Warewulf template (hosts.tmpl) in the\n" + "Warewulf configuration directory.", diff --git a/internal/app/wwctl/configure/nfs/root.go b/internal/app/wwctl/configure/nfs/root.go index 4121efa9..4e074021 100644 --- a/internal/app/wwctl/configure/nfs/root.go +++ b/internal/app/wwctl/configure/nfs/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "nfs", + DisableFlagsInUseLine: true, + Use: "nfs [OPTIONS]", Short: "Manage and initialize NFS", Long: "NFS is an optional dependent service of Warewulf, this tool will automatically\n" + "configure NFS as per the configuration in the warewulf.conf file.", diff --git a/internal/app/wwctl/configure/root.go b/internal/app/wwctl/configure/root.go index aece4bfa..6d8a2c41 100644 --- a/internal/app/wwctl/configure/root.go +++ b/internal/app/wwctl/configure/root.go @@ -15,7 +15,8 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "configure", + DisableFlagsInUseLine: true, + Use: "configure [OPTIONS]", Short: "Manage system services", Long: "This application allows you to manage and initialize Warewulf dependent system\n" + "services based on the configuration in the warewulf.conf file.", diff --git a/internal/app/wwctl/configure/ssh/root.go b/internal/app/wwctl/configure/ssh/root.go index 8f7ba046..2246c0db 100644 --- a/internal/app/wwctl/configure/ssh/root.go +++ b/internal/app/wwctl/configure/ssh/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "ssh", + DisableFlagsInUseLine: true, + Use: "ssh [OPTIONS]", Short: "Manage and initialize SSH", Long: "SSH is an optionally dependent service for Warewulf, this tool will automatically\n" + "setup the ssh keys nodes using the 'default' system overlay as well as user owned\n" + diff --git a/internal/app/wwctl/configure/tftp/root.go b/internal/app/wwctl/configure/tftp/root.go index 9b5edb2e..7629fa2c 100644 --- a/internal/app/wwctl/configure/tftp/root.go +++ b/internal/app/wwctl/configure/tftp/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "tftp", + DisableFlagsInUseLine: true, + Use: "tftp [OPTIONS]", Short: "Manage and initialize TFTP", Long: "TFTP is a dependent service of Warewulf, this tool will enable the tftp services\n" + "on your Warewulf master.", diff --git a/internal/app/wwctl/container/build/root.go b/internal/app/wwctl/container/build/root.go index d02e651d..93ff98cd 100644 --- a/internal/app/wwctl/container/build/root.go +++ b/internal/app/wwctl/container/build/root.go @@ -7,9 +7,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "build [flags] [container name]...", + DisableFlagsInUseLine: true, + Use: "build [OPTIONS] CONTAINER [...]", Short: "(Re)build a bootable VNFS image", - Long: "This command will build a bootable VNFS image from an imported container image.", + Long: "This command will build a bootable VNFS image from imported CONTAINER image(s).", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { diff --git a/internal/app/wwctl/container/delete/root.go b/internal/app/wwctl/container/delete/root.go index fe0e5d23..64e909ba 100644 --- a/internal/app/wwctl/container/delete/root.go +++ b/internal/app/wwctl/container/delete/root.go @@ -7,9 +7,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "delete [flags] [container name]...", + DisableFlagsInUseLine: true, + Use: "delete [OPTIONS] CONTAINER [...]", Short: "Delete an imported container", - Long: "This command will delete a container that has been imported into Warewulf.", + Long: "This command will delete CONTAINERs that have been imported into Warewulf.", RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { diff --git a/internal/app/wwctl/container/exec/child/root.go b/internal/app/wwctl/container/exec/child/root.go index 8a9d422b..0b7dde7c 100644 --- a/internal/app/wwctl/container/exec/child/root.go +++ b/internal/app/wwctl/container/exec/child/root.go @@ -4,6 +4,7 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ + DisableFlagsInUseLine: true, Use: "__child", Hidden: true, RunE: CobraRunE, diff --git a/internal/app/wwctl/container/exec/root.go b/internal/app/wwctl/container/exec/root.go index 8e418541..a3022599 100644 --- a/internal/app/wwctl/container/exec/root.go +++ b/internal/app/wwctl/container/exec/root.go @@ -8,10 +8,11 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "exec [flags] [container name] [command]", + DisableFlagsInUseLine: true, + Use: "exec [OPTIONS] CONTAINER COMMAND", Short: "Run a command inside of a Warewulf container", - Long: "This command will allow you to run any command inside of a given\n" + - "warewulf container. This is commonly used with an interactive shell such as /bin/bash\n" + + Long: "Run a COMMAND inside of a warewulf CONTAINER.\n" + + "This is commonly used with an interactive shell such as /bin/bash\n" + "to run a virtual environment within the container.", RunE: CobraRunE, Args: cobra.MinimumNArgs(2), diff --git a/internal/app/wwctl/container/imprt/root.go b/internal/app/wwctl/container/imprt/root.go index 1725d06a..061c0efe 100644 --- a/internal/app/wwctl/container/imprt/root.go +++ b/internal/app/wwctl/container/imprt/root.go @@ -4,10 +4,14 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "import [flags] [container source URI]", + DisableFlagsInUseLine: true, + Use: "import [OPTIONS] SOURCE [NAME]", Short: "Import a container into Warewulf", - Long: "This command will pull and import a container into Warewulf so it can be used\n" + - "as a source to create a bootable VNFS image.", + Long: +`This command will pull and import a container into Warewulf from SOURCE, +optionally renaming it to NAME. The SOURCE must be in a supported URI format. +Imported containers are used to create bootable VNFS images.`, + Example: "wwctl container import docker://warewulf/centos-8 my_container", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), } diff --git a/internal/app/wwctl/container/list/root.go b/internal/app/wwctl/container/list/root.go index 273eb371..908aae77 100644 --- a/internal/app/wwctl/container/list/root.go +++ b/internal/app/wwctl/container/list/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "list [flags]", + DisableFlagsInUseLine: true, + Use: "list [OPTIONS]", Short: "List imported Warewulf containers", Long: "This command will show you the containers that are imported into Warewulf.", RunE: CobraRunE, diff --git a/internal/app/wwctl/container/root.go b/internal/app/wwctl/container/root.go index 79bc0a5f..02efc3b0 100644 --- a/internal/app/wwctl/container/root.go +++ b/internal/app/wwctl/container/root.go @@ -11,11 +11,12 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "container", + DisableFlagsInUseLine: true, + Use: "container COMMAND [OPTIONS]", Short: "Container / VNFS image management", Long: "Starting with version 4, Warewulf uses containers to build the bootable VNFS\n" + - "images for nodes to boot. These commands will help you import, management, and\n" + - "transform containers into bootable Warewulf VNFS images.", + "node images. These commands will help you import, manage, and transform\n" + + "containers into bootable Warewulf VNFS images.", Aliases: []string{"vnfs"}, } ) diff --git a/internal/app/wwctl/kernel/delete/root.go b/internal/app/wwctl/kernel/delete/root.go index 4e714b46..5fc68bea 100644 --- a/internal/app/wwctl/kernel/delete/root.go +++ b/internal/app/wwctl/kernel/delete/root.go @@ -7,9 +7,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "delete [flags] [kernel version]...", - Short: "Delete an imported kernel", - Long: "This command will delete a kernel that has been imported into Warewulf.", + DisableFlagsInUseLine: true, + Use: "delete [OPTIONS] KERNEL [...]", + Short: "Delete imported kernels", + Long: "This command will delete KERNEL versions that have been imported into Warewulf.", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { diff --git a/internal/app/wwctl/kernel/imprt/root.go b/internal/app/wwctl/kernel/imprt/root.go index cc6e2539..7d398264 100644 --- a/internal/app/wwctl/kernel/imprt/root.go +++ b/internal/app/wwctl/kernel/imprt/root.go @@ -9,10 +9,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "import [flags] [kernel version]", + DisableFlagsInUseLine: true, + Use: "import [OPTIONS] KERNEL", Short: "Import Kernel version into Warewulf", - Long: "This will import a Kernel version from the control node into Warewulf for nodes\n" + - "to be configured to boot on.", + Long: "This will import a boot KERNEL version from the control node into Warewulf", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), } diff --git a/internal/app/wwctl/kernel/list/root.go b/internal/app/wwctl/kernel/list/root.go index 2fcceb4b..fba84101 100644 --- a/internal/app/wwctl/kernel/list/root.go +++ b/internal/app/wwctl/kernel/list/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "list [flags]", + DisableFlagsInUseLine: true, + Use: "list [OPTIONS]", Short: "List imported Kernel images", Long: "This command will list the kernels that have been imported into Warewulf.", RunE: CobraRunE, diff --git a/internal/app/wwctl/kernel/root.go b/internal/app/wwctl/kernel/root.go index 33d74b0a..bb0cdb8d 100644 --- a/internal/app/wwctl/kernel/root.go +++ b/internal/app/wwctl/kernel/root.go @@ -9,10 +9,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "kernel", + DisableFlagsInUseLine: true, + Use: "kernel COMMAND [OPTIONS]", Short: "Kernel Image Management", - Long: "This command is for management of Warewulf Kernels to be used for\n" + - "bootstrapping nodes", + Long: "This command manages Warewulf Kernels used for bootstrapping nodes", } ) diff --git a/internal/app/wwctl/node/add/root.go b/internal/app/wwctl/node/add/root.go index f15c2749..a3081965 100644 --- a/internal/app/wwctl/node/add/root.go +++ b/internal/app/wwctl/node/add/root.go @@ -4,9 +4,10 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "add [flags] [node pattern]", + DisableFlagsInUseLine: true, + Use: "add [OPTIONS] NODENAME", Short: "Add new node to Warewulf", - Long: "This command will add a new node to Warewulf.", + Long: "This command will add a new node named NODENAME to Warewulf.", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), } diff --git a/internal/app/wwctl/node/console/power.go b/internal/app/wwctl/node/console/power.go index 9332edb7..aa9fcfa7 100644 --- a/internal/app/wwctl/node/console/power.go +++ b/internal/app/wwctl/node/console/power.go @@ -49,11 +49,13 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } ipmiCmd := power.IPMI{ - NodeName: node.Id.Get(), - HostName: node.IpmiIpaddr.Get(), - User: node.IpmiUserName.Get(), - Password: node.IpmiPassword.Get(), - AuthType: "MD5", + NodeName: node.Id.Get(), + HostName: node.IpmiIpaddr.Get(), + Port: node.IpmiPort.Get(), + User: node.IpmiUserName.Get(), + Password: node.IpmiPassword.Get(), + AuthType: "MD5", + Interface: node.IpmiInterface.Get(), } err := ipmiCmd.Console() diff --git a/internal/app/wwctl/node/console/root.go b/internal/app/wwctl/node/console/root.go index 6a4e1b9f..a0d05f4e 100644 --- a/internal/app/wwctl/node/console/root.go +++ b/internal/app/wwctl/node/console/root.go @@ -6,9 +6,10 @@ import ( var ( powerCmd = &cobra.Command{ - Use: "console [flags] [node pattern]", + DisableFlagsInUseLine: true, + Use: "console [OPTIONS] NODENAME", Short: "Connect to IPMI console", - Long: "Start IPMI console for a singe node.", + Long: "Start a new IPMI console for NODENAME.", Args: cobra.MinimumNArgs(1), RunE: CobraRunE, } diff --git a/internal/app/wwctl/node/delete/root.go b/internal/app/wwctl/node/delete/root.go index b62664f2..95a96f8f 100644 --- a/internal/app/wwctl/node/delete/root.go +++ b/internal/app/wwctl/node/delete/root.go @@ -7,9 +7,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "delete [flags] [exact node name]...", + DisableFlagsInUseLine: true, + Use: "delete [OPTIONS] NODE [NODE ...]", Short: "Delete a node from Warewulf", - Long: "This command will remove a node from the Warewulf node configuration.", + Long: "This command will remove NODE(s) from the Warewulf node configuration.", Args: cobra.MinimumNArgs(1), RunE: CobraRunE, Aliases: []string{"rm", "del"}, diff --git a/internal/app/wwctl/node/list/main.go b/internal/app/wwctl/node/list/main.go index ea9c6301..9dda4a8b 100644 --- a/internal/app/wwctl/node/list/main.go +++ b/internal/app/wwctl/node/list/main.go @@ -85,11 +85,11 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } } else if ShowIpmi { - fmt.Printf("%-22s %-16s %-5s %-20s %-20s %-20s\n", "NODE NAME", "IPMI IPADDR", "IPMI PORT", "IPMI USERNAME", "IPMI PASSWORD", "IPMI INTERFACE") - fmt.Println(strings.Repeat("=", 80)) + fmt.Printf("%-22s %-16s %-10s %-20s %-20s %-14s\n", "NODE NAME", "IPMI IPADDR", "IPMI PORT", "IPMI USERNAME", "IPMI PASSWORD", "IPMI INTERFACE") + fmt.Println(strings.Repeat("=", 108)) for _, node := range node.FilterByName(nodes, args) { - fmt.Printf("%-22s %-16s %-5s %-20s %-20s %-20s\n", node.Id.Get(), node.IpmiIpaddr.Print(), node.IpmiPort.Print(), node.IpmiUserName.Print(), node.IpmiPassword.Print(), node.IpmiInterface.Print()) + fmt.Printf("%-22s %-16s %-10s %-20s %-20s %-14s\n", node.Id.Get(), node.IpmiIpaddr.Print(), node.IpmiPort.Print(), node.IpmiUserName.Print(), node.IpmiPassword.Print(), node.IpmiInterface.Print()) } } else if ShowLong { diff --git a/internal/app/wwctl/node/list/root.go b/internal/app/wwctl/node/list/root.go index b72903ed..b354c881 100644 --- a/internal/app/wwctl/node/list/root.go +++ b/internal/app/wwctl/node/list/root.go @@ -1,14 +1,32 @@ package list -import "github.com/spf13/cobra" +import ( + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/spf13/cobra" +) var ( baseCmd = &cobra.Command{ - Use: "list [flags] (node pattern)", - Short: "List nodes matching pattern", - Long: "This command will show you configured nodes.", + DisableFlagsInUseLine: true, + Use: "list [OPTIONS] [PATTERN]", + Short: "List nodes", + Long: "This command lists all configured nodes. Optionally, it will list only\n" + + "nodes matching a glob PATTERN.", RunE: CobraRunE, Aliases: []string{"ls"}, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + nodeDB, _ := node.New() + nodes, _ := nodeDB.FindAllNodes() + var node_names []string + for _, node := range nodes { + node_names = append(node_names, node.Id.Get()) + } + return node_names, cobra.ShellCompDirectiveNoFileComp + }, } ShowNet bool ShowIpmi bool diff --git a/internal/app/wwctl/node/ready/root.go b/internal/app/wwctl/node/ready/root.go index abcba8b8..fb129a22 100644 --- a/internal/app/wwctl/node/ready/root.go +++ b/internal/app/wwctl/node/ready/root.go @@ -7,8 +7,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "ready", - Short: "Warewulf Status Check", + DisableFlagsInUseLine: true, + Use: "ready [OPTIONS]", + Short: "Warewulf status check", + Long: "Provides the current status of all Warewulf nodes.", RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { diff --git a/internal/app/wwctl/node/root.go b/internal/app/wwctl/node/root.go index 7a37b726..a8b10061 100644 --- a/internal/app/wwctl/node/root.go +++ b/internal/app/wwctl/node/root.go @@ -13,7 +13,8 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "node", + DisableFlagsInUseLine: true, + Use: "node COMMAND [OPTONS]", Short: "Node management", Long: "Management of node settings. All node ranges can use brackets to identify\n" + "node ranges. For example: n00[00-4].cluster[0-1] will identify the first 5 nodes\n" + diff --git a/internal/app/wwctl/node/sensors/root.go b/internal/app/wwctl/node/sensors/root.go index ea95dd4f..07405812 100644 --- a/internal/app/wwctl/node/sensors/root.go +++ b/internal/app/wwctl/node/sensors/root.go @@ -7,9 +7,10 @@ import ( var ( powerCmd = &cobra.Command{ - Use: "sensors [flags] [node pattern]", - Short: "Show node's IPMI sensor information", - Long: "Show IPMI sensors for a single node.", + DisableFlagsInUseLine: true, + Use: "sensors [OPTIONS] PATTERN", + Short: "Show node IPMI sensor information", + Long: "Show IPMI sensor information for nodes matching PATTERN.", Args: cobra.MinimumNArgs(1), RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { diff --git a/internal/app/wwctl/node/set/root.go b/internal/app/wwctl/node/set/root.go index 344b7c11..fb6c3b5c 100644 --- a/internal/app/wwctl/node/set/root.go +++ b/internal/app/wwctl/node/set/root.go @@ -12,9 +12,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "set [flags] [node pattern]...", + DisableFlagsInUseLine: true, + Use: "set [OPTIONS] PATTERN [PATTERN ...]", Short: "Configure node properties", - Long: "This command will allow you to set configuration properties for nodes.\n\n" + + Long: "This command sets configuration properties for nodes matching PATTERN.\n\n" + "Note: use the string 'UNSET' to remove a configuration", Args: cobra.MinimumNArgs(1), RunE: CobraRunE, diff --git a/internal/app/wwctl/overlay/build/root.go b/internal/app/wwctl/overlay/build/root.go index a6901f87..5b5bb45d 100644 --- a/internal/app/wwctl/overlay/build/root.go +++ b/internal/app/wwctl/overlay/build/root.go @@ -4,9 +4,10 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "build [flags] ", + DisableFlagsInUseLine: true, + Use: "build [OPTIONS] {system|runtime} OVERLAY_NAME", Short: "(Re)build an overlay", - Long: "This command will build a system or runtime overlay.", + Long: "This command builds a new system or runtime overlay named OVERLAY_NAME.", RunE: CobraRunE, Args: cobra.RangeArgs(0, 2), } diff --git a/internal/app/wwctl/overlay/chmod/root.go b/internal/app/wwctl/overlay/chmod/root.go index b9a254d6..7790c5df 100644 --- a/internal/app/wwctl/overlay/chmod/root.go +++ b/internal/app/wwctl/overlay/chmod/root.go @@ -1,15 +1,21 @@ package chmod -import "github.com/spf13/cobra" +import ( + "github.com/spf13/cobra" +) var ( baseCmd = &cobra.Command{ - Use: "chmod [flags] ", - Short: "Change file permissions within an overlay", - Long: "This command will allow you to change the permissions of a file within an\n" + - "overlay.", - RunE: CobraRunE, - Args: cobra.ExactArgs(4), + DisableFlagsInUseLine: true, + Use: "chmod [OPTIONS] {runtime|system} OVERLAY_NAME FILENAME MODE", + Short: "Change file permissions in an overlay", + Long: `Changes the permissions of a single FILENAME within an overlay specified by +overlay type (system or runtime) and its OVERLAY_NAME. + +You can use any MODE format supported by the chmod command.`, + Example: "wwctl overlay chmod system default /etc/hostname.ww 0660", + RunE: CobraRunE, + Args: cobra.ExactArgs(4), } ) diff --git a/internal/app/wwctl/overlay/chown/root.go b/internal/app/wwctl/overlay/chown/root.go index 941328f0..8d45823e 100644 --- a/internal/app/wwctl/overlay/chown/root.go +++ b/internal/app/wwctl/overlay/chown/root.go @@ -4,10 +4,10 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "chown [flags] []", + Use: "chown [OPTIONS] {system|runtime} OVERLAY_NAME FILE UID [GID]", Short: "Change file ownership within an overlay", - Long: "This command will allow you to change the ownership of a file within an\n" + - "overlay.", + Long: "This command changes the ownership of a FILE within the system or runtime OVERLAY_NAME\n" + + "to the user specified by UID. Optionally, it will also change group ownership to GID.", RunE: CobraRunE, Args: cobra.RangeArgs(4, 5), } diff --git a/internal/app/wwctl/overlay/create/root.go b/internal/app/wwctl/overlay/create/root.go index 15242009..b09b249e 100644 --- a/internal/app/wwctl/overlay/create/root.go +++ b/internal/app/wwctl/overlay/create/root.go @@ -6,9 +6,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "create [flags] ", + DisableFlagsInUseLine: true, + Use: "create [OPTIONS] {system|runtime} OVERLAY_NAME", Short: "Initialize a new Overlay", - Long: "This command will create a new empty overlay.", + Long: "This command creates a new empty system or runtime overlay named OVERLAY_NAME.", RunE: CobraRunE, Args: cobra.ExactArgs(2), } diff --git a/internal/app/wwctl/overlay/delete/main.go b/internal/app/wwctl/overlay/delete/main.go index 7a3a17a5..69d20226 100644 --- a/internal/app/wwctl/overlay/delete/main.go +++ b/internal/app/wwctl/overlay/delete/main.go @@ -24,10 +24,6 @@ func CobraRunE(cmd *cobra.Command, args []string) error { fileName = args[2] } - if overlayName == "default" { - return errors.New("refusing to delete the default overlay") - } - if overlayKind != "system" && overlayKind != "runtime" { return errors.New("overlay kind must be of type 'system' or 'runtime'") } @@ -49,6 +45,9 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } if fileName == "" { + if overlayName == "default" { + return errors.New("refusing to delete the default overlay") + } if Force { err := os.RemoveAll(overlayPath) if err != nil { diff --git a/internal/app/wwctl/overlay/delete/root.go b/internal/app/wwctl/overlay/delete/root.go index bfe9299c..875909b3 100644 --- a/internal/app/wwctl/overlay/delete/root.go +++ b/internal/app/wwctl/overlay/delete/root.go @@ -6,10 +6,11 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "delete [flags] [overlay file]", + DisableFlagsInUseLine: true, + Use: "delete [OPTIONS] {runtime|system} OVERLAY_NAME [FILE [FILE ...]]", Short: "Delete Warewulf Overlay or files", - Long: "This command will delete files within an overlay or an entire overlay if no\n" + - "files are given to remove (use with caution).", + Long: "This command will delete FILEs within OVERLAY_NAME or the entire OVERLAY_NAME if no\n" + + "files are listed. Use with caution!", RunE: CobraRunE, Args: cobra.RangeArgs(2, 3), Aliases: []string{"rm", "del"}, diff --git a/internal/app/wwctl/overlay/edit/root.go b/internal/app/wwctl/overlay/edit/root.go index 3ec6add2..7f981614 100644 --- a/internal/app/wwctl/overlay/edit/root.go +++ b/internal/app/wwctl/overlay/edit/root.go @@ -6,11 +6,12 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "edit [flags] ", - Short: "Edit/Create a file within a Warewulf Overlay", - Long: "This command will allow you to edit or create a new file within a given\n" + - "overlay. Note: when creating files ending in a '.ww' suffix this will always be\n" + - "parsed as a Warewulf template file, and the suffix will be removed automatically.", + DisableFlagsInUseLine: true, + Use: "edit [OPTIONS] {system|runtime} OVERLAY_NAME FILE", + Short: "Edit or create a file within a Warewulf Overlay", + Long: "This command will open the FILE for editing or create a new file within the\n" + + "OVERLAY_NAME. Note: files created with a '.ww' suffix will always be\n" + + "parsed as Warewulf template files, and the suffix will be removed automatically.", RunE: CobraRunE, Args: cobra.ExactArgs(3), } diff --git a/internal/app/wwctl/overlay/imprt/root.go b/internal/app/wwctl/overlay/imprt/root.go index 983052b8..0e4c2146 100644 --- a/internal/app/wwctl/overlay/imprt/root.go +++ b/internal/app/wwctl/overlay/imprt/root.go @@ -4,9 +4,11 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "import [flags] [overlay target]", + DisableFlagsInUseLine: true, + Use: "import [OPTIONS] {system|runtime} OVERLAY_NAME FILE [NEW_NAME]", Short: "Import a file into a Warewulf Overlay", - Long: "This command will import a file into a given Warewulf overlay.", + Long: "This command imports the FILE into the Warewulf OVERLAY_NAME.\n" + + "Optionally, the file can be renamed to NEW_NAME", RunE: CobraRunE, Args: cobra.RangeArgs(3, 4), Aliases: []string{"cp"}, diff --git a/internal/app/wwctl/overlay/list/root.go b/internal/app/wwctl/overlay/list/root.go index 9643845b..c7b33ce4 100644 --- a/internal/app/wwctl/overlay/list/root.go +++ b/internal/app/wwctl/overlay/list/root.go @@ -6,10 +6,11 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "list [flags] [overlay name]", + DisableFlagsInUseLine: true, + Use: "list [OPTIONS] {system|runtime} [OVERLAY_NAME]", Short: "List Warewulf Overlays and files", - Long: "This command will show you information about Warewulf overlays and the\n" + - "files contained within.", + Long: "This command displays information about all Warewulf overlays or the specified\n" + + "OVERLAY_NAME. It also supports listing overlay content information.", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), Aliases: []string{"ls"}, diff --git a/internal/app/wwctl/overlay/mkdir/root.go b/internal/app/wwctl/overlay/mkdir/root.go index 8a557079..e95cba0a 100644 --- a/internal/app/wwctl/overlay/mkdir/root.go +++ b/internal/app/wwctl/overlay/mkdir/root.go @@ -6,9 +6,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "mkdir [flags] ", + DisableFlagsInUseLine: true, + Use: "mkdir [OPTIONS] {system|runtime} OVERLAY_NAME DIRECTORY", Short: "Create a new directory within an Overlay", - Long: "This command will allow you to create a new file within a given Warewulf overlay.", + Long: "This command creates a new directory within the Warewulf OVERLAY_NAME.", RunE: CobraRunE, Args: cobra.MinimumNArgs(3), } diff --git a/internal/app/wwctl/overlay/root.go b/internal/app/wwctl/overlay/root.go index 616a99c0..4bb1cd95 100644 --- a/internal/app/wwctl/overlay/root.go +++ b/internal/app/wwctl/overlay/root.go @@ -16,7 +16,8 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "overlay", + DisableFlagsInUseLine: true, + Use: "overlay COMMAND [OPTIONS]", Short: "Warewulf Overlay Management", Long: "Management interface for Warewulf overlays", } diff --git a/internal/app/wwctl/overlay/show/root.go b/internal/app/wwctl/overlay/show/root.go index a8c51efc..43c491ce 100644 --- a/internal/app/wwctl/overlay/show/root.go +++ b/internal/app/wwctl/overlay/show/root.go @@ -6,10 +6,9 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "show [flags] ", + Use: "show [OPTIONS] {system|runtime} OVERLAY_NAME FILE", Short: "Show (cat) a file within a Warewulf Overlay", - Long: "This command will output the contents of a file within a given\n" + - "Warewulf overlay.", + Long: "This command displays the contents of FILE within OVERLAY_NAME.", RunE: CobraRunE, Aliases: []string{"cat"}, Args: cobra.ExactArgs(3), diff --git a/internal/app/wwctl/power/cycle/root.go b/internal/app/wwctl/power/cycle/root.go index e89899fc..18f0fc51 100644 --- a/internal/app/wwctl/power/cycle/root.go +++ b/internal/app/wwctl/power/cycle/root.go @@ -6,9 +6,10 @@ import ( var ( powerCmd = &cobra.Command{ - Use: "cycle [flags] ...", + DisableFlagsInUseLine: true, + Use: "cycle [OPTIONS] [PATTERN ...]", Short: "Power cycle the given node(s)", - Long: "This command will cycle the power for a given set of nodes.", + Long: "This command cycles power for a set of nodes specified by PATTERN.", RunE: CobraRunE, } ) diff --git a/internal/app/wwctl/power/off/root.go b/internal/app/wwctl/power/off/root.go index 0b98bed1..cb659dda 100644 --- a/internal/app/wwctl/power/off/root.go +++ b/internal/app/wwctl/power/off/root.go @@ -7,9 +7,10 @@ import ( var ( powerCmd = &cobra.Command{ - Use: "off", + DisableFlagsInUseLine: true, + Use: "off [OPTIONS] [PATTERN ...]", Short: "Power off the given node(s)", - Long: "This command will shutdown the power to a given set of nodes.", + Long: "This command will shutdown power to a set of nodes specified by PATTERN.", RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { diff --git a/internal/app/wwctl/power/on/root.go b/internal/app/wwctl/power/on/root.go index 7918299a..8e195e18 100644 --- a/internal/app/wwctl/power/on/root.go +++ b/internal/app/wwctl/power/on/root.go @@ -7,9 +7,9 @@ import ( var ( powerCmd = &cobra.Command{ - Use: "on", + Use: "on [OPTIONS] [PATTERN ...]", Short: "Power on the given node(s)", - Long: "This command will power on a given set of nodes.", + Long: "This command will power on a set of nodes specified by PATTERN.", RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { diff --git a/internal/app/wwctl/power/reset/root.go b/internal/app/wwctl/power/reset/root.go index 6622de8e..02d79c3a 100644 --- a/internal/app/wwctl/power/reset/root.go +++ b/internal/app/wwctl/power/reset/root.go @@ -7,9 +7,10 @@ import ( var ( powerCmd = &cobra.Command{ - Use: "reset", - Short: "Issue a reset to the given node(s)", - Long: "This command will issue a reset to the given set of nodes.", + DisableFlagsInUseLine: true, + Use: "reset [OPTIONS] [PATTERN ...]", + Short: "Issue a reset to node(s)", + Long: "This command will issue a reset to a set of nodes specified by PATTERN.", RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { diff --git a/internal/app/wwctl/power/root.go b/internal/app/wwctl/power/root.go index e2222e29..135139b2 100644 --- a/internal/app/wwctl/power/root.go +++ b/internal/app/wwctl/power/root.go @@ -12,9 +12,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "power", + DisableFlagsInUseLine: true, + Use: "power COMMAND [OPTIONS]", Short: "Warewulf node power management", - Long: "This command can control the power state of nodes.", + Long: "This command controls the power state of nodes.", } ) diff --git a/internal/app/wwctl/power/soft/root.go b/internal/app/wwctl/power/soft/root.go index af13114c..77100254 100644 --- a/internal/app/wwctl/power/soft/root.go +++ b/internal/app/wwctl/power/soft/root.go @@ -7,9 +7,10 @@ import ( var ( powerCmd = &cobra.Command{ + DisableFlagsInUseLine: true, Use: "soft", Short: "Gracefully shuts down the given node(s)", - Long: "This command will gracefully shutdown the given set of nodes.", + Long: "This command uses the operationg system to shut down the set of nodes specified by PATTERN.", RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { diff --git a/internal/app/wwctl/power/status/root.go b/internal/app/wwctl/power/status/root.go index 454165e5..40c6fc64 100644 --- a/internal/app/wwctl/power/status/root.go +++ b/internal/app/wwctl/power/status/root.go @@ -7,9 +7,10 @@ import ( var ( powerCmd = &cobra.Command{ - Use: "status", + DisableFlagsInUseLine: true, + Use: "status [OPTIONS] [PATTERN ...]", Short: "Show power status for the given node(s)", - Long: "This command will show the power status of a given set of nodes.", + Long: "This command displays the power status of a set of nodes specified by PATTERN.", RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { diff --git a/internal/app/wwctl/profile/add/root.go b/internal/app/wwctl/profile/add/root.go index 85d1ac42..9e95d8d2 100644 --- a/internal/app/wwctl/profile/add/root.go +++ b/internal/app/wwctl/profile/add/root.go @@ -4,9 +4,10 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "add ", + DisableFlagsInUseLine: true, + Use: "add PROFILE", Short: "Add a new node profile", - Long: "This command will add a new node profile.", + Long: "This command adds a new named PROFILE.", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), } diff --git a/internal/app/wwctl/profile/delete/root.go b/internal/app/wwctl/profile/delete/root.go index 44f3afe0..a5cea9e1 100644 --- a/internal/app/wwctl/profile/delete/root.go +++ b/internal/app/wwctl/profile/delete/root.go @@ -4,9 +4,10 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "delete [flags] ", + DisableFlagsInUseLine: true, + Use: "delete [OPTIONS] PROFILE", Short: "Delete a node profile", - Long: "This command will delete a node profile.", + Long: "This command deletes the node PROFILE. You may use a pattern for PROFILE.", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), } diff --git a/internal/app/wwctl/profile/list/main.go b/internal/app/wwctl/profile/list/main.go index 20e2dcd6..8734ffb0 100644 --- a/internal/app/wwctl/profile/list/main.go +++ b/internal/app/wwctl/profile/list/main.go @@ -41,7 +41,6 @@ func CobraRunE(cmd *cobra.Command, args []string) error { fmt.Printf("%-20s %-18s %s\n", profile.Id.Get(), "RuntimeOverlay", profile.RuntimeOverlay.Print()) fmt.Printf("%-20s %-18s %s\n", profile.Id.Get(), "SystemOverlay", profile.SystemOverlay.Print()) fmt.Printf("%-20s %-18s %s\n", profile.Id.Get(), "Ipxe", profile.Ipxe.Print()) - fmt.Printf("%-20s %-18s %s\n", profile.Id.Get(), "IpmiIpaddr", profile.IpmiIpaddr.Print()) fmt.Printf("%-20s %-18s %s\n", profile.Id.Get(), "IpmiNetmask", profile.IpmiNetmask.Print()) fmt.Printf("%-20s %-18s %s\n", profile.Id.Get(), "IpmiPort", profile.IpmiPort.Print()) fmt.Printf("%-20s %-18s %s\n", profile.Id.Get(), "IpmiGateway", profile.IpmiGateway.Print()) diff --git a/internal/app/wwctl/profile/list/root.go b/internal/app/wwctl/profile/list/root.go index 6c795ebb..1144d5c2 100644 --- a/internal/app/wwctl/profile/list/root.go +++ b/internal/app/wwctl/profile/list/root.go @@ -4,9 +4,10 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "list [flags] [profile pattern]...", + DisableFlagsInUseLine: true, + Use: "list [OPTIONS] [PROFILE ...]", Short: "List profiles and configurations", - Long: "This command will list and show the profile configurations.", + Long: "This command will display configurations for PROFILE.", RunE: CobraRunE, Aliases: []string{"ls"}, } diff --git a/internal/app/wwctl/profile/root.go b/internal/app/wwctl/profile/root.go index faa89044..2945d04e 100644 --- a/internal/app/wwctl/profile/root.go +++ b/internal/app/wwctl/profile/root.go @@ -10,7 +10,8 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "profile", + DisableFlagsInUseLine: true, + Use: "profile COMMAND [OPTIONS]", Short: "Node configuration profile management", Long: "Management of node profile settings", } diff --git a/internal/app/wwctl/profile/set/main.go b/internal/app/wwctl/profile/set/main.go index 2ef3b549..45f8ed61 100644 --- a/internal/app/wwctl/profile/set/main.go +++ b/internal/app/wwctl/profile/set/main.go @@ -96,12 +96,17 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } if SetIpmiNetmask != "" { - wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI username to: %s\n", p.Id.Get(), SetIpmiNetmask) + wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI netmask to: %s\n", p.Id.Get(), SetIpmiNetmask) p.IpmiNetmask.Set(SetIpmiNetmask) } + if SetIpmiPort != "" { + wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI port to: %s\n", p.Id.Get(), SetIpmiPort) + p.IpmiPort.Set(SetIpmiPort) + } + if SetIpmiGateway != "" { - wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI username to: %s\n", p.Id.Get(), SetIpmiGateway) + wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI gateway to: %s\n", p.Id.Get(), SetIpmiGateway) p.IpmiGateway.Set(SetIpmiGateway) } @@ -111,12 +116,12 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } if SetIpmiPassword != "" { - wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI username to: %s\n", p.Id.Get(), SetIpmiPassword) + wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI password to: %s\n", p.Id.Get(), SetIpmiPassword) p.IpmiPassword.Set(SetIpmiPassword) } if SetIpmiInterface != "" { - wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI username to: %s\n", p.Id.Get(), SetIpmiInterface) + wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI interface to: %s\n", p.Id.Get(), SetIpmiInterface) p.IpmiInterface.Set(SetIpmiInterface) } diff --git a/internal/app/wwctl/profile/set/root.go b/internal/app/wwctl/profile/set/root.go index 42e55403..c3f61bbc 100644 --- a/internal/app/wwctl/profile/set/root.go +++ b/internal/app/wwctl/profile/set/root.go @@ -12,9 +12,9 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "set [flags] ...", + Use: "set [OPTIONS] [PROFILE ...]", Short: "Configure node profile properties", - Long: "This command will allow you to set configuration properties for node profiles.\n\n" + + Long: "This command sets configuration properties for the node PROFILE(s).\n\n" + "Note: use the string 'UNSET' to remove a configuration", Args: cobra.MinimumNArgs(1), RunE: CobraRunE, @@ -44,6 +44,7 @@ var ( SetRuntimeOverlay string SetSystemOverlay string SetIpmiNetmask string + SetIpmiPort string SetIpmiGateway string SetIpmiUsername string SetIpmiPassword string @@ -104,10 +105,11 @@ func init() { log.Println(err) } baseCmd.PersistentFlags().StringVar(&SetIpmiNetmask, "ipminetmask", "", "Set the node's IPMI netmask") + baseCmd.PersistentFlags().StringVar(&SetIpmiPort, "ipmiport", "", "Set the node's IPMI port") baseCmd.PersistentFlags().StringVar(&SetIpmiGateway, "ipmigateway", "", "Set the node's IPMI gateway") baseCmd.PersistentFlags().StringVar(&SetIpmiUsername, "ipmiuser", "", "Set the node's IPMI username") baseCmd.PersistentFlags().StringVar(&SetIpmiPassword, "ipmipass", "", "Set the node's IPMI password") - baseCmd.PersistentFlags().StringVar(&SetIpmiInterface, "ipmiinterface", "", "Set the node's IPMI interface (defaults to 'lan' if empty)") + baseCmd.PersistentFlags().StringVar(&SetIpmiInterface, "ipmiinterface", "", "Set the node's IPMI interface (defaults to 'lan')") baseCmd.PersistentFlags().StringVarP(&SetNetName, "netname", "n", "default", "Define the network name to configure") baseCmd.PersistentFlags().StringVarP(&SetNetDev, "netdev", "N", "", "Define the network device") diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go index 840fbb6e..059b38db 100644 --- a/internal/app/wwctl/root.go +++ b/internal/app/wwctl/root.go @@ -9,7 +9,9 @@ import ( "github.com/hpcng/warewulf/internal/app/wwctl/power" "github.com/hpcng/warewulf/internal/app/wwctl/profile" "github.com/hpcng/warewulf/internal/app/wwctl/server" + "github.com/hpcng/warewulf/internal/app/wwctl/version" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/internal/pkg/help" "github.com/spf13/cobra" "github.com/spf13/cobra/doc" @@ -18,9 +20,10 @@ import ( var ( rootCmd = &cobra.Command{ - Use: "wwctl", + DisableFlagsInUseLine: true, + Use: "wwctl COMMAND [OPTIONS]", Short: "Warewulf Control", - Long: "Control interface to the Cluster Warewulf Provisioning System.", + Long: "Control interface to the Warewulf Cluster Provisioning System.", PersistentPreRunE: rootPersistentPreRunE, SilenceUsage: true, SilenceErrors: true, @@ -33,6 +36,9 @@ func init() { rootCmd.PersistentFlags().BoolVarP(&verboseArg, "verbose", "v", false, "Run with increased verbosity.") rootCmd.PersistentFlags().BoolVarP(&DebugFlag, "debug", "d", false, "Run with debugging messages enabled.") + rootCmd.SetUsageTemplate(help.UsageTemplate) + rootCmd.SetHelpTemplate(help.HelpTemplate) + rootCmd.AddCommand(overlay.GetCommand()) rootCmd.AddCommand(container.GetCommand()) rootCmd.AddCommand(node.GetCommand()) @@ -41,6 +47,7 @@ func init() { rootCmd.AddCommand(profile.GetCommand()) rootCmd.AddCommand(configure.GetCommand()) rootCmd.AddCommand(server.GetCommand()) + rootCmd.AddCommand(version.GetCommand()) } @@ -60,14 +67,17 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) error { return nil } -// GenBashCompletionFile +// 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: "MINE", + Title: "WWCTL", Section: "1", } return doc.GenManTree(rootCmd, header, fileName) diff --git a/internal/app/wwctl/server/reload/root.go b/internal/app/wwctl/server/reload/root.go index b503b746..9a880b7a 100644 --- a/internal/app/wwctl/server/reload/root.go +++ b/internal/app/wwctl/server/reload/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "reload", + DisableFlagsInUseLine: true, + Use: "reload [OPTIONS]", Short: "Reload the Warewulf server configuration", RunE: CobraRunE, } diff --git a/internal/app/wwctl/server/restart/root.go b/internal/app/wwctl/server/restart/root.go index c1538dcd..84224ed6 100644 --- a/internal/app/wwctl/server/restart/root.go +++ b/internal/app/wwctl/server/restart/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "restart", + DisableFlagsInUseLine: true, + Use: "restart [OPTIONS]", Short: "Restart the Warewulf server", RunE: CobraRunE, } diff --git a/internal/app/wwctl/server/root.go b/internal/app/wwctl/server/root.go index de865f63..22b1bce3 100644 --- a/internal/app/wwctl/server/root.go +++ b/internal/app/wwctl/server/root.go @@ -11,7 +11,8 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "server", + DisableFlagsInUseLine: true, + Use: "server COMMAND [OPTIONS]", Short: "Warewulf server process commands", Long: "This command will allow you to control the Warewulf daemon process.", } diff --git a/internal/app/wwctl/server/start/root.go b/internal/app/wwctl/server/start/root.go index 2d4ca442..be4e0621 100644 --- a/internal/app/wwctl/server/start/root.go +++ b/internal/app/wwctl/server/start/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "start", + DisableFlagsInUseLine: true, + Use: "start [OPTIONS]", Short: "Start Warewulf server", RunE: CobraRunE, } diff --git a/internal/app/wwctl/server/status/root.go b/internal/app/wwctl/server/status/root.go index d7e6270a..83a07b8c 100644 --- a/internal/app/wwctl/server/status/root.go +++ b/internal/app/wwctl/server/status/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "status", + DisableFlagsInUseLine: true, + Use: "status [OPTIONS]", Short: "Warewulf server status", RunE: CobraRunE, } diff --git a/internal/app/wwctl/server/stop/root.go b/internal/app/wwctl/server/stop/root.go index 23549d95..94df3acc 100644 --- a/internal/app/wwctl/server/stop/root.go +++ b/internal/app/wwctl/server/stop/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "stop", + DisableFlagsInUseLine: true, + Use: "stop [OPTIONS]", Short: "Stop Warewulf server", RunE: CobraRunE, } diff --git a/internal/app/wwctl/version/main.go b/internal/app/wwctl/version/main.go new file mode 100644 index 00000000..08e48d09 --- /dev/null +++ b/internal/app/wwctl/version/main.go @@ -0,0 +1,15 @@ +package version + +import ( + "fmt" + + "github.com/hpcng/warewulf/internal/pkg/version" + "github.com/spf13/cobra" +) + +func CobraRunE(cmd *cobra.Command, args []string) error { + + fmt.Println("Version:\t", version.GetVersion()) + + return nil +} diff --git a/internal/app/wwctl/version/root.go b/internal/app/wwctl/version/root.go new file mode 100644 index 00000000..766d3b8f --- /dev/null +++ b/internal/app/wwctl/version/root.go @@ -0,0 +1,22 @@ +package version + +import "github.com/spf13/cobra" + +var ( + baseCmd = &cobra.Command{ + Use: "version", + Short: "Version information", + Long: "This command will print the Warewulf version.", + RunE: CobraRunE, + Args: cobra.ExactArgs(0), + Aliases: []string{"vers"}, + } +) + +func init() { +} + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/pkg/help/help.go b/internal/pkg/help/help.go new file mode 100644 index 00000000..93726f76 --- /dev/null +++ b/internal/pkg/help/help.go @@ -0,0 +1,35 @@ +package help + +// UsageTemplate replaces the default usage template from cobra +const UsageTemplate string = `Usage: +{{if .Runnable}} {{.UseLine}} +{{end}}{{if .HasAvailableSubCommands}} {{.CommandPath}} COMMAND [OPTIONS] +{{end}}{{if gt (len .Aliases) 0}} +Aliases: + {{.NameAndAliases}} +{{end}}{{if .HasExample}} +Examples: + {{.Example}} +{{end}}{{if .HasAvailableSubCommands}} +Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}} +{{end}}{{if .HasAvailableLocalFlags}} +Options: +{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}} +{{end}}{{if .HasAvailableInheritedFlags}} +Global Options: +{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}} +{{end}}{{if .HasHelpSubCommands}} +Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} + {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}} +{{end}}{{if .HasAvailableSubCommands}} +Use "{{.CommandPath}} COMMAND --help" for more information about a COMMAND. +{{end}} +` +// End UsageTemplate + +// HelpTemplate replaces the default help template from cobra +const HelpTemplate string = ` +{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}} +{{end}} +{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}` diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index e4e2ff8d..2a09fabb 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -1,18 +1,17 @@ package node import ( + "errors" "fmt" "io/ioutil" "sort" "strings" - "github.com/pkg/errors" - "gopkg.in/yaml.v2" - "github.com/hpcng/warewulf/internal/pkg/wwlog" + "gopkg.in/yaml.v2" ) -const ConfigFile = "/etc/warewulf/nodes.conf" +var ConfigFile = "/etc/warewulf/nodes.conf" func New() (nodeYaml, error) { var ret nodeYaml @@ -271,35 +270,3 @@ func (config *nodeYaml) FindDiscoverableNode() (NodeInfo, string, error) { return ret, "", errors.New("No unconfigured nodes found") } - -func (config *nodeYaml) FindByHwaddr(hwa string) (NodeInfo, error) { - var ret NodeInfo - - n, _ := config.FindAllNodes() - - for _, node := range n { - for _, dev := range node.NetDevs { - if dev.Hwaddr.Get() == hwa { - return node, nil - } - } - } - - return ret, errors.New("No nodes found with HW Addr: " + hwa) -} - -func (config *nodeYaml) FindByIpaddr(ipaddr string) (NodeInfo, error) { - var ret NodeInfo - - n, _ := config.FindAllNodes() - - for _, node := range n { - for _, dev := range node.NetDevs { - if dev.Ipaddr.Get() == ipaddr { - return node, nil - } - } - } - - return ret, errors.New("No nodes found with IP Addr: " + ipaddr) -} diff --git a/internal/pkg/node/datastructure.go b/internal/pkg/node/datastructure.go index 5d605bf9..94df277c 100644 --- a/internal/pkg/node/datastructure.go +++ b/internal/pkg/node/datastructure.go @@ -1,9 +1,6 @@ package node import ( - "fmt" - "os" - "github.com/hpcng/warewulf/internal/pkg/util" "github.com/hpcng/warewulf/internal/pkg/wwlog" ) @@ -108,22 +105,10 @@ type NetDevEntry struct { } func init() { - //TODO: Check to make sure nodes.conf is found + // Check that nodes.conf is found if !util.IsFile(ConfigFile) { - c, err := os.OpenFile(ConfigFile, os.O_RDWR|os.O_CREATE, 0640) - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not create new configuration file: %s\n", err) - // just return silently, as init is also called for bash_completion - return - } - - fmt.Fprintf(c, "nodeprofiles:\n") - fmt.Fprintf(c, " default:\n") - fmt.Fprintf(c, " comment: This profile is automatically included for each node\n") - fmt.Fprintf(c, "nodes: {}\n") - - c.Close() - - wwlog.Printf(wwlog.INFO, "Created default node configuration\n") + wwlog.Printf(wwlog.WARN, "Missing node configuration file\n") + // just return silently, as init is also called for bash_completion + return } } diff --git a/internal/pkg/node/methods.go b/internal/pkg/node/methods.go index b2179a1d..bc5de819 100644 --- a/internal/pkg/node/methods.go +++ b/internal/pkg/node/methods.go @@ -14,7 +14,7 @@ func FilterByName(set []NodeInfo, searchList []string) []NodeInfo { if len(searchList) > 0 { for _, search := range searchList { for _, entry := range set { - b, _ := regexp.MatchString(search, entry.Id.Get()) + b, _ := regexp.MatchString("^"+search+"$", entry.Id.Get()) if b { ret = append(ret, entry) } diff --git a/internal/pkg/node/util.go b/internal/pkg/node/util.go new file mode 100644 index 00000000..4b8bb9b3 --- /dev/null +++ b/internal/pkg/node/util.go @@ -0,0 +1,47 @@ +package node + +import ( + "errors" + "net" + "strings" +) + +func (config *nodeYaml) FindByHwaddr(hwa string) (NodeInfo, error) { + if _, err := net.ParseMAC(hwa); err != nil { + return NodeInfo{}, errors.New("invalid hardware address: " + hwa) + } + + var ret NodeInfo + + n, _ := config.FindAllNodes() + + for _, node := range n { + for _, dev := range node.NetDevs { + if strings.EqualFold(dev.Hwaddr.Get(), hwa) { + return node, nil + } + } + } + + return ret, errors.New("No nodes found with HW Addr: " + hwa) +} + +func (config *nodeYaml) FindByIpaddr(ipaddr string) (NodeInfo, error) { + if net.ParseIP(ipaddr) == nil { + return NodeInfo{}, errors.New("invalid IP:" + ipaddr) + } + + var ret NodeInfo + + n, _ := config.FindAllNodes() + + for _, node := range n { + for _, dev := range node.NetDevs { + if dev.Ipaddr.Get() == ipaddr { + return node, nil + } + } + } + + return ret, errors.New("No nodes found with IP Addr: " + ipaddr) +} diff --git a/internal/pkg/node/util_test.go b/internal/pkg/node/util_test.go new file mode 100644 index 00000000..78f4127c --- /dev/null +++ b/internal/pkg/node/util_test.go @@ -0,0 +1,135 @@ +package node + +import ( + "gopkg.in/yaml.v2" + "testing" +) + +func NewTestNode() (nodeYaml, error) { + var data = ` +nodeprofiles: + default: + comment: This profile is automatically included for each node +nodes: + test_node: + profiles: + - default + network devices: + eth0: + default: true + hwaddr: 00:00:00:00:12:34 + ipaddr: 1.2.3.4 + ipcidr: "" + prefix: "" + netmask: "" + eno1: + default: true + hwaddr: ab:cd:ef:00:12:34 + ipaddr: 1.2.3.4 + ipcidr: "" + prefix: "" + netmask: "" + eno2: + default: true + hwaddr: aB:Cd:eF:12:34:56 + ipaddr: 1.2.3.4 + ipcidr: "" + prefix: "" + netmask: "" + test_node_IPv6: + profiles: + - default + network devices: + eth1: + default: false + hwaddr: "" + ipaddr: fd1a:2b3c:4d5e:06f0:1234:5678:90ab:cdef + ipcidr: "" + prefix: "" + netmask: "" +` + var ret nodeYaml + err := yaml.Unmarshal([]byte(data), &ret) + if err != nil { + return ret, err + } + return ret, nil +} + +func Test_nodeYaml_FindByHwaddr(t *testing.T) { + c, _ := NewTestNode() + //type fields struct { + // NodeProfiles map[string]*NodeConf + // Nodes map[string]*NodeConf + //} + type args struct { + hwa string + } + tests := []struct { + name string + //fields fields + config nodeYaml + args args + want string + wantErr bool + }{ + {"emptyString", c, args{hwa: ""}, "", true}, + {"noIpString", c, args{hwa: "this is not a MAC"}, "", true}, + {"intString", c, args{hwa: "4294967296"}, "", true}, + {"invalidMAC", c, args{hwa: "xx:00:00:00:12:34"}, "", true}, + {"validMACNotFound", c, args{hwa: "aa:FF:ee:65:43:21"}, "", true}, + {"validMAC", c, args{hwa: "ab:cd:ef:00:12:34"}, "test_node", false}, + {"validMAC2", c, args{hwa: "aB:Cd:eF:00:12:34"}, "test_node", false}, + {"validMAC3", c, args{hwa: "Ab:cD:Ef:12:34:56"}, "test_node", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config := tt.config + got, err := config.FindByHwaddr(tt.args.hwa) + if (err != nil) != tt.wantErr { + t.Errorf("FindByHwaddr() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !(got.Id.Get() == tt.want) { + t.Errorf("FindByHwaddr() got = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_nodeYaml_FindByIpaddr(t *testing.T) { + c, _ := NewTestNode() + type args struct { + ipaddr string + } + tests := []struct { + name string + config nodeYaml + args args + want string + wantErr bool + }{ + {"emptyString", c, args{ipaddr: ""}, "", true}, + {"noIpString", c, args{ipaddr: "this is not an IP"}, "", true}, + {"intString", c, args{ipaddr: "4294967296"}, "", true}, + {"invalidIPv4", c, args{ipaddr: "1.2.3.256"}, "", true}, + {"invalidIPv6", c, args{ipaddr: "xd1a:2b3c:4d5e:06f0:1234:5678:90ab:cdef"}, "", true}, + {"validIPv4NotFound", c, args{ipaddr: "1.1.1.1"}, "", true}, + {"validIPv6NotFound", c, args{ipaddr: "fd1a:2b3c:4d5e:06f0:1234:5678:90ab:fedc"}, "", true}, + {"validIPv4", c, args{ipaddr: "1.2.3.4"}, "test_node", false}, + {"validIPv6", c, args{ipaddr: "fd1a:2b3c:4d5e:06f0:1234:5678:90ab:cdef"}, "test_node_IPv6", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config := tt.config + got, err := config.FindByIpaddr(tt.args.ipaddr) + if (err != nil) != tt.wantErr { + t.Errorf("FindByIpaddr() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !(got.Id.Get() == tt.want) { + t.Errorf("FindByHwaddr() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/pkg/version/version.go b/internal/pkg/version/version.go new file mode 100644 index 00000000..0e8e1a5a --- /dev/null +++ b/internal/pkg/version/version.go @@ -0,0 +1,7 @@ +package version + +var Version string = "development" + +func GetVersion() string { + return Version +} diff --git a/internal/pkg/warewulfconf/datastructure.go b/internal/pkg/warewulfconf/datastructure.go index 8315ddd6..d47670ec 100644 --- a/internal/pkg/warewulfconf/datastructure.go +++ b/internal/pkg/warewulfconf/datastructure.go @@ -5,7 +5,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/wwlog" ) -const ConfigFile = "/etc/warewulf/warewulf.conf" +var ConfigFile = "/etc/warewulf/warewulf.conf" type ControllerConf struct { Comment string `yaml:"comment"` diff --git a/internal/pkg/warewulfd/daemon.go b/internal/pkg/warewulfd/daemon.go index 042808d0..acb04ac8 100644 --- a/internal/pkg/warewulfd/daemon.go +++ b/internal/pkg/warewulfd/daemon.go @@ -9,6 +9,7 @@ import ( "syscall" "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/version" "github.com/pkg/errors" ) @@ -53,7 +54,7 @@ func DaemonStart() error { fmt.Fprintf(p, "%d", pid) - fmt.Printf("Started Warewulf server at PID: %d\n", pid) + fmt.Printf("Started Warewulf (%s) server at PID: %d\n", version.GetVersion(), pid) } diff --git a/internal/pkg/warewulfd/ipxe.go b/internal/pkg/warewulfd/ipxe.go index f8a57dc9..2eec83ec 100644 --- a/internal/pkg/warewulfd/ipxe.go +++ b/internal/pkg/warewulfd/ipxe.go @@ -18,6 +18,8 @@ type iPxeTemplate struct { WaitTime string Hostname string Fqdn string + Id string + Cluster string ContainerName string Hwaddr string Ipaddr string @@ -129,6 +131,8 @@ func IpxeSend(w http.ResponseWriter, req *http.Request) { var replace iPxeTemplate + replace.Id = nodeobj.Id.Get() + replace.Cluster = nodeobj.ClusterName.Get() replace.Fqdn = nodeobj.Id.Get() replace.Ipaddr = conf.Ipaddr replace.Port = strconv.Itoa(conf.Warewulf.Port) diff --git a/warewulf.spec.in b/warewulf.spec.in index 7e771cc1..19c4a637 100644 --- a/warewulf.spec.in +++ b/warewulf.spec.in @@ -1,74 +1,107 @@ +%global wwgroup warewulf +%{!?wwshared: %global wwshared %{_localstatedir}} %define debug_package %{nil} Name: warewulf -Summary: A suite of tools for bare metal provisioning of containers +Summary: A provisioning system for large clusters of bare metal and/or virtual systems Version: @VERSION@ -Release: 1%{?dist} -License: BSD -Source: https://github.com/hpcng/warewulf/archive/refs/tags/%{name}-%{version}.tar.gz +Release: @RELEASE@%{?dist} +License: BSD-3-Clause +URL: https://github.com/hpcng/warewulf +Source: https://github.com/hpcng/warewulf/archive/refs/tags/v%{version}.tar.gz + ExclusiveOS: linux +Conflicts: warewulf < 4 +Conflicts: warewulf-common +Conflicts: warewulf-cluster +Conflicts: warewulf-vnfs +Conflicts: warewulf-provision +Conflicts: warewulf-ipmi + BuildRequires: make -BuildRequires: golang %if 0%{?rhel} BuildRequires: systemd +BuildRequires: golang +Requires: tftp-server +Requires: nfs-utils %else +# sle_version BuildRequires: systemd-rpm-macros +BuildRequires: go +Requires: tftp +Requires: nfs-kernel-server %endif -%if 0%{?rhel} > 7 +%if 0%{?rhel} >= 8 || 0%{?sle_version} Requires: dhcp-server %else +# rhel < 8 Requires: dhcp %endif -Requires: tftp-server -Requires: nfs-utils - %description -Warewulf is a bare metal container provisioning system. +Warewulf is a stateless and diskless container operating system provisioning +system for large clusters of bare metal and/or virtual systems. + %prep -%setup -q +%setup -q -n %{name}-%{version} + %build make all + %install %make_install DESTDIR=%{buildroot} %{?mflags_install} + %pre -getent group warewulf >/dev/null || groupadd -r warewulf +getent group %{wwgroup} >/dev/null || groupadd -r %{wwgroup} + %post %systemd_post warewulfd.service + %preun %systemd_preun warewulfd.service + %postun %systemd_postun_with_restart warewulfd.service -%files -%attr(0755, root, warewulf) %dir %{_sysconfdir}/%{name} -%config(noreplace) %{_sysconfdir}/%{name}/* +%files +%defattr(-, root, %{wwgroup}) +%dir %{_sysconfdir}/%{name} +%config(noreplace) %{_sysconfdir}/%{name}/* +%config(noreplace) %attr(0640,-,-) %{_sysconfdir}/%{name}/nodes.conf %{_sysconfdir}/bash_completion.d/warewulf -%{_bindir}/wwctl +%dir %{wwshared}/%{name} +%{wwshared}/%{name}/overlays/runtime/* +%{wwshared}/%{name}/overlays/system/* -%{_prefix}/lib/firewalld/services/warewulf.xml - -%{_unitdir}/warewulfd.service - -%attr(0755, root, warewulf) %dir %{_localstatedir}/%{name} -%{_localstatedir}/%{name}/overlays/runtime/* -%{_localstatedir}/%{name}/overlays/system/* - -%{_mandir}/man1/wwctl* +%attr(-, root, root) %{_bindir}/wwctl +%if 0%{?rhel} +%attr(-, root, root) %{_prefix}/lib/firewalld/services/warewulf.xml +%else +# sle_version +%attr(-, root, root) %{_libexecdir}/firewalld/services/warewulf.xml +%endif +%attr(-, root, root) %{_unitdir}/warewulfd.service +%attr(-, root, root) %{_mandir}/man1/wwctl* %changelog +* Mon Nov 1 2021 Jeremy Siadal - 4.2.0-1 +- Add support for OpenSUSE +- Update file attribs +- Update license string +- Make shared store relocatable + * Fri Sep 24 2021 Michael L. Young - 4.2.0-1 - Update spec file to use systemd macros - Use macros to refer to system paths