- Benchmarks overlay build in an environment with 1000 nodes - Updates testenv to support both benchmarks and tests To execute: ``` go test github.com/warewulf/warewulf/internal/app/wwctl/overlay/build -bench Benchmark_Overlay_Build ``` Signed-off-by: Jonathon Anderson <janderson@ciq.com>
361 lines
11 KiB
Go
361 lines
11 KiB
Go
package host
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/warewulf/warewulf/internal/app/wwctl/overlay/show"
|
|
"github.com/warewulf/warewulf/internal/pkg/config"
|
|
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
|
)
|
|
|
|
func Test_hostOverlay(t *testing.T) {
|
|
hostname, _ := os.Hostname()
|
|
env := testenv.New(t)
|
|
defer env.RemoveAll()
|
|
env.ImportFile("etc/warewulf/nodes.conf", "nodes.conf")
|
|
env.ImportFile("var/lib/warewulf/overlays/host/rootfs/etc/dhcp/dhcpd.conf.ww", "../rootfs/etc/dhcp/dhcpd.conf.ww")
|
|
env.ImportFile("var/lib/warewulf/overlays/host/rootfs/etc/dnsmasq.d/ww4-hosts.conf.ww", "../rootfs/etc/dnsmasq.d/ww4-hosts.conf.ww")
|
|
env.ImportFile("var/lib/warewulf/overlays/host/rootfs/etc/exports.ww", "../rootfs/etc/exports.ww")
|
|
env.ImportFile("var/lib/warewulf/overlays/host/rootfs/etc/hosts.ww", "../rootfs/etc/hosts.ww")
|
|
env.ImportFile("var/lib/warewulf/overlays/host/rootfs/etc/profile.d/ssh_setup.csh.ww", "../rootfs/etc/profile.d/ssh_setup.csh.ww")
|
|
env.ImportFile("var/lib/warewulf/overlays/host/rootfs/etc/profile.d/ssh_setup.sh.ww", "../rootfs/etc/profile.d/ssh_setup.sh.ww")
|
|
|
|
tests := []struct {
|
|
name string
|
|
conf string
|
|
args []string
|
|
log string
|
|
header string
|
|
}{
|
|
{
|
|
name: "host:dhcp",
|
|
conf: "warewulf.conf",
|
|
args: []string{"--render", "host", "host", "etc/dhcp/dhcpd.conf.ww"},
|
|
log: host_dhcp,
|
|
header: "",
|
|
},
|
|
{
|
|
name: "host:dhcp(static)",
|
|
conf: "warewulf.conf-static",
|
|
args: []string{"--render", "host", "host", "etc/dhcp/dhcpd.conf.ww"},
|
|
log: host_dhcp_static,
|
|
header: "",
|
|
},
|
|
{
|
|
name: "host:dnsmasq",
|
|
conf: "warewulf.conf",
|
|
args: []string{"--render", "host", "host", "etc/dnsmasq.d/ww4-hosts.conf.ww"},
|
|
log: host_dnsmasq,
|
|
header: "",
|
|
},
|
|
{
|
|
name: "host:/etc/exports",
|
|
conf: "",
|
|
args: []string{"--render", "host", "host", "etc/exports.ww"},
|
|
log: host_exports,
|
|
header: "",
|
|
},
|
|
{
|
|
name: "host:/etc/hosts",
|
|
args: []string{"--render", "host", "host", "etc/hosts.ww"},
|
|
log: host_hosts,
|
|
header: "# Do not edit after this line",
|
|
},
|
|
{
|
|
name: "host:ssh_setup.csh",
|
|
conf: "",
|
|
args: []string{"--render", "host", "host", "etc/profile.d/ssh_setup.csh.ww"},
|
|
log: host_ssh_setup_csh,
|
|
header: "",
|
|
},
|
|
{
|
|
name: "host:ssh_setup.sh",
|
|
conf: "",
|
|
args: []string{"--render", "host", "host", "etc/profile.d/ssh_setup.sh.ww"},
|
|
log: host_ssh_setup_sh,
|
|
header: "",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.conf != "" {
|
|
env.ImportFile("etc/warewulf/warewulf.conf", tt.conf)
|
|
assert.NoError(t, config.Get().Read(env.GetPath("etc/warewulf/warewulf.conf")))
|
|
}
|
|
cmd := show.GetCommand()
|
|
cmd.SetArgs(tt.args)
|
|
stdout := bytes.NewBufferString("")
|
|
stderr := bytes.NewBufferString("")
|
|
logbuf := bytes.NewBufferString("")
|
|
cmd.SetOut(stdout)
|
|
cmd.SetErr(stderr)
|
|
wwlog.SetLogWriter(logbuf)
|
|
err := cmd.Execute()
|
|
assert.NoError(t, err)
|
|
assert.Empty(t, stdout.String())
|
|
assert.Empty(t, stderr.String())
|
|
var log string
|
|
if tt.header != "" {
|
|
log = skipHeader(t, logbuf.String(), tt.header)
|
|
} else {
|
|
log = logbuf.String()
|
|
}
|
|
assert.Equal(t, strings.Replace(tt.log, "%HOSTNAME%", hostname, -1), log)
|
|
})
|
|
}
|
|
}
|
|
|
|
func skipHeader(t *testing.T, log string, header string) (newlog string) {
|
|
newlog = ""
|
|
found := false
|
|
for _, line := range strings.SplitAfter(log, "\n") {
|
|
if strings.Contains(line, header) {
|
|
found = true
|
|
}
|
|
if found || strings.HasPrefix(line, "backupFile:") || strings.HasPrefix(line, "writeFile:") || strings.HasPrefix(line, "Filename:") {
|
|
newlog = newlog + line
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
const host_dhcp string = `backupFile: true
|
|
writeFile: true
|
|
Filename: etc/dhcp/dhcpd.conf
|
|
# This file is autogenerated by warewulf
|
|
|
|
allow booting;
|
|
allow bootp;
|
|
ddns-update-style interim;
|
|
authoritative;
|
|
|
|
option space ipxe;
|
|
|
|
# Tell iPXE to not wait for ProxyDHCP requests to speed up boot.
|
|
option ipxe.no-pxedhcp code 176 = unsigned integer 8;
|
|
option ipxe.no-pxedhcp 1;
|
|
|
|
option space PXE;
|
|
option PXE.mtftp-ip code 1 = ip-address;
|
|
option PXE.mtftp-cport code 2 = unsigned integer 16;
|
|
option PXE.mtftp-sport code 3 = unsigned integer 16;
|
|
option PXE.mtftp-tmout code 4 = unsigned integer 8;
|
|
option PXE.mtftp-delay code 5 = unsigned integer 8;
|
|
|
|
option architecture-type code 93 = unsigned integer 16;
|
|
if exists user-class and option user-class = "iPXE" {
|
|
filename "http://192.168.0.1:9873/ipxe/${mac:hexhyp}?assetkey=${asset}&uuid=${uuid}";
|
|
} else {
|
|
|
|
if option architecture-type = 00:00 {
|
|
filename "/warewulf/undionly.kpxe";
|
|
}
|
|
if option architecture-type = 00:07 {
|
|
filename "/warewulf/ipxe-snponly-x86_64.efi";
|
|
}
|
|
if option architecture-type = 00:09 {
|
|
filename "/warewulf/ipxe-snponly-x86_64.efi";
|
|
}
|
|
if option architecture-type = 00:0B {
|
|
filename "/warewulf/snponly.efi";
|
|
}
|
|
}
|
|
|
|
subnet 192.168.0.0 netmask 255.255.255.0 {
|
|
max-lease-time 120;
|
|
range 192.168.0.100 192.168.0.199;
|
|
next-server 192.168.0.1;
|
|
}
|
|
`
|
|
|
|
const host_dhcp_static string = `backupFile: true
|
|
writeFile: true
|
|
Filename: etc/dhcp/dhcpd.conf
|
|
# This file is autogenerated by warewulf
|
|
|
|
allow booting;
|
|
allow bootp;
|
|
ddns-update-style interim;
|
|
authoritative;
|
|
|
|
option space ipxe;
|
|
|
|
# Tell iPXE to not wait for ProxyDHCP requests to speed up boot.
|
|
option ipxe.no-pxedhcp code 176 = unsigned integer 8;
|
|
option ipxe.no-pxedhcp 1;
|
|
|
|
option space PXE;
|
|
option PXE.mtftp-ip code 1 = ip-address;
|
|
option PXE.mtftp-cport code 2 = unsigned integer 16;
|
|
option PXE.mtftp-sport code 3 = unsigned integer 16;
|
|
option PXE.mtftp-tmout code 4 = unsigned integer 8;
|
|
option PXE.mtftp-delay code 5 = unsigned integer 8;
|
|
|
|
option architecture-type code 93 = unsigned integer 16;
|
|
if exists user-class and option user-class = "iPXE" {
|
|
filename "http://192.168.0.1:9873/ipxe/${mac:hexhyp}?assetkey=${asset}&uuid=${uuid}";
|
|
} else {
|
|
|
|
if option architecture-type = 00:00 {
|
|
filename "/warewulf/undionly.kpxe";
|
|
}
|
|
if option architecture-type = 00:07 {
|
|
filename "/warewulf/ipxe-snponly-x86_64.efi";
|
|
}
|
|
if option architecture-type = 00:09 {
|
|
filename "/warewulf/ipxe-snponly-x86_64.efi";
|
|
}
|
|
if option architecture-type = 00:0B {
|
|
filename "/warewulf/snponly.efi";
|
|
}
|
|
}
|
|
|
|
subnet 192.168.0.0 netmask 255.255.255.0 {
|
|
max-lease-time 120;
|
|
range 192.168.0.100 192.168.0.199;
|
|
next-server 192.168.0.1;
|
|
}
|
|
host node1-default
|
|
{
|
|
hardware ethernet e6:92:39:49:7b:03;
|
|
fixed-address 192.168.3.21;
|
|
option host-name "node1";
|
|
}
|
|
|
|
host node1-secondary
|
|
{
|
|
hardware ethernet 9a:77:29:73:14:f1;
|
|
fixed-address 192.168.3.22;
|
|
}
|
|
|
|
|
|
host node2-default
|
|
{
|
|
hardware ethernet e6:92:39:49:7b:04;
|
|
fixed-address 192.168.3.23;
|
|
option host-name "node2";
|
|
}
|
|
|
|
|
|
|
|
`
|
|
|
|
const host_dnsmasq string = `backupFile: false
|
|
writeFile: true
|
|
Filename: etc/dnsmasq.d/ww4-hosts.conf
|
|
# This file was autgenerated by warewulf
|
|
|
|
# select the x86 hosts which will get the iXPE binary
|
|
dhcp-match=set:bios,option:client-arch,0 #legacy boot
|
|
dhcp-match=set:x86PC,option:client-arch, 7 #EFI x86-64
|
|
dhcp-match=set:x86PC,option:client-arch, 6 #EFI x86-64
|
|
dhcp-match=set:x86PC,option:client-arch, 9 #EFI x86-64
|
|
dhcp-match=set:aarch64,option:client-arch, 11 #EFI aarch64
|
|
dhcp-match=set:iPXE,77,"iPXE"
|
|
dhcp-userclass=set:iPXE,iPXE
|
|
dhcp-vendorclass=set:efi-http,HTTPClient:Arch:00016
|
|
dhcp-option-force=tag:efi-http,60,HTTPClient
|
|
# for http boot always use shim/grub
|
|
dhcp-boot=tag:efi-http,"http://192.168.0.1:9873/efiboot/shim.efi"
|
|
dhcp-boot=tag:x86PC,"/warewulf/ipxe-snponly-x86_64.efi"
|
|
dhcp-boot=tag:aarch64,"/warewulf/arm64-efi/snponly.efi"
|
|
# iPXE binary will get the following configuration file
|
|
dhcp-boot=tag:iPXE,"http://192.168.0.1:9873/ipxe/${mac:hexhyp}?assetkey=${asset}&uuid=${uuid}"
|
|
dhcp-no-override
|
|
# define the the range
|
|
dhcp-range=192.168.0.100,192.168.0.199,255.255.255.0,6h
|
|
dhcp-host=e6:92:39:49:7b:03,set:warewulf,node1,192.168.3.21,infinite
|
|
dhcp-host=9a:77:29:73:14:f1,set:warewulf,node1,192.168.3.22,infinite
|
|
dhcp-host=e6:92:39:49:7b:04,set:warewulf,node2,192.168.3.23,infinite
|
|
`
|
|
|
|
const host_exports string = `backupFile: true
|
|
writeFile: true
|
|
Filename: etc/exports
|
|
|
|
# This file is autogenerated by warewulf
|
|
/home 192.168.0.0/255.255.255.0(rw,sync)
|
|
/opt 192.168.0.0/255.255.255.0(ro,sync,no_root_squash)
|
|
|
|
`
|
|
|
|
const host_hosts string = `backupFile: true
|
|
writeFile: true
|
|
Filename: etc/hosts
|
|
# Do not edit after this line
|
|
# This block is autogenerated by warewulf
|
|
|
|
|
|
# Warewulf Server
|
|
192.168.0.1 %HOSTNAME% warewulf
|
|
# Entry for node1
|
|
192.168.3.21 node1 node1-default node1-wwnet0
|
|
192.168.3.22 node1-secondary node1-wwnet1
|
|
# Entry for node2
|
|
192.168.3.23 node2 node2-default node2-wwnet0
|
|
`
|
|
|
|
const host_ssh_setup_csh = `backupFile: true
|
|
writeFile: true
|
|
Filename: etc/profile.d/ssh_setup.csh
|
|
#!/bin/csh
|
|
|
|
## Automatically configure SSH keys for a user on C SHell login
|
|
## Copy this file to /etc/profile.d along with ssh_setup.sh
|
|
|
|
` + "set _UID=`id -u`" + `
|
|
if ( ( $_UID > 500 || $_UID == 0 ) && ( ! -f "$HOME/.ssh/config" && ! -f "$HOME/.ssh/cluster" ) ) then
|
|
echo "Configuring SSH for cluster access"
|
|
install -d -m 700 $HOME/.ssh
|
|
ssh-keygen -t rsa -f $HOME/.ssh/cluster -N '' -C "Warewulf Cluster key" >& /dev/null
|
|
cat $HOME/.ssh/cluster.pub >>! $HOME/.ssh/authorized_keys
|
|
chmod 0600 $HOME/.ssh/authorized_keys
|
|
|
|
touch $HOME/.ssh/config
|
|
echo -n "# Added by Warewulf " >>! $HOME/.ssh/config
|
|
(date +%Y-%m-%d >> $HOME/.ssh/config) >& /dev/null
|
|
echo "Host *" >> $HOME/.ssh/config
|
|
echo " IdentityFile ~/.ssh/cluster" >> $HOME/.ssh/config
|
|
echo " StrictHostKeyChecking=no" >> $HOME/.ssh/config
|
|
chmod 0600 $HOME/.ssh/config
|
|
endif
|
|
`
|
|
|
|
const host_ssh_setup_sh = `backupFile: true
|
|
writeFile: true
|
|
Filename: etc/profile.d/ssh_setup.sh
|
|
#!/bin/sh
|
|
##
|
|
## Copyright (c) 2001-2003 Gregory M. Kurtzer
|
|
##
|
|
## Copyright (c) 2003-2012, The Regents of the University of California,
|
|
## through Lawrence Berkeley National Laboratory (subject to receipt of any
|
|
## required approvals from the U.S. Dept. of Energy). All rights reserved.
|
|
##
|
|
## Copied from https://github.com/warewulf/warewulf3/blob/master/cluster/bin/cluster-env
|
|
|
|
## Automatically configure SSH keys for a user on login
|
|
## Copy this file to /etc/profile.d
|
|
|
|
` + "_UID=`id -u`" + `
|
|
if [ $_UID -ge 500 -o $_UID -eq 0 ] && [ ! -f "$HOME/.ssh/config" -a ! -f "$HOME/.ssh/cluster" ]; then
|
|
echo "Configuring SSH for cluster access"
|
|
install -d -m 700 $HOME/.ssh
|
|
ssh-keygen -t rsa -f $HOME/.ssh/cluster -N '' -C "Warewulf Cluster key" > /dev/null 2>&1
|
|
cat $HOME/.ssh/cluster.pub >> $HOME/.ssh/authorized_keys
|
|
chmod 0600 $HOME/.ssh/authorized_keys
|
|
|
|
echo "# Added by Warewulf ` + "`date +%Y-%m-%d 2>/dev/null`" + `" >> $HOME/.ssh/config
|
|
echo "Host *" >> $HOME/.ssh/config
|
|
echo " IdentityFile ~/.ssh/cluster" >> $HOME/.ssh/config
|
|
echo " StrictHostKeyChecking=no" >> $HOME/.ssh/config
|
|
chmod 0600 $HOME/.ssh/config
|
|
fi
|
|
`
|