Merge branch 'development' into lint-update

This commit is contained in:
Jonathon Anderson
2023-02-02 14:38:33 -07:00
committed by GitHub
92 changed files with 1029 additions and 377 deletions

View File

@@ -1,4 +1,4 @@
# wwapiclient is intended as and example grpc wwapid client.
Run wwapid to start the server
Run wwapid to start the server
```./wwapic``` will get the version.
This works with or without mTLS.

View File

@@ -33,9 +33,9 @@ curl http://localhost:9871/v1/node?nodeNames=testnode1 # this works! case sensit
curl http://localhost:9871/v1/node?nodeNames=testnode%5B1-2%5D
# node add single discoverable node
curl -d '{"nodeNames": ["testApiNode0"], "discoverable": true}' -H "Content-Type: application/json" -X POST http://localhost:9871/v1/node
curl -d '{"nodeNames": ["testApiNode0"], "discoverable": true}' -H "Content-Type: application/json" -X POST http://localhost:9871/v1/node
curl -d '{"nodeNames": ["testApiNode1"], "discoverable": true}' -H "Content-Type: application/json" -X POST http://localhost:9871/v1/node
curl -d '{"nodeNames": ["testApiNode1"], "discoverable": true}' -H "Content-Type: application/json" -X POST http://localhost:9871/v1/node
# list the node we just added
@@ -45,7 +45,7 @@ curl http://localhost:9871/v1/node?nodeNames=testApiNode0
curl -d '{"nodeNames": ["testApiNode0"], "ipmiIpAddr": "10.0.8.220", "updateMask": "ipmiIpAddr,nodeNames"}' -H "Content-Type: application/json" -X PATCH http://localhost:9871/v1/node
# Node set with post:
curl -d '{"nodeNames": ["testApiNode0"], "ipmiIpaddr": "6.7.8.9"}' -H "Content-Type: application/json" -X POST http://localhost:9871/v1/nodeset
curl -d '{"nodeNames": ["testApiNode0"], "ipmiIpaddr": "6.7.8.9"}' -H "Content-Type: application/json" -X POST http://localhost:9871/v1/nodeset
# node status
curl http://localhost:9871/v1/nodestatus

View File

@@ -1,15 +1,24 @@
package delete
import (
"fmt"
"github.com/hpcng/warewulf/internal/pkg/api/container"
"github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1"
"github.com/hpcng/warewulf/internal/pkg/api/util"
"github.com/spf13/cobra"
)
func CobraRunE(cmd *cobra.Command, args []string) (err error) {
cdp := &wwapiv1.ContainerDeleteParameter{
ContainerNames: args,
}
if !SetYes {
yes := util.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to container %s", args))
if !yes {
return
}
}
return container.ContainerDelete(cdp)
}

View File

