=========
Debugging
=========

Whether developing a new feature or fixing a bug, using the automated
test suite together with a debugger is a potent combination. This
guide here can't substitute for full documentation on a given
debugger; but it might help you get started debugging Warewulf.

Validating the code with vet
============================

The Warewulf ``Makefile`` includes a ``vet`` target which runs ``go
vet`` on the full codebase.

.. code-block:: console

   $ make vet


Running the full test suite
===========================

The Warewulf ``Makefile`` includes a ``test`` target which runs the
full test suite.

.. code-block:: console

   $ make test


Using delve
===========

If you have a failing test but you're having trouble tracking down
why, try using a debugger to step through the test. These instructions
use delve.


Installing delve
----------------

You can install delve as a regular user directly with Go.

.. code-block:: console

   $ go install github.com/go-delve/delve/cmd/dlv@latest

The ``dlv`` binary will be installed by default at
``$HOME/go/bin/dlv``. You can, of course, add ``$HOME/go/bin`` to your
path if you prefer.

.. code-block:: console

   $ PATH=$HOME/go/bin:$PATH


Running delve against a specific test
-------------------------------------

You can use delve to specifically run the test suite and, even more
specifically, a single failing test. In this example delve is
instructed to run the tests for Warewulf's ``node`` package, and
specifically the ``Test_GetAllNodeInfoDefaults`` test.

.. code-block:: console

   $ dlv test github.com/warewulf/warewulf/internal/pkg/node -- -test.v -test.run Test_GetAllNodeInfoDefaults
   Type 'help' for list of commands.
   (dlv) break node.Test_GetAllNodeInfoDefaults
   Breakpoint 1 set at 0x26c0d0 for github.com/warewulf/warewulf/internal/pkg/node.Test_GetAllNodeInfoDefaults() ./internal/pkg/node/nodeyaml_test.go:51

Setting a breakpoint at ``node.Test_GetAllNodeInfoDefaults`` pauses
execution once the test starts, and allows us to ``continue`` through
all the setup prior to that point.

