diff --git a/internal/app/wwctl/container/exec/child/main.go b/internal/app/wwctl/container/exec/child/main.go index bf32ad17..9b62b507 100644 --- a/internal/app/wwctl/container/exec/child/main.go +++ b/internal/app/wwctl/container/exec/child/main.go @@ -21,7 +21,7 @@ import ( "github.com/spf13/cobra" ) -func CobraRunE(cmd *cobra.Command, args []string) error { +func CobraRunE(cmd *cobra.Command, args []string) (err error) { if os.Getpid() != 1 { wwlog.Error("PID is not 1: %d", os.Getpid()) os.Exit(1) @@ -33,10 +33,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { wwlog.Error("Unknown Warewulf container: %s", containerName) os.Exit(1) } - conf, err := warewulfconf.New() - if err != nil { - wwlog.Verbose("Couldn't get warewulf ocnfiguration: %s", err) - } + conf := warewulfconf.New() mountPts := conf.MountsContainer mountPts = append(container.InitMountPnts(binds), mountPts...) // check for valid mount points diff --git a/internal/app/wwctl/node/status/main.go b/internal/app/wwctl/node/status/main.go index 1999627f..5d8cdef7 100644 --- a/internal/app/wwctl/node/status/main.go +++ b/internal/app/wwctl/node/status/main.go @@ -2,7 +2,6 @@ package nodestatus import ( "fmt" - "os" "sort" "strings" "time" @@ -19,15 +18,11 @@ import ( func CobraRunE(cmd *cobra.Command, args []string) (err error) { - controller, err := warewulfconf.New() - if err != nil { - wwlog.Error("%s", err) - os.Exit(1) - } + controller := warewulfconf.New() if controller.Ipaddr == "" { - wwlog.Error("The Warewulf Server IP Address is not properly configured") - os.Exit(1) + return fmt.Errorf("warewulf Server IP Address is not properly configured") + } for { diff --git a/internal/app/wwctl/overlay/build/main.go b/internal/app/wwctl/overlay/build/main.go index 30babd08..556833c4 100644 --- a/internal/app/wwctl/overlay/build/main.go +++ b/internal/app/wwctl/overlay/build/main.go @@ -14,11 +14,7 @@ import ( ) func CobraRunE(cmd *cobra.Command, args []string) error { - controller, err := warewulfconf.New() - if err != nil { - wwlog.Error("%s", err) - os.Exit(1) - } + controller := warewulfconf.New() nodeDB, err := node.New() if err != nil { wwlog.Error("Could not open node configuration: %s", err) diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go index 9c746c79..f40e859d 100644 --- a/internal/app/wwctl/root.go +++ b/internal/app/wwctl/root.go @@ -1,6 +1,8 @@ package wwctl import ( + "os" + "github.com/hpcng/warewulf/internal/app/wwctl/configure" "github.com/hpcng/warewulf/internal/app/wwctl/container" "github.com/hpcng/warewulf/internal/app/wwctl/genconf" @@ -72,6 +74,11 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) error { if LogLevel != wwlog.INFO { wwlog.SetLogLevel(LogLevel) } - warewulfconf.ConfigFile = WarewulfConfArg + conf := warewulfconf.New() + if WarewulfConfArg != "" { + conf.ReadConf(WarewulfConfArg) + } else if os.Getenv("WAREWULFCONF") != "" { + conf.ReadConf(os.Getenv("WAREWULFCONF")) + } return nil } diff --git a/internal/app/wwctl/server/start/main.go b/internal/app/wwctl/server/start/main.go index b1954a46..00429177 100644 --- a/internal/app/wwctl/server/start/main.go +++ b/internal/app/wwctl/server/start/main.go @@ -10,10 +10,7 @@ import ( func CobraRunE(cmd *cobra.Command, args []string) error { if SetForeground { - conf, err := warewulfconf.New() - if err != nil { - return errors.Wrap(err, "Could not read Warewulf configuration file") - } + conf := warewulfconf.New() conf.Warewulf.Syslog = false return errors.Wrap(warewulfd.RunServer(), "failed to start Warewulf server") } else { diff --git a/internal/pkg/api/node/node.go b/internal/pkg/api/node/node.go index 47cc4c0b..69ad38ff 100644 --- a/internal/pkg/api/node/node.go +++ b/internal/pkg/api/node/node.go @@ -290,11 +290,7 @@ func NodeStatus(nodeNames []string) (nodeStatusResponse *wwapiv1.NodeStatusRespo Nodes map[string]*nodeStatusInternal `json:"nodes"` } - controller, err := warewulfconf.New() - if err != nil { - wwlog.Error("%s", err) - return - } + controller := warewulfconf.New() if controller.Ipaddr == "" { err = fmt.Errorf("the Warewulf Server IP Address is not properly configured") diff --git a/internal/pkg/configure/dhcp.go b/internal/pkg/configure/dhcp.go index 356dbefe..4d2f42df 100644 --- a/internal/pkg/configure/dhcp.go +++ b/internal/pkg/configure/dhcp.go @@ -15,13 +15,9 @@ import ( Configures the dhcpd server, when show is set to false, else the dhcp configuration is checked. */ -func Dhcp() error { +func Dhcp() (err error) { - controller, err := warewulfconf.New() - if err != nil { - wwlog.Error("%s", err) - os.Exit(1) - } + controller := warewulfconf.New() if !controller.Dhcp.Enabled { wwlog.Info("This system is not configured as a Warewulf DHCP controller") @@ -51,5 +47,5 @@ func Dhcp() error { return errors.Wrap(err, "failed to start") } - return nil + return } diff --git a/internal/pkg/configure/nfs.go b/internal/pkg/configure/nfs.go index 5a8f8158..d7e18469 100644 --- a/internal/pkg/configure/nfs.go +++ b/internal/pkg/configure/nfs.go @@ -2,7 +2,6 @@ package configure import ( "fmt" - "os" "github.com/hpcng/warewulf/internal/pkg/overlay" "github.com/hpcng/warewulf/internal/pkg/util" @@ -17,18 +16,11 @@ nfs server. */ func NFS() error { - controller, err := warewulfconf.New() - if err != nil { - wwlog.Error("%s", err) - os.Exit(1) - } + controller := warewulfconf.New() if controller.Nfs.Enabled { - if err != nil { - fmt.Println(err) - } if controller.Warewulf.EnableHostOverlay { - err = overlay.BuildHostOverlay() + err := overlay.BuildHostOverlay() if err != nil { wwlog.Warn("host overlay could not be built: %s", err) } diff --git a/internal/pkg/configure/tftp.go b/internal/pkg/configure/tftp.go index 9e71f67a..808852f2 100644 --- a/internal/pkg/configure/tftp.go +++ b/internal/pkg/configure/tftp.go @@ -13,13 +13,9 @@ import ( func TFTP() error { var tftpdir string = path.Join(buildconfig.TFTPDIR(), "warewulf") - controller, err := warewulfconf.New() - if err != nil { - wwlog.Error("%s", err) - return err - } + controller := warewulfconf.New() - err = os.MkdirAll(tftpdir, 0755) + err := os.MkdirAll(tftpdir, 0755) if err != nil { wwlog.Error("%s", err) return err diff --git a/internal/pkg/overlay/datastructure.go b/internal/pkg/overlay/datastructure.go index eb8004cf..f02bdedc 100644 --- a/internal/pkg/overlay/datastructure.go +++ b/internal/pkg/overlay/datastructure.go @@ -43,11 +43,7 @@ Initialize an TemplateStruct with the given node.NodeInfo */ func InitStruct(nodeInfo node.NodeInfo) TemplateStruct { var tstruct TemplateStruct - controller, err := warewulfconf.New() - if err != nil { - wwlog.Error("%s", err) - os.Exit(1) - } + controller := warewulfconf.New() nodeDB, err := node.New() if err != nil { wwlog.Error("%s", err) diff --git a/internal/pkg/warewulfconf/constructors.go b/internal/pkg/warewulfconf/constructors.go index f956bcd0..231749c2 100644 --- a/internal/pkg/warewulfconf/constructors.go +++ b/internal/pkg/warewulfconf/constructors.go @@ -11,7 +11,6 @@ import ( "github.com/creasty/defaults" "github.com/hpcng/warewulf/internal/pkg/buildconfig" "github.com/hpcng/warewulf/internal/pkg/wwlog" - "gopkg.in/yaml.v2" ) @@ -31,91 +30,107 @@ func init() { } } -func New() (ControllerConf, error) { - var ret ControllerConf - var warewulfconf WarewulfConf - var dhpdconf DhcpConf - var tftpconf TftpConf - var nfsConf NfsConf - ret.Warewulf = &warewulfconf - ret.Dhcp = &dhpdconf - ret.Tftp = &tftpconf - ret.Nfs = &nfsConf - err := defaults.Set(&ret) - // ipxe binaries are merged not overwritten, store defaults separate - defIpxe := make(map[string]string) - for k, v := range ret.Tftp.IpxeBinaries { - defIpxe[k] = v - delete(ret.Tftp.IpxeBinaries, k) - } - if err != nil { - wwlog.Error("Could initialize default variables") - return ret, err - } - // Check if cached config is old before re-reading config file +/* +Creates a new empty ControllerConf object, returns a cached +one if called in a nother context. +*/ +func New() (ret ControllerConf) { + // NOTE: This function can be called before any log level is set + // so using wwlog.Verbose or wwlog.Debug won't work if !cachedConf.current { - wwlog.Debug("Opening Warewulf configuration file: %s", ConfigFile) - data, err := os.ReadFile(ConfigFile) + ret.Warewulf = new(WarewulfConf) + ret.Dhcp = new(DhcpConf) + ret.Tftp = new(TftpConf) + ret.Nfs = new(NfsConf) + err := defaults.Set(&ret) if err != nil { - wwlog.Warn("Error reading Warewulf configuration file, falling back on defaults") + ret.setDynamicDefaults() } - wwlog.Debug("Unmarshaling the Warewulf configuration") - err = yaml.Unmarshal(data, &ret) - if err != nil { - return ret, err - } - if len(ret.Tftp.IpxeBinaries) == 0 { - ret.Tftp.IpxeBinaries = defIpxe - } - if ret.Ipaddr == "" || ret.Netmask == "" { - conn, error := net.Dial("udp", "8.8.8.8:80") - if error != nil { - return ret, err - } - defer conn.Close() - localIp := conn.LocalAddr().(*net.UDPAddr) - if ret.Ipaddr == "" { - ret.Ipaddr = localIp.IP.String() - wwlog.Verbose("IP address is not configured in warewulfd.conf, using %s", ret.Ipaddr) - } - if ret.Netmask == "" { - mask := localIp.IP.DefaultMask() - ret.Netmask = fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]) - wwlog.Verbose("Netmask address is not configured in warewulfd.conf, using %s", ret.Netmask) - } - } - - if ret.Network == "" { - mask := net.IPMask(net.ParseIP(ret.Netmask).To4()) - size, _ := mask.Size() - - sub := ipsubnet.SubnetCalculator(ret.Ipaddr, size) - - ret.Network = sub.GetNetworkPortion() - } - // check validity of ipv6 net - if ret.Ipaddr6 != "" { - _, ipv6net, err := net.ParseCIDR(ret.Ipaddr6) - if err != nil { - wwlog.Error("Invalid ipv6 address specified, mut be CIDR notation: %s", ret.Ipaddr6) - return ret, errors.New("invalid ipv6 network") - } - if msize, _ := ipv6net.Mask.Size(); msize > 64 { - wwlog.Error("ipv6 mask size must be smaller than 64") - return ret, errors.New("invalid ipv6 network size") - } - } - - wwlog.Debug("Returning warewulf config object") cachedConf = ret cachedConf.current = true } else { - wwlog.Debug("Returning cached warewulf config object") // If cached struct isn't empty, use it as the return value ret = cachedConf } - - return ret, nil + return ret +} + +/* +Populate the configuration with the values from the configuration file. +*/ +func (conf *ControllerConf) ReadConf(confFileName string) (err error) { + fileHandle, err := os.ReadFile(confFileName) + if err != nil { + return err + } + return conf.Read(fileHandle) +} + +/* +Populate the configuration with the values from the given yaml information +*/ +func (conf *ControllerConf) Read(data []byte) (err error) { + // ipxe binaries are merged not overwritten, store defaults separate + defIpxe := make(map[string]string) + for k, v := range conf.Tftp.IpxeBinaries { + defIpxe[k] = v + delete(conf.Tftp.IpxeBinaries, k) + } + err = yaml.Unmarshal(data, &conf) + if err != nil { + return + } + if len(conf.Tftp.IpxeBinaries) == 0 { + conf.Tftp.IpxeBinaries = defIpxe + } + cachedConf = *conf + cachedConf.current = true + return +} + +/* +Set the runtime defaults like IP address of running system to the config +*/ +func (ret *ControllerConf) setDynamicDefaults() (err error) { + if ret.Ipaddr == "" || ret.Netmask == "" { + conn, error := net.Dial("udp", "8.8.8.8:80") + if error != nil { + return err + } + defer conn.Close() + localIp := conn.LocalAddr().(*net.UDPAddr) + if ret.Ipaddr == "" { + ret.Ipaddr = localIp.IP.String() + wwlog.Verbose("IP address is not configured in warewulfd.conf, using %s", ret.Ipaddr) + } + if ret.Netmask == "" { + mask := localIp.IP.DefaultMask() + ret.Netmask = fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]) + wwlog.Verbose("Netmask address is not configured in warewulfd.conf, using %s", ret.Netmask) + } + } + + if ret.Network == "" { + mask := net.IPMask(net.ParseIP(ret.Netmask).To4()) + size, _ := mask.Size() + + sub := ipsubnet.SubnetCalculator(ret.Ipaddr, size) + + ret.Network = sub.GetNetworkPortion() + } + // check validity of ipv6 net + if ret.Ipaddr6 != "" { + _, ipv6net, err := net.ParseCIDR(ret.Ipaddr6) + if err != nil { + wwlog.Error("Invalid ipv6 address specified, mut be CIDR notation: %s", ret.Ipaddr6) + return errors.New("invalid ipv6 network") + } + if msize, _ := ipv6net.Mask.Size(); msize > 64 { + wwlog.Error("ipv6 mask size must be smaller than 64") + return errors.New("invalid ipv6 network size") + } + } + return } diff --git a/internal/pkg/warewulfconf/datastructure.go b/internal/pkg/warewulfconf/datastructure.go index 38443883..ad0921a1 100644 --- a/internal/pkg/warewulfconf/datastructure.go +++ b/internal/pkg/warewulfconf/datastructure.go @@ -2,7 +2,6 @@ package warewulfconf import ( "github.com/creasty/defaults" - "github.com/hpcng/warewulf/internal/pkg/wwlog" ) type ControllerConf struct { @@ -78,15 +77,9 @@ func (s *NfsConf) Unmarshal(unmarshal func(interface{}) error) error { return nil } -func init() { - _, err := New() - if err != nil { - wwlog.Warn("Could not get any Warewulf configuration: %s", err) - } -} - // Waste processor cycles to make code more readable func DataStore() string { + _ = New() return cachedConf.Warewulf.DataStore } diff --git a/internal/pkg/warewulfd/daemon.go b/internal/pkg/warewulfd/daemon.go index 2ed57ed4..99407d23 100644 --- a/internal/pkg/warewulfd/daemon.go +++ b/internal/pkg/warewulfd/daemon.go @@ -44,10 +44,7 @@ func DaemonInitLogging() error { wwlog.SetLogLevel(wwlog.SERV) } - conf, err := warewulfconf.New() - if err != nil { - return errors.Wrap(err, "Could not read Warewulf configuration file") - } + conf := warewulfconf.New() if conf.Warewulf.Syslog { diff --git a/internal/pkg/warewulfd/provision.go b/internal/pkg/warewulfd/provision.go index a2abe4dd..67d23ac1 100644 --- a/internal/pkg/warewulfd/provision.go +++ b/internal/pkg/warewulfd/provision.go @@ -31,12 +31,7 @@ type iPxeTemplate struct { } func ProvisionSend(w http.ResponseWriter, req *http.Request) { - conf, err := warewulfconf.New() - if err != nil { - wwlog.Error("Could not open Warewulf configuration: %s", err) - w.WriteHeader(http.StatusServiceUnavailable) - return - } + conf := warewulfconf.New() rinfo, err := parseReq(req) if err != nil { @@ -45,7 +40,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { return } - wwlog.Recv("hwaddr: %s, ipaddr: %s, stage: %s", rinfo.hwaddr, req.RemoteAddr, rinfo.stage ) + wwlog.Recv("hwaddr: %s, ipaddr: %s, stage: %s", rinfo.hwaddr, req.RemoteAddr, rinfo.stage) if rinfo.stage == "runtime" && conf.Warewulf.Secure { if rinfo.remoteport >= 1024 { @@ -56,11 +51,11 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { } status_stages := map[string]string{ - "ipxe": "IPXE", - "kernel": "KERNEL", - "kmods": "KMODS_OVERLAY", - "system": "SYSTEM_OVERLAY", - "runtime": "RUNTIME_OVERLAY" } + "ipxe": "IPXE", + "kernel": "KERNEL", + "kmods": "KMODS_OVERLAY", + "system": "SYSTEM_OVERLAY", + "runtime": "RUNTIME_OVERLAY"} status_stage := status_stages[rinfo.stage] var stage_overlays []string @@ -87,24 +82,24 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { if rinfo.stage == "ipxe" { stage_file = path.Join(buildconfig.SYSCONFDIR(), "/warewulf/ipxe/unconfigured.ipxe") tmpl_data = iPxeTemplate{ - Hwaddr : rinfo.hwaddr } + Hwaddr: rinfo.hwaddr} } - }else if rinfo.stage == "ipxe" { + } else if rinfo.stage == "ipxe" { stage_file = path.Join(buildconfig.SYSCONFDIR(), "warewulf/ipxe/"+node.Ipxe.Get()+".ipxe") tmpl_data = iPxeTemplate{ - Id : node.Id.Get(), - Cluster : node.ClusterName.Get(), - Fqdn : node.Id.Get(), - Ipaddr : conf.Ipaddr, - Port : strconv.Itoa(conf.Warewulf.Port), - Hostname : node.Id.Get(), - Hwaddr : rinfo.hwaddr, - ContainerName : node.ContainerName.Get(), - KernelArgs : node.Kernel.Args.Get(), - KernelOverride : node.Kernel.Override.Get() } + Id: node.Id.Get(), + Cluster: node.ClusterName.Get(), + Fqdn: node.Id.Get(), + Ipaddr: conf.Ipaddr, + Port: strconv.Itoa(conf.Warewulf.Port), + Hostname: node.Id.Get(), + Hwaddr: rinfo.hwaddr, + ContainerName: node.ContainerName.Get(), + KernelArgs: node.Kernel.Args.Get(), + KernelOverride: node.Kernel.Override.Get()} - }else if rinfo.stage == "kernel" { + } else if rinfo.stage == "kernel" { if node.Kernel.Override.Defined() { stage_file = kernel.KernelImage(node.Kernel.Override.Get()) } else if node.ContainerName.Defined() { @@ -117,28 +112,28 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { wwlog.Warn("No kernel version set for node %s", node.Id.Get()) } - }else if rinfo.stage == "kmods" { + } else if rinfo.stage == "kmods" { if node.Kernel.Override.Defined() { stage_file = kernel.KmodsImage(node.Kernel.Override.Get()) - }else{ + } else { wwlog.Warn("No kernel override modules set for node %s", node.Id.Get()) } - }else if rinfo.stage == "container" { + } else if rinfo.stage == "container" { if node.ContainerName.Defined() { stage_file = container.ImageFile(node.ContainerName.Get()) } else { wwlog.Warn("No container set for node %s", node.Id.Get()) } - }else if rinfo.stage == "system" { + } else if rinfo.stage == "system" { if len(node.SystemOverlay.GetSlice()) != 0 { stage_overlays = node.SystemOverlay.GetSlice() } else { wwlog.Warn("No system overlay set for node %s", node.Id.Get()) } - }else if rinfo.stage == "runtime" { + } else if rinfo.stage == "runtime" { if rinfo.overlay != "" { stage_overlays = []string{rinfo.overlay} } else if len(node.RuntimeOverlay.GetSlice()) != 0 { @@ -153,7 +148,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { stage_file, err = getOverlayFile( node.Id.Get(), stage_overlays, - conf.Warewulf.AutobuildOverlays ) + conf.Warewulf.AutobuildOverlays) if err != nil { w.WriteHeader(http.StatusInternalServerError) @@ -162,7 +157,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { } } - wwlog.Serv("stage_file '%s'", stage_file ) + wwlog.Serv("stage_file '%s'", stage_file) if util.IsFile(stage_file) { @@ -200,7 +195,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { wwlog.Send("%15s: %s", node.Id.Get(), stage_file) - }else{ + } else { if rinfo.compress == "gz" { stage_file += ".gz" @@ -210,7 +205,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { w.WriteHeader(http.StatusNotFound) return } - }else if rinfo.compress != "" { + } else if rinfo.compress != "" { wwlog.Error("unsupported %s compressed version of file %s", rinfo.compress, stage_file) w.WriteHeader(http.StatusNotFound) @@ -225,14 +220,14 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { updateStatus(node.Id.Get(), status_stage, path.Base(stage_file), rinfo.ipaddr) - }else if stage_file == "" { + } else if stage_file == "" { w.WriteHeader(http.StatusBadRequest) wwlog.Error("No resource selected") updateStatus(node.Id.Get(), status_stage, "BAD_REQUEST", rinfo.ipaddr) - }else{ + } else { w.WriteHeader(http.StatusNotFound) - wwlog.Error("Not found: %s", stage_file ) + wwlog.Error("Not found: %s", stage_file) updateStatus(node.Id.Get(), status_stage, "NOT_FOUND", rinfo.ipaddr) } diff --git a/internal/pkg/warewulfd/warewulfd.go b/internal/pkg/warewulfd/warewulfd.go index 5ecf48b6..d69aa210 100644 --- a/internal/pkg/warewulfd/warewulfd.go +++ b/internal/pkg/warewulfd/warewulfd.go @@ -58,10 +58,7 @@ func RunServer() error { http.HandleFunc("/overlay-runtime/", ProvisionSend) http.HandleFunc("/status", StatusSend) - conf, err := warewulfconf.New() - if err != nil { - return errors.Wrap(err, "could not get Warewulf configuration") - } + conf := warewulfconf.New() daemonPort := conf.Warewulf.Port wwlog.Serv("Starting HTTPD REST service on port %d", daemonPort)