@@ -8,10 +8,10 @@ import (
var (
baseCmd = &cobra.Command{
DisableFlagsInUseLine: true,
Use: "delete [OPTIONS] CONTAINER [...]",
Short: "Delete an imported container",
Long: "This command will delete CONTAINERs that have been imported into Warewulf.",
RunE: CobraRunE,
Use: "delete [OPTIONS] CONTAINER [...]",
Short: "Delete an imported container",
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 {
return nil, cobra.ShellCompDirectiveNoFileComp
@@ -20,10 +20,11 @@ var (
return list, cobra.ShellCompDirectiveNoFileComp
},
}
SetYes bool
)
func init() {
baseCmd.PersistentFlags().BoolVarP(&SetYes, "yes", "y", false, "Set 'yes' to all questions asked")
}
// GetRootCommand returns the root cobra.Command for the application.

View File

@@ -8,7 +8,7 @@ var (
Use: "import [OPTIONS] SOURCE [NAME]",
Short: "Import a container into Warewulf",
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. Formats
optionally renaming it to NAME. The SOURCE must be in a supported URI format. Formats
are:
* docker://registry.example.org/example:latest
* docker-daemon://example:latest

View File

@@ -40,6 +40,8 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
c.Stdout = os.Stdout
c.Stderr = os.Stderr
os.Setenv("WW_CONTAINER_SHELL", containerName)
if err := c.Run(); err != nil {
fmt.Println(err)
os.Exit(1)

View File

@@ -10,7 +10,7 @@ var (
DisableFlagsInUseLine: true,
Use: "show [OPTIONS] CONTAINER",
Short: "Show root fs dir for container",
Long: `Shows the base directory for the chroot of the given container.
Long: `Shows the base directory for the chroot of the given container.
More information about the conainer can be shown with the '-a' option.`,
RunE: CobraRunE,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {

View File

@@ -25,6 +25,9 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) {
if err != nil {
return
}
if len(nodeList) == 0 {
return
}
yes := util.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to delete %d nodes(s)", len(nodeList)))
if !yes {
return

View File

@@ -22,6 +22,7 @@ var (
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" +
"in cluster0 and cluster1.",
Aliases: []string{"nodes"},
}
)

View File

@@ -1,7 +1,10 @@
package edit
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
"path"
"path/filepath"
@@ -47,34 +50,69 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
os.Exit(1)
}
}
newFile := false
if !util.IsFile(overlayFile) && filepath.Ext(overlayFile) == ".ww" {
wwlog.Verbose("This is a new file, creating some default content")
newFile = true
w, err := os.OpenFile(overlayFile, os.O_RDWR|os.O_CREATE, os.FileMode(PermMode))
if err != nil {
wwlog.Warn("Could not create file for writing: %s", err)
}
fmt.Fprintf(w, "# This is a Warewulf Template file.\n")
fmt.Fprintf(w, "#\n")
fmt.Fprintf(w, "# This file (suffix '.ww') will be automatically rewritten without the suffix\n")
fmt.Fprintf(w, "# when the overlay is rendered for the individual nodes. Here are some examples\n")
fmt.Fprintf(w, "# of macros and logic which can be used within this file:\n")
fmt.Fprintf(w, "#\n")
fmt.Fprintf(w, "# Node FQDN = {{.Id}}\n")
fmt.Fprintf(w, "# Node Cluster = {{.ClusterName}}\n")
fmt.Fprintf(w, "# Network Config = {{.NetDevs.eth0.Ipaddr}}, {{.NetDevs.eth0.Hwaddr}}, etc.\n")
fmt.Fprintf(w, "#\n")
fmt.Fprintf(w, "# Goto the documentation pages for more information: http://www.hpcng.org/...\n")
fmt.Fprintf(w, "\n")
fmtStr := `# This is a Warewulf Template file.
#
# This file (suffix '.ww') will be automatically rewritten without the suffix
# when the overlay is rendered for the individual nodes. Here are some examples
# of macros and logic which can be used within this file:
#
# Node FQDN = {{.Id}}\n")
# Node Cluster = {{.ClusterName}}\n")
# Network Config = {{.NetDevs.eth0.Ipaddr}}, {{.NetDevs.eth0.Hwaddr}}, etc.
#
# Goto the documentation pages for more information: http://www.hpcng.org/...
# Keep the following for better reference
# This file is autogenerated by warewulf
# Host: {{.BuildHost}}
# Time: {{.BuildTime}}
# Source: {{.BuildSource}}
`
fmt.Fprint(w, fmtStr)
w.Close()
}
err := util.ExecInteractive(editor, overlayFile)
fileDesc1, err := os.OpenFile(overlayFile, os.O_RDWR, os.FileMode(PermMode))
if err != nil {
wwlog.Warn("Could not open file for editing: %s", err)
}
defer fileDesc1.Close()
_, _ = fileDesc1.Seek(0, 0)
hasher := sha256.New()
if _, err := io.Copy(hasher, fileDesc1); err != nil {
wwlog.Error("Problems getting checksum of file %s\n", err)
}
sum1 := hex.EncodeToString(hasher.Sum(nil))
fileDesc1.Close()
err = util.ExecInteractive(editor, overlayFile)
if err != nil {
wwlog.Error("Editor process existed with non-zero")
os.Exit(1)
}
fileDesc2, err := os.OpenFile(overlayFile, os.O_RDWR, os.FileMode(PermMode))
if err != nil {
wwlog.Warn("Could not open file for editing: %s", err)
}
defer fileDesc2.Close()
_, _ = fileDesc2.Seek(0, 0)
hasher.Reset()
if _, err := io.Copy(hasher, fileDesc2); err != nil {
wwlog.Error("Problems getting checksum of file %s\n", err)
}
sum2 := hex.EncodeToString(hasher.Sum(nil))
if sum1 == sum2 && newFile {
wwlog.Verbose("New template %s wasn't modified, deleting it", overlayFile)
err = os.Remove(overlayFile)
if err != nil {
wwlog.Warn("Could not delete %s", overlayFile)
}
}
return nil
}

View File

@@ -58,6 +58,7 @@ func GetCommand() *cobra.Command {
}); err != nil {
log.Println(err)
}
baseCmd.PersistentFlags().BoolVarP(&SetYes, "yes", "y", false, "Set 'yes' to all questions asked")
return baseCmd
}

View File

@@ -15,7 +15,6 @@ var (
Use: "profile COMMAND [OPTIONS]",
Short: "Node configuration profile management",
Long: "Management of node profile settings",
Aliases: []string{"nodes"},
}
)

View File

@@ -34,14 +34,14 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) {
}
if !SetYes {
var nodeCount uint
var profileCount uint
// The checks run twice in the prompt case.
// Avoiding putting in a blocking prompt in an API.
_, nodeCount, err = apiprofile.ProfileSetParameterCheck(&set, false)
_, profileCount, err = apiprofile.ProfileSetParameterCheck(&set, false)
if err != nil {
return
}
yes := util.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to modify %d nodes(s)", nodeCount))
yes := util.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to modify %d profile(s)", profileCount))
if !yes {
return
}

View File

@@ -293,14 +293,19 @@ func ContainerList() (containerInfo []*wwapiv1.ContainerInfo, err error) {
wwlog.Debug("Finding kernel version for: %s", source)
kernelVersion := container.KernelVersion(source)
creationTime, err := os.Stat(container.SourceDir(source))
var creationTime uint64
sourceStat, err := os.Stat(container.SourceDir(source))
if err != nil {
wwlog.Error("%s\n", err)
} else {
creationTime = uint64(sourceStat.ModTime().Unix())
}
modTime, err := os.Stat(container.ImageFile(source))
var modTime uint64
imageStat, err := os.Stat(container.ImageFile(source))
if err != nil {
wwlog.Error("%s\n", err)
} else {
modTime = uint64(imageStat.ModTime().Unix())
}
size, err := util.DirSize(container.SourceDir(source))
if err != nil {
@@ -309,20 +314,22 @@ func ContainerList() (containerInfo []*wwapiv1.ContainerInfo, err error) {
imgSize, err := os.Stat(container.ImageFile(source))
if err != nil {
wwlog.Error("%s\n", err)
} else {
size += imgSize.Size()
}
size += imgSize.Size()
imgSize, err = os.Stat(container.ImageFile(source) + ".gz")
if err != nil {
wwlog.Error("%s\n", err)
} else {
size += imgSize.Size()
}
size += imgSize.Size()
containerInfo = append(containerInfo, &wwapiv1.ContainerInfo{
Name: source,
NodeCount: uint32(nodemap[source]),
KernelVersion: kernelVersion,
CreateDate: uint64(creationTime.ModTime().Unix()),
ModDate: uint64(modTime.ModTime().Unix()),
CreateDate: creationTime,
ModDate: modTime,
Size: uint64(size),
})

View File

@@ -116,7 +116,11 @@ func NodeList(nodeGet *wwapiv1.GetNodeList) (nodeList wwapiv1.NodeList, err erro
for j := 0; j < netInfoType.NumField(); j++ {
netConfField, ok := netConfType.FieldByName(netInfoType.Field(j).Name)
if ok {
fieldName = netName + ":" + netConfField.Tag.Get("lopt")
if netConfField.Tag.Get("lopt") != "nettagadd" {
fieldName = netName + ":" + netConfField.Tag.Get("lopt")
} else {
fieldName = netName + ":tag"
}
} else {
fieldName = netName + ":" + netInfoType.Field(j).Name
}

View File

@@ -107,7 +107,7 @@ message NodeListResponse {
// Request a node list
message GetNodeList {
enum ListType {
Simple = 0;
Simple = 0;
Ipmi = 1;
Network = 2;
Long = 3;

View File

@@ -15,22 +15,24 @@ import (
Creates '/etc/hosts' from the host template.
*/
func Hostfile() error {
if !(util.IsFile(path.Join(overlay.OverlaySourceDir("host"), "/host/etc/hosts.ww"))) {
hostTemplate := path.Join(overlay.OverlaySourceDir("host"), "/etc/hosts.ww")
if !(util.IsFile(hostTemplate)) {
wwlog.Error("'the overlay template '/etc/hosts.ww' does not exists in 'host' overlay")
os.Exit(1)
}
var nodeInfo node.NodeInfo
tstruct := overlay.InitStruct(nodeInfo)
nodeInfo := node.NewInfo()
hostname, _ := os.Hostname()
nodeInfo.Id.Set(hostname)
tstruct := overlay.InitStruct(nodeInfo)
buffer, backupFile, writeFile, err := overlay.RenderTemplateFile(
path.Join(overlay.OverlaySourceDir("host"), "/host/etc/hosts.ww"),
hostTemplate,
tstruct)
if err != nil {
wwlog.Error("%s", err)
os.Exit(1)
}
info, err := os.Stat(path.Join(overlay.OverlaySourceDir("host"), "/host/etc/hosts.ww"))
info, err := os.Stat(hostTemplate)
if err != nil {
wwlog.Error("%s", err)
os.Exit(1)

View File

@@ -4,8 +4,8 @@ import (
"path"
"path/filepath"
"sort"
"strings"
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
)
@@ -15,11 +15,11 @@ var (
`vmlinuz`,
`vmlinux-*`,
`vmlinuz-*`,
`vmlinuz.gz` }
`vmlinuz.gz`}
kernelDirs = []string{
`/lib/modules/*/`,
`/boot/` }
`/boot/`}
)
func KernelFind(container string) string {
@@ -48,7 +48,11 @@ func KernelFind(container string) string {
for _, kernelPath := range kernelPaths {
wwlog.Debug("Checking for kernel path: %s", kernelPath)
if util.IsFile(kernelPath) {
// Only succeeds if kernelPath exists and, if a
// symlink, links to a path that also exists
kernelPath, err = filepath.EvalSymlinks(kernelPath)
if err == nil {
wwlog.Debug("found kernel: %s", kernelPath)
return kernelPath
}
}
@@ -65,5 +69,10 @@ func KernelVersion(container string) string {
return ""
}
return path.Base(path.Dir(kernel))
ret := path.Base(path.Dir(kernel))
if ret == "boot" {
ret = path.Base(kernel)
}
return strings.TrimPrefix(ret, "vmlinuz-")
}

View File

@@ -24,7 +24,7 @@ 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}}
`
// End UsageTemplate

View File

@@ -17,7 +17,7 @@ var ConfigFile string
var DefaultConfig string
// used as fallback if DefaultConfig can't be read
var FallBackConf = `
var FallBackConf = `---
defaultnode:
runtime overlay:
- generic
@@ -27,6 +27,7 @@ defaultnode:
args: quiet crashkernel=no vga=791 net.naming-scheme=v238
init: /sbin/init
root: initramfs
ipxe template: default
profiles:
- default
network devices:
@@ -78,7 +79,7 @@ func (config *NodeYaml) FindAllNodes() ([]NodeInfo, error) {
}
*/
var defConf map[string]*NodeConf
wwlog.Verbose("Opening defaults failed %s\n", DefaultConfig)
wwlog.Verbose("Opening defaults from file failed %s\n", DefaultConfig)
defData, err := os.ReadFile(DefaultConfig)
if err != nil {
wwlog.Verbose("Couldn't read DefaultConfig :%s\n", err)

View File

@@ -2,6 +2,7 @@ package node
import (
"fmt"
"reflect"
"regexp"
"strings"
@@ -200,13 +201,20 @@ func (ent *Entry) Get() string {
Get the bool value of an entry.
*/
func (ent *Entry) GetB() bool {
if len(ent.value) == 0 || ent.value[0] == "false" || ent.value[0] == "no" {
if len(ent.altvalue) == 0 || ent.altvalue[0] == "false" || ent.altvalue[0] == "no" {
return false
}
return false
if len(ent.value) > 0 {
return !(strings.ToLower(ent.value[0]) == "false" ||
strings.ToLower(ent.value[0]) == "no" ||
ent.value[0] == "0")
} else if len(ent.altvalue) > 0 {
return !(strings.ToLower(ent.altvalue[0]) == "false" ||
strings.ToLower(ent.altvalue[0]) == "no" ||
ent.altvalue[0] == "0")
} else {
return !(len(ent.def) == 0 ||
strings.ToLower(ent.def[0]) == "false" ||
strings.ToLower(ent.def[0]) == "no" ||
ent.def[0] == "0")
}
return true
}
/*
@@ -334,6 +342,32 @@ Create an empty node NodeConf
func NewConf() (nodeconf NodeConf) {
nodeconf.Ipmi = new(IpmiConf)
nodeconf.Kernel = new(KernelConf)
nodeconf.NetDevs = map[string]*NetDevs{}
nodeconf.NetDevs = make(map[string]*NetDevs)
return nodeconf
}
/*
Create an empty node NodeInfo
*/
func NewInfo() (nodeInfo NodeInfo) {
nodeInfo.Ipmi = new(IpmiEntry)
nodeInfo.Kernel = new(KernelEntry)
nodeInfo.NetDevs = make(map[string]*NetDevEntry)
return nodeInfo
}
/*
Get a entry by its name
*/
func GetByName(node interface{}, name string) (string, error) {
valEntry := reflect.ValueOf(node)
entryField := valEntry.Elem().FieldByName(name)
if entryField == (reflect.Value{}) {
return "", fmt.Errorf("couldn't find field with name: %s", name)
}
if entryField.Type() != reflect.TypeOf(Entry{}) {
return "", fmt.Errorf("field %s is not of type node.Entry", name)
}
myEntry := entryField.Interface().(Entry)
return myEntry.Get(), nil
}

View File

@@ -0,0 +1,37 @@
package node
import (
"reflect"
"testing"
)
func Test_nodeYaml_SetFrom(t *testing.T) {
c, _ := NewTestNode()
singleNodeConf := c.Nodes["test_node"]
singleNodeInfo := NewInfo()
singleNodeInfo.SetFrom(singleNodeConf)
tests := []struct {
name string
arg string
want string
wantErr bool
}{
{"Right comment", "Comment", "Node Comment", false},
{"FieldName", "comment", "NodeComment", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GetByName(&singleNodeInfo, tt.arg)
if (err != nil) != tt.wantErr {
t.Errorf("GetByName(%s,%s) error = %v, wantErr %v",
reflect.TypeOf(singleNodeConf), 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)
return
}
})
}
}

View File

@@ -28,7 +28,7 @@ func (nodeConf *NodeConf) GetFrom(nodeInfo NodeInfo) {
}
/*
Abstract function which populates a NodeConf form the given NodeInfo
Abstract function which populates a NodeConf from the given NodeInfo
via getter functions.
*/
func (nodeConf *NodeConf) getterFrom(nodeInfo NodeInfo,
@@ -72,7 +72,8 @@ func (nodeConf *NodeConf) getterFrom(nodeInfo NodeInfo,
for key, val := range entryMap {
confMap[key] = getter(val)
}
} else if nodeInfoVal.Field(i).Type().Kind() == reflect.Ptr {
} else if nodeInfoVal.Field(i).Type().Kind() == reflect.Ptr && !nodeInfoVal.Field(i).IsNil() {
// initialize the nested NodeConf structs, but only if these will be set
if confField.Addr().Elem().IsZero() {
switch confField.Addr().Elem().Type() {
case reflect.TypeOf((*KernelConf)(nil)):
@@ -164,6 +165,10 @@ func (nodeConf *NodeConf) getterFrom(nodeInfo NodeInfo,
} else if netVal.Type() == reflect.TypeOf(map[string]*Entry{}) {
entryMap := netVal.Interface().(map[string](*Entry))
confMap := netConfVal.Elem().Field(j).Interface().(map[string]string)
if confMap == nil {
confMapPtr := netConfVal.Elem().Field(j).Addr().Interface().(*map[string]string)
*confMapPtr = make(map[string]string)
}
if len(confMap) > len(entryMap) {
for confMapKey := range confMap {
foundKey := false
@@ -337,6 +342,11 @@ func (node *NodeInfo) setterFrom(n *NodeConf, nameArg string,
if node.Ipmi == nil {
node.Ipmi = new(IpmiEntry)
}
// also n could be nil
if n == nil {
myn := NewConf()
n = &myn
}
nodeInfoVal := reflect.ValueOf(node)
nodeInfoType := reflect.TypeOf(node)
nodeConfVal := reflect.ValueOf(n)
@@ -505,6 +515,10 @@ Abstract function for setting a NetDevEntry from a NetDevs
func (netDev *NetDevEntry) setterFrom(netYaml *NetDevs, nameArg string,
setter func(*Entry, string, string),
setterSlice func(*Entry, []string, string)) {
// check if netYaml is empty
if netYaml == nil {
netYaml = new(NetDevs)
}
netValues := reflect.ValueOf(netDev)
netInfoType := reflect.TypeOf(*netYaml)
netInfoVal := reflect.ValueOf(*netYaml)

View File

@@ -13,6 +13,7 @@ nodeprofiles:
comment: This profile is automatically included for each node
nodes:
test_node:
comment: Node Comment
profiles:
- default
network devices:

View File

@@ -130,8 +130,9 @@ func (p *puller) Pull(ctx context.Context, uri, dst string) (err error) {
// copy to cache location
_, err = copy.Image(ctx, policyCtx, cacheRef, srcRef, &copy.Options{
ReportWriter: os.Stdout,
SourceCtx: p.sysCtx,
ReportWriter: os.Stdout,
SourceCtx: p.sysCtx,
RemoveSignatures: true,
})
if err != nil {
return err

View File

@@ -60,8 +60,6 @@ func InitStruct(nodeInfo node.NodeInfo) TemplateStruct {
// init some convininence vars
tstruct.Id = nodeInfo.Id.Get()
tstruct.Hostname = nodeInfo.Id.Get()
tstruct.Id = nodeInfo.Id.Get()
tstruct.Hostname = nodeInfo.Id.Get()
// Backwards compatibility for templates using "Keys"
tstruct.AllNodes = allNodes
tstruct.Nfs = *controller.Nfs
@@ -83,6 +81,7 @@ func InitStruct(nodeInfo node.NodeInfo) TemplateStruct {
dt := time.Now()
tstruct.BuildTime = dt.Format("01-02-2006 15:04:05 MST")
tstruct.BuildTimeUnix = strconv.FormatInt(dt.Unix(), 10)
tstruct.NodeConf.Tags = map[string]string{}
tstruct.NodeConf.GetFrom(nodeInfo)
// FIXME: Set ipCIDR address at this point, will fail with
// invalid ipv4 addr

View File

@@ -10,6 +10,7 @@ import (
"path/filepath"
"regexp"
"strings"
"syscall"
"text/template"
"github.com/hpcng/warewulf/internal/pkg/node"
@@ -48,13 +49,13 @@ func BuildAllOverlays(nodes []node.NodeInfo) error {
wwlog.Info("Building system overlays for %s: [%s]", n.Id.Get(), strings.Join(sysOverlays, ", "))
err := BuildOverlay(n, sysOverlays)
if err != nil {
return errors.Wrapf(err, "could not build system overlays %v for nide %s", sysOverlays, n.Id.Get())
return errors.Wrapf(err, "could not build system overlays %v for node %s", sysOverlays, n.Id.Get())
}
runOverlays := n.RuntimeOverlay.GetSlice()
wwlog.Info("Building runtime overlays for %s: [%s]", n.Id.Get(), strings.Join(runOverlays, ", "))
err = BuildOverlay(n, runOverlays)
if err != nil {
return errors.Wrapf(err, "could not build runtime overlays %v for nide %s", runOverlays, n.Id.Get())
return errors.Wrapf(err, "could not build runtime overlays %v for node %s", runOverlays, n.Id.Get())
}
}
@@ -80,14 +81,11 @@ func BuildSpecificOverlays(nodes []node.NodeInfo, overlayNames []string) error {
Build overlay for the host, so no argument needs to be given
*/
func BuildHostOverlay() error {
var host node.NodeInfo
host.Kernel = new(node.KernelEntry)
host.Ipmi = new(node.IpmiEntry)
var idEntry node.Entry
host := node.NewInfo()
hostname, _ := os.Hostname()
host.Id.Set(hostname)
wwlog.Info("Building overlay for %s: host", hostname)
idEntry.Set(hostname)
host.Id = idEntry
hostdir := OverlaySourceDir("host")
stats, err := os.Stat(hostdir)
if err != nil {
@@ -198,6 +196,9 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
return errors.Errorf("overlay names contains illegal characters: %v", overlayNames)
}
// Temporarily set umask to 0000, so directories in the overlay retain permissions
defer syscall.Umask(syscall.Umask(0))
wwlog.Verbose("Processing node/overlay: %s/%s", nodeInfo.Id.Get(), strings.Join(overlayNames, "-"))
for _, overlayName := range overlayNames {
wwlog.Verbose("Building overlay %s for node %s in %s", overlayName, nodeInfo.Id.Get(), outputDir)

View File

@@ -596,10 +596,14 @@ func FileGz(
compressor, err := exec.LookPath("pigz")
if err != nil {
wwlog.Verbose("Could not locate PIGZ")
compressor = "gzip"
compressor, err = exec.LookPath("gzip")
if err != nil {
wwlog.Verbose("Could not locate GZIP")
return errors.Wrapf(err, "No compressor program for image file: %s", file_gz)
}
}
wwlog.Verbose("Using gz compressor: %s", compressor)
wwlog.Verbose("Using compressor program: %s", compressor)
proc := exec.Command(
compressor,
@@ -608,6 +612,52 @@ func FileGz(
out, err := proc.CombinedOutput()
if len(out) > 0 {
outStr := string(out[:])
if err != nil && strings.HasSuffix(compressor, "gzip") && strings.Contains(outStr, "unrecognized option") {
var gzippedFile *os.File
var gzipStderr io.ReadCloser
/* Older version of gzip, try it another way: */
wwlog.Verbose("%s does not recognize the --keep flag, trying redirected stdout", compressor)
/* Open the output file for writing: */
gzippedFile, err = os.Create(file_gz)
if err != nil {
return errors.Wrapf(err, "Unable to open compressed image file for writing: %s", file_gz)
}
/* We'll execute gzip with output to stdout and attach stdout to the compressed file we just
created:
*/
proc = exec.Command(
compressor,
"--stdout",
file )
proc.Stdout = gzippedFile
gzipStderr, err = proc.StderrPipe()
if err != nil {
return errors.Wrapf(err, "Unable to open stderr pipe for compression program: %s", compressor)
}
/* Execute the command: */
err = proc.Start()
if err != nil {
_ = proc.Wait()
gzippedFile.Close()
os.Remove(file_gz)
err = errors.Wrapf(err, "Unable to successfully execute compression program: %s", compressor)
} else {
err = proc.Wait()
gzippedFile.Close()
if err != nil {
os.Remove(file_gz)
err = errors.Wrapf(err, "Unable to successfully create compressed image file: %s", file_gz)
} else {
wwlog.Verbose("Successfully compressed image file: %s", file_gz)
}
}
out, _ = io.ReadAll(gzipStderr)
}
wwlog.Debug(string(out))
}