diff --git a/CHANGELOG.md b/CHANGELOG.md index 0592ef9a..70280717 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed a bug where profile tags were erroneously overridden by empty node values. #884 - Fixed bug where tags from profiles weren't rendered #967 +- Fixed a bug when using `wwctl container import --force` to replace an existing container + will generate an error #474 ### Changed @@ -119,6 +121,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 binaries not provided by warewulf - use distrubtion ipxe binaries in the rpm - Allow absolute iPXE paths in warewulf.conf +- Support importing containers with symlinked `/bin/sh` #797 +- Don't panic on malformed passwd #527 ## [4.4.0] 2023-01-18 diff --git a/internal/app/wwctl/container/imprt/root.go b/internal/app/wwctl/container/imprt/root.go index edafc127..3465fe94 100644 --- a/internal/app/wwctl/container/imprt/root.go +++ b/internal/app/wwctl/container/imprt/root.go @@ -1,6 +1,9 @@ package imprt -import "github.com/spf13/cobra" +import ( + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/spf13/cobra" +) var ( baseCmd = &cobra.Command{ @@ -19,6 +22,11 @@ Imported containers are used to create bootable VNFS images.`, Example: "wwctl container import docker://ghcr.io/hpcng/warewulf-rockylinux:8 rockylinux-8", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), + PreRun: func(cmd *cobra.Command, args []string) { + if SetForce && SetUpdate { + wwlog.Warn("Both --force and --update flags are set, will ignore --update flag") + } + }, } SetForce bool SetUpdate bool diff --git a/internal/pkg/api/container/container.go b/internal/pkg/api/container/container.go index ca5bf82a..1942aed8 100644 --- a/internal/pkg/api/container/container.go +++ b/internal/pkg/api/container/container.go @@ -175,21 +175,23 @@ func ContainerImport(cip *wwapiv1.ContainerImportParameter) (containerName strin containerName = cip.Name fullPath := container.SourceDir(cip.Name) + // container already exists and should be removed first + if util.IsDir(fullPath) && cip.Force { + wwlog.Info("Overwriting existing VNFS") + err = os.RemoveAll(fullPath) + if err != nil { + wwlog.ErrorExc(err, "") + return + } + } + if util.IsDir(fullPath) { - if cip.Force { - wwlog.Info("Overwriting existing VNFS") - err = os.RemoveAll(fullPath) - if err != nil { - wwlog.ErrorExc(err, "") - return - } - } else if cip.Update { - wwlog.Info("Updating existing VNFS") - } else { + if !cip.Update { err = fmt.Errorf("VNFS Name exists, specify --force, --update, or choose a different name: %s", cip.Name) wwlog.Error(err.Error()) return } + wwlog.Info("Updating existing VNFS") } else if strings.HasPrefix(cip.Source, "docker://") || strings.HasPrefix(cip.Source, "docker-daemon://") || strings.HasPrefix(cip.Source, "file://") || util.IsFile(cip.Source) { var sCtx *types.SystemContext @@ -238,12 +240,14 @@ func ContainerImport(cip *wwapiv1.ContainerImportParameter) (containerName strin } } - wwlog.Info("Building container: %s", cip.Name) - err = container.Build(cip.Name, true) - if err != nil { - err = fmt.Errorf("could not build container %s: %s", cip.Name, err.Error()) - wwlog.Error(err.Error()) - return + if cip.Build { + wwlog.Info("Building container: %s", cip.Name) + err = container.Build(cip.Name, true) + if err != nil { + err = fmt.Errorf("could not build container %s: %s", cip.Name, err.Error()) + wwlog.Error(err.Error()) + return + } } if cip.Default { diff --git a/internal/pkg/container/syncuids.go b/internal/pkg/container/syncuids.go index 529b2e86..484147bf 100644 --- a/internal/pkg/container/syncuids.go +++ b/internal/pkg/container/syncuids.go @@ -113,8 +113,8 @@ func (db syncDB) checkConflicts() error { for nameInHost, hostIds := range db { if hostIds.HostID == containerIds.ContainerID { errorMsg := fmt.Sprintf("id(%v) collision: host(%s) container(%s)", containerIds.ContainerID, nameInHost, nameInContainer) - wwlog.Error(errorMsg) - wwlog.Error("add %s to host to resolve conflict", nameInContainer) + wwlog.Warn(errorMsg) + wwlog.Warn("add %s to host to resolve conflict", nameInContainer) return errors.New(errorMsg) } } @@ -167,7 +167,10 @@ func (db syncDB) read(fileName string, fromContainer bool) error { for fileScanner.Scan() { line := fileScanner.Text() fields := strings.Split(line, ":") - + if len(fields) != 7 { + wwlog.Debug("malformed line in passwd: %s", line) + continue + } name := fields[0] if name == "" { continue diff --git a/internal/pkg/container/syncuids_test.go b/internal/pkg/container/syncuids_test.go index 70951b92..abaad68a 100644 --- a/internal/pkg/container/syncuids_test.go +++ b/internal/pkg/container/syncuids_test.go @@ -258,3 +258,13 @@ func Test_differ(t *testing.T) { assert.False(t, entry.match()) assert.True(t, entry.differ()) } + +func Test_malformed_passwd(t *testing.T) { + hostInput := `"testuser1:x:1001:1001::/home/testuser:/bin/bash" + asdf` + hostFileName := writeTempFile(t, hostInput) + defer os.Remove(hostFileName) + db := make(syncDB) + err := db.readFromHost(hostFileName) + assert.NoError(t, err) +} diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index 3b4109a4..08c27b19 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -40,7 +40,10 @@ defaultnode: netmask: 255.255.255.0 onboot: true` -func init() { +/* +Creates a new nodeDb object from the on-disk configuration +*/ +func New() (NodeYaml, error) { conf := warewulfconf.Get() if ConfigFile == "" { ConfigFile = path.Join(conf.Paths.Sysconfdir, "warewulf/nodes.conf") @@ -48,12 +51,6 @@ func init() { if DefaultConfig == "" { DefaultConfig = path.Join(conf.Paths.Datadir, "warewulf/defaults.conf") } -} - -/* -Creates a new nodeDb object from the on-disk configuration -*/ -func New() (NodeYaml, error) { wwlog.Verbose("Opening node configuration file: %s", ConfigFile) data, err := os.ReadFile(ConfigFile) if err != nil { diff --git a/internal/pkg/util/util.go b/internal/pkg/util/util.go index 6ad445cb..2381e77b 100644 --- a/internal/pkg/util/util.go +++ b/internal/pkg/util/util.go @@ -177,7 +177,7 @@ func IsFile(path string) bool { return false } - if stat, err := os.Stat(path); err == nil && !stat.IsDir() { + if stat, err := os.Lstat(path); err == nil && !stat.IsDir() { return true } return false diff --git a/userdocs/index.rst b/userdocs/index.rst index f18504d1..71fb9da5 100644 --- a/userdocs/index.rst +++ b/userdocs/index.rst @@ -35,6 +35,8 @@ Welcome to the Warewulf User Guide! EL8 (Rocky Linux and RHEL) EL9 (Rocky Linux and RHEL) openSUSE Leap and SLES 15 + Debian 12 + .. toctree:: :maxdepth: 2 diff --git a/userdocs/quickstart/debian12.rst b/userdocs/quickstart/debian12.rst new file mode 100644 index 00000000..51800c3c --- /dev/null +++ b/userdocs/quickstart/debian12.rst @@ -0,0 +1,210 @@ +===================================== +Debian 12 Quickstart +===================================== + + +Install the basic services +========================== + +.. code-block:: bash + + sudo apt install firewalld nfs-kernel-server tftpd-hpa isc-dhcp-server + + +.. note:: + + If you get an error message concerning *isc-dhcp-server.service* you + probably need to configure the network intarface that isc-dhcp-server + will listen to. Run ``sudo dpkg-reconfigure isc-dhcp-server`` and enter + the name of your cluster's private network interface (e.g. enp2s0). After that, you might also need to run ``sudo systemctl enable isc-dhcp-server``. + + + +Install Warewulf and dependencies +================================= + + +.. code-block:: bash + + sudo apt install build-essential curl unzip + + sudo apt install git golang libnfs-utils libgpgme-dev libassuan-dev + + + mkdir ~/git + cd ~/git + git clone https://github.com/hpcng/warewulf.git + cd warewulf + git checkout main # or switch to a tag like 'v4.4.0' + make all && sudo make install + + + +Configure firewalld +=================== + +Restart firewalld to register the added service file, add the service +to the default zone, and reload. + +.. code-block:: bash + + sudo systemctl restart firewalld + sudo firewall-cmd --permanent --add-service warewulf + sudo firewall-cmd --permanent --add-service nfs + sudo firewall-cmd --permanent --add-service tftp + sudo firewall-cmd --reload + +Configure the controller +======================== + +Edit the file ``/etc/warewulf/warewulf.conf`` and ensure that you've +set the appropriate configuration parameters. Here are some of the +defaults for reference assuming that ``192.168.200.1`` is the IP +address of your cluster's private network interface: + +.. code-block:: yaml + + WW_INTERNAL: 43 + ipaddr: 192.168.200.1 + netmask: 255.255.255.0 + network: 192.168.200.0 + warewulf: + port: 9873 + secure: false + update interval: 60 + autobuild overlays: true + host overlay: true + syslog: false + dhcp: + enabled: true + range start: 192.168.200.50 + range end: 192.168.200.99 + systemd name: isc-dhcp-server + tftp: + enabled: true + systemd name: tftpd-hpa + nfs: + enabled: true + export paths: + - path: /home + export options: rw,sync + mount options: defaults + mount: true + - path: /opt + export options: ro,sync,no_root_squash + mount options: defaults + mount: false + systemd name: nfs-server + +.. note:: + + The DHCP range ends at ``192.168.200.99`` and as you will see + below, the first node static IP address (post boot) is configured + to ``192.168.200.100``. + +Start and enable the Warewulf service +===================================== + +.. code-block:: bash + + # Start and enable the warewulfd service + sudo systemctl enable --now warewulfd + +Configure system services automatically +======================================= + +There are a number of services and configurations that Warewulf relies +on to operate. If you wish to configure all services, you can do so +individually (omitting the ``--all``) will print a help and usage +instructions. + +.. code-block:: bash + + sudo wwctl configure --all + +.. note:: + + If you just installed the system fresh and have SELinux enforcing, + you may need to reboot the system at this stage to properly set the + contexts of the TFTP contents. After rebooting, you might also need + to run ``$ sudo restorecon -Rv /var/lib/tftpboot/`` if there are + errors with TFTP still. + +Pull and build the VNFS container (including the kernel) +======================================================== + +This will pull a basic VNFS container from Docker Hub and import the +default running kernel from the controller node and set both in the +"default" node profile. + +.. code-block:: bash + + wwctl container import docker://ghcr.io/hpcng/warewulf-debian:12.0 debian-12.0 + + +Set up the default node profile +=============================== + +Node configurations can be set via node profiles. Each node by default +is configured to be part of the ``default`` node profile, so any +changes you make to that profile will affect all nodes. + +The following command will set the container we just imported above to +the ``default`` node profile: + +.. code-block:: bash + + sudo wwctl profile set --yes --container debian-12.0 "default" + + +Next we set some default networking configurations for the first +ethernet device. On modern Linux distributions, the name of the device +is not critical, as it will be setup according to the HW +address. Because all nodes will share the netmask and gateway +configuration, we can set them in the default profile as follows: + +.. code-block:: bash + + sudo wwctl profile set --yes --netdev eth0 --netmask 255.255.255.0 --gateway 192.168.200.1 "default" + +Once those configurations have been set, you can view the changes by +listing the profiles as follows: + +.. code-block:: bash + + sudo wwctl profile list -a + +Add a node +========== + +Adding nodes can be done while setting configurations in one +command. Here we are setting the IP address of ``eth0`` and setting +this node to be discoverable, which will then automatically have the +HW address added to the configuration as the node boots. + +Node names must be unique. If you have node groups and/or multiple +clusters, designate them using dot notation. + +Note that the full node configuration comes from both cascading +profiles and node configurations which always supersede profile +configurations. + +.. code-block:: bash + + sudo wwctl node add n0000.cluster --ipaddr 192.168.200.100 --discoverable true + +At this point you can view the basic configuration of this node by +typing the following: + +.. code-block:: bash + + sudo wwctl node list -a n0000.cluster + +To make node changes effective, it is a good practice to update warewulf +overlays with the following command: + +.. code-block:: bash + + sudo wwctl overlay build + +Now, turn on your compute node and watch it boot!