.. code-block:: console

   (dlv) continue
   === RUN   Test_GetAllNodeInfoDefaults
   > github.com/warewulf/warewulf/internal/pkg/node.Test_GetAllNodeInfoDefaults() ./internal/pkg/node/nodeyaml_test.go:51 (hits goroutine(35):1 total:1) (PC: 0x26c0d0)
       46:		assert.Contains(t, nodeYaml.Nodes, "test_node")
       47:		assert.Equal(t, "A single node", nodeYaml.Nodes["test_node"].Comment)
       48:	}
       49:	
       50:	
   =>  51:	func Test_GetAllNodeInfoDefaults(t *testing.T) {
       52:		file, writeErr := writeTestConfigFile(`
       53:	nodes:
       54:	  test_node: {}`)
       55:		if file != nil {
       56:			defer os.Remove(file.Name())

Helpful commands from here include

``next``

  Execute the current line (marked by ``=>``) and proceed to the next
  line.

``step``

  Execute the current line (marked by ``=>``) and proceed to the next
  line, potentially moving into a function call.

``list``

  Display a contextual Go code listing, marking the next instruction.

``locals``

  Display all local variables in the current scope.

``print``

  Display (in detail) the value of a single variable from the current
  scope.

Read about other commands available within delve using the ``help``
command.


Example debugging session
-------------------------

.. code-block:: console

   $ ~/go/bin/dlv test github.com/warewulf/warewulf/internal/pkg/node -- -test.v -test.run Test_GetAllNodeInfoDefaults
   Type 'help' for list of commands.

   (dlv) break node.Test_GetAllNodeInfoDefaults
   Breakpoint 1 set at 0x26c0d0 for github.com/warewulf/warewulf/internal/pkg/node.Test_GetAllNodeInfoDefaults() ./internal/pkg/node/nodeyaml_test.go:51

   (dlv) break nodeinfo.go:417
   Breakpoint 2 set at 0x267f18 for github.com/warewulf/warewulf/internal/pkg/node.NewNodeInfo() ./internal/pkg/node/nodeinfo.go:417

   (dlv) continue
   === RUN   Test_GetAllNodeInfoDefaults
   > github.com/warewulf/warewulf/internal/pkg/node.Test_GetAllNodeInfoDefaults() ./internal/pkg/node/nodeyaml_test.go:51 (hits goroutine(19):1 total:1) (PC: 0x26c0d0)
   46:		assert.Contains(t, nodeYaml.Nodes, "test_node")
   47:		assert.Equal(t, "A single node", nodeYaml.Nodes["test_node"].Comment)
   48:	}
   49:	
   50:	
   =>  51:	func Test_GetAllNodeInfoDefaults(t *testing.T) {
   52:		file, writeErr := writeTestConfigFile(`
   53:	nodes:
   54:	  test_node: {}`)
   55:		if file != nil {
   56:			defer os.Remove(file.Name())

   (dlv) continue
   WARN   : Error reading UNDEF/warewulf/defaults.conf: open UNDEF/warewulf/defaults.conf: no such file or directory
   > github.com/warewulf/warewulf/internal/pkg/node.NewNodeInfo() ./internal/pkg/node/nodeinfo.go:417 (hits goroutine(19):1 total:1) (PC: 0x267f18)
   412:			defaultNodeConf.NetDevs = nil
   413:			nodeInfo.SetDefFrom(defaultNodeConf)
   414:		}
   415:	
   416:		// Load normal attributes
   => 417:		if nodeConf != nil {
   418:			// If no profiles are included, automatically include the
   419:			// default profile.
   420:			if len(nodeConf.Profiles) == 0 {
   421:				nodeInfo.Profiles.SetSlice([]string{"default"})
   422:			} else {

   (dlv) next
   > github.com/warewulf/warewulf/internal/pkg/node.NewNodeInfo() ./internal/pkg/node/nodeinfo.go:420 (PC: 0x267f24)
   415:	
   416:		// Load normal attributes
   417:		if nodeConf != nil {
   418:			// If no profiles are included, automatically include the
   419:			// default profile.
   => 420:			if len(nodeConf.Profiles) == 0 {
   421:				nodeInfo.Profiles.SetSlice([]string{"default"})
   422:			} else {
   423:				nodeInfo.Profiles.SetSlice(nodeConf.Profiles)
   424:			}
   425:	

   (dlv) next
   > github.com/warewulf/warewulf/internal/pkg/node.NewNodeInfo() ./internal/pkg/node/nodeinfo.go:421 (PC: 0x267f3c)
   416:		// Load normal attributes
   417:		if nodeConf != nil {
   418:			// If no profiles are included, automatically include the
   419:			// default profile.
   420:			if len(nodeConf.Profiles) == 0 {
   => 421:				nodeInfo.Profiles.SetSlice([]string{"default"})
   422:			} else {
   423:				nodeInfo.Profiles.SetSlice(nodeConf.Profiles)
   424:			}
   425:	
   426:			nodeInfo.SetFrom(nodeConf)

   (dlv) next
   > github.com/warewulf/warewulf/internal/pkg/node.NewNodeInfo() ./internal/pkg/node/nodeinfo.go:426 (PC: 0x267fec)
   421:				nodeInfo.Profiles.SetSlice([]string{"default"})
   422:			} else {
   423:				nodeInfo.Profiles.SetSlice(nodeConf.Profiles)
   424:			}
   425:	
   => 426:			nodeInfo.SetFrom(nodeConf)
   427:		}
   428:	
   429:		// Load default attributes for each NetDev
   430:		if defaultNetDevConf != nil {
   431:			for _, netdev := range nodeInfo.NetDevs {

   (dlv) next
   > github.com/warewulf/warewulf/internal/pkg/node.NewNodeInfo() ./internal/pkg/node/nodeinfo.go:430 (PC: 0x268000)
   425:	
   426:			nodeInfo.SetFrom(nodeConf)
   427:		}
   428:	
   429:		// Load default attributes for each NetDev
   => 430:		if defaultNetDevConf != nil {
   431:			for _, netdev := range nodeInfo.NetDevs {
   432:				netdev.SetDefFrom(defaultNetDevConf)
   433:			}
   434:		}
   435:	

   (dlv) print nodeInfo
   github.com/warewulf/warewulf/internal/pkg/node.NodeInfo {
   Id: github.com/warewulf/warewulf/internal/pkg/node.Entry {
   value: []string len: 1, cap: 1, [
   "test_node",
   ],
   altvalue: []string len: 0, cap: 0, nil,
   from: "",
   def: []string len: 0, cap: 0, nil,},
   Comment: github.com/warewulf/warewulf/internal/pkg/node.Entry {
   value: []string len: 0, cap: 0, nil,
   altvalue: []string len: 0, cap: 0, nil,
   from: "",
   def: []string len: 0, cap: 0, nil,},
   ClusterName: github.com/warewulf/warewulf/internal/pkg/node.Entry {
   value: []string len: 0, cap: 0, nil,
   altvalue: []string len: 0, cap: 0, nil,
   from: "",
   def: []string len: 0, cap: 0, nil,},
   ContainerName: github.com/warewulf/warewulf/internal/pkg/node.Entry {
   value: []string len: 0, cap: 0, nil,
   altvalue: []string len: 0, cap: 0, nil,
   from: "",
   def: []string len: 0, cap: 0, nil,},
   Ipxe: github.com/warewulf/warewulf/internal/pkg/node.Entry {
   value: []string len: 0, cap: 0, nil,
   altvalue: []string len: 0, cap: 0, nil,
   from: "",
   def: []string len: 1, cap: 1, ["default"],},
   RuntimeOverlay: github.com/warewulf/warewulf/internal/pkg/node.Entry {
   value: []string len: 0, cap: 0, nil,
   altvalue: []string len: 0, cap: 0, nil,
   from: "",
   def: []string len: 1, cap: 1, ["generic"],},
   SystemOverlay: github.com/warewulf/warewulf/internal/pkg/node.Entry {
   value: []string len: 0, cap: 0, nil,
   altvalue: []string len: 0, cap: 0, nil,
   from: "",
   def: []string len: 1, cap: 1, ["wwinit"],},
   Root: github.com/warewulf/warewulf/internal/pkg/node.Entry {
   value: []string len: 0, cap: 0, nil,
   altvalue: []string len: 0, cap: 0, nil,
   from: "",
   def: []string len: 1, cap: 1, [
   "initramfs",
   ],},
   Discoverable: github.com/warewulf/warewulf/internal/pkg/node.Entry {
   value: []string len: 0, cap: 0, nil,
   altvalue: []string len: 0, cap: 0, nil,
   from: "",
   def: []string len: 0, cap: 0, nil,},
   Init: github.com/warewulf/warewulf/internal/pkg/node.Entry {
   value: []string len: 0, cap: 0, nil,
   altvalue: []string len: 0, cap: 0, nil,
   from: "",
   def: []string len: 1, cap: 1, [
   "/sbin/init",
   ],},
   AssetKey: github.com/warewulf/warewulf/internal/pkg/node.Entry {
   value: []string len: 0, cap: 0, nil,
   altvalue: []string len: 0, cap: 0, nil,
   from: "",
   def: []string len: 0, cap: 0, nil,},
   Kernel: *github.com/warewulf/warewulf/internal/pkg/node.KernelEntry {
   Override: (*"github.com/warewulf/warewulf/internal/pkg/node.Entry")(0x4000158370),
   Args: (*"github.com/warewulf/warewulf/internal/pkg/node.Entry")(0x40001583c8),},
   Ipmi: *github.com/warewulf/warewulf/internal/pkg/node.IpmiEntry {
   Ipaddr: (*"github.com/warewulf/warewulf/internal/pkg/node.Entry")(0x40001b6600),
   Netmask: (*"github.com/warewulf/warewulf/internal/pkg/node.Entry")(0x40001b6658),
   Port: (*"github.com/warewulf/warewulf/internal/pkg/node.Entry")(0x40001b66b0),
   Gateway: (*"github.com/warewulf/warewulf/internal/pkg/node.Entry")(0x40001b6708),
   UserName: (*"github.com/warewulf/warewulf/internal/pkg/node.Entry")(0x40001b6760),
   Password: (*"github.com/warewulf/warewulf/internal/pkg/node.Entry")(0x40001b67b8),
   Interface: (*"github.com/warewulf/warewulf/internal/pkg/node.Entry")(0x40001b6810),
   Write: (*"github.com/warewulf/warewulf/internal/pkg/node.Entry")(0x40001b6868),
   Tags: map[string]*github.com/warewulf/warewulf/internal/pkg/node.Entry [],},
   Profiles: github.com/warewulf/warewulf/internal/pkg/node.Entry {
   value: []string len: 1, cap: 1, ["default"],
   altvalue: []string len: 0, cap: 0, nil,
   from: "",
   def: []string len: 1, cap: 1, ["default"],},
   PrimaryNetDev: github.com/warewulf/warewulf/internal/pkg/node.Entry {
   value: []string len: 0, cap: 0, nil,
   altvalue: []string len: 0, cap: 0, nil,
   from: "",
   def: []string len: 0, cap: 0, nil,},
   NetDevs: map[string]*github.com/warewulf/warewulf/internal/pkg/node.NetDevEntry [],
   Tags: map[string]*github.com/warewulf/warewulf/internal/pkg/node.Entry [],}
