diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e82fe7b..cd19991e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Add support to render template using `host` or `$(uname -n)` as the value of `overlay show --render`. #623 - Added command line parameters for credentials of a container registry - Add flag `--build` to `wwctl container copy`. #1378 +- Add `wwctl clean` to remove OCI cache and overlays from deleted nodes ### Changed @@ -38,6 +39,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Remove NodeInfo (in-memory-only) data structure, consolidating onto NodeConf. #916 - Replace `defaults.conf` with settings on the default profile. #917 - Switched from yaml.v2 to yaml.v3 #1462 +- Make OCIBlobCache a seperate path and point it to `/var/cache` #1459 ### Removed diff --git a/Variables.mk b/Variables.mk index 58a895e8..be0f5db9 100644 --- a/Variables.mk +++ b/Variables.mk @@ -27,7 +27,7 @@ ifdef PREFIX endif # System directory paths -VARLIST += PREFIX BINDIR SYSCONFDIR SRVDIR DATADIR MANDIR DOCDIR LOCALSTATEDIR RELEASE +VARLIST += PREFIX BINDIR SYSCONFDIR SRVDIR DATADIR MANDIR DOCDIR LOCALSTATEDIR RELEASE CACHEDIR PREFIX ?= /usr/local BINDIR ?= $(PREFIX)/bin SYSCONFDIR ?= $(PREFIX)/etc @@ -42,6 +42,7 @@ else SRVDIR ?= $(PREFIX)/srv LOCALSTATEDIR ?= $(PREFIX)/var endif +CACHEDIR ?= $(LOCALSTATEDIR)/cache # OS-Specific Service Locations VARLIST += TFTPDIR FIREWALLDDIR SYSTEMDDIR BASHCOMPDIR LOGROTATEDIR DRACUTMODDIR diff --git a/internal/app/wwctl/clean/main.go b/internal/app/wwctl/clean/main.go new file mode 100644 index 00000000..5a1ce4d2 --- /dev/null +++ b/internal/app/wwctl/clean/main.go @@ -0,0 +1,19 @@ +package clean + +import ( + "github.com/warewulf/warewulf/internal/pkg/clean" + + "github.com/spf13/cobra" +) + +func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err error) { + return func(cmd *cobra.Command, args []string) (err error) { + if err = clean.CleanOciBlobCacheDir(); err != nil { + return err + } else if err = clean.CleanOverlays(); err != nil { + return err + } else { + return nil + } + } +} diff --git a/internal/app/wwctl/clean/main_test.go b/internal/app/wwctl/clean/main_test.go new file mode 100644 index 00000000..23d8c38b --- /dev/null +++ b/internal/app/wwctl/clean/main_test.go @@ -0,0 +1,29 @@ +package clean + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/warewulf/warewulf/internal/pkg/testenv" + "github.com/warewulf/warewulf/internal/pkg/wwlog" +) + +func Test_Clean(t *testing.T) { + wwlog.SetLogLevel(wwlog.DEBUG) + env := testenv.New(t) + env.WriteFile(t, "etc/warewulf/nodes.conf", + `WW_INTERNAL: 45 +nodeprofiles: {} +nodes: + node1: {} +`) + env.WriteFile(t, "srv/warewulf/overlays/node1/__SYSTEM__.img", "Fake System") + env.WriteFile(t, "srv/warewulf/overlays/node2/__SYSTEM__.img", "Fake System") + env.WriteFile(t, "var/cache/warewulf/test", "Nothing to see here") + baseCmd := GetCommand() + err := baseCmd.Execute() + assert.NoError(t, err) + assert.FileExists(t, env.GetPath("srv/warewulf/overlays/node1/__SYSTEM__.img")) + assert.NoFileExists(t, env.GetPath("srv/warewulf/overlays/node2/__SYSTEM__.img")) + assert.NoFileExists(t, env.GetPath("/var/cache/warewulf/test")) +} diff --git a/internal/app/wwctl/clean/root.go b/internal/app/wwctl/clean/root.go new file mode 100644 index 00000000..d6515dce --- /dev/null +++ b/internal/app/wwctl/clean/root.go @@ -0,0 +1,20 @@ +package clean + +import ( + "github.com/spf13/cobra" +) + +type variables struct { +} + +func GetCommand() *cobra.Command { + vars := variables{} + baseCmd := &cobra.Command{ + DisableFlagsInUseLine: true, + Use: "clean", + Short: "Clean up", + Long: "This command cleans the OCI cache and removes leftovers from deleted nodes", + RunE: CobraRunE(&vars), + } + return baseCmd +} diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go index 0b68398e..fb730d48 100644 --- a/internal/app/wwctl/root.go +++ b/internal/app/wwctl/root.go @@ -4,6 +4,7 @@ import ( "os" "github.com/spf13/cobra" + "github.com/warewulf/warewulf/internal/app/wwctl/clean" "github.com/warewulf/warewulf/internal/app/wwctl/configure" "github.com/warewulf/warewulf/internal/app/wwctl/container" "github.com/warewulf/warewulf/internal/app/wwctl/genconf" @@ -58,6 +59,7 @@ func init() { rootCmd.AddCommand(version.GetCommand()) rootCmd.AddCommand(ssh.GetCommand()) rootCmd.AddCommand(genconf.GetCommand()) + rootCmd.AddCommand(clean.GetCommand()) } // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/pkg/clean/clean.go b/internal/pkg/clean/clean.go new file mode 100644 index 00000000..f37e0d9d --- /dev/null +++ b/internal/pkg/clean/clean.go @@ -0,0 +1,46 @@ +package clean + +import ( + "os" + "path" + + warewulfconf "github.com/warewulf/warewulf/internal/pkg/config" + "github.com/warewulf/warewulf/internal/pkg/node" + "github.com/warewulf/warewulf/internal/pkg/util" + "github.com/warewulf/warewulf/internal/pkg/wwlog" +) + +/* +Cleans up the OCI cache and remains of deleted nodes +*/ +func CleanOciBlobCacheDir() error { + warewulfconf := warewulfconf.Get() + wwlog.Verbose("removing oci cache dir: %s", warewulfconf.Paths.OciBlobCachedir()) + return os.RemoveAll(warewulfconf.Paths.OciBlobCachedir()) +} + +func CleanOverlays() error { + warewulfconf := warewulfconf.Get() + nodeDB, err := node.New() + if err != nil { + return err + } + nodes := nodeDB.ListAllNodes() + dirList, err := os.ReadDir(warewulfconf.Paths.OverlayProvisiondir()) + if err != nil { + return err + } + for _, item := range dirList { + if !item.IsDir() { + continue + } + if !util.InSlice(nodes, item.Name()) { + wwlog.Verbose("removing overlays of delete node: %s", item.Name()) + err = os.RemoveAll(path.Join(warewulfconf.Paths.OverlayProvisiondir(), item.Name())) + if err != nil { + return err + } + } + } + return nil +} diff --git a/internal/pkg/config/buildconfig.go.in b/internal/pkg/config/buildconfig.go.in index 0b1b3195..c0368564 100644 --- a/internal/pkg/config/buildconfig.go.in +++ b/internal/pkg/config/buildconfig.go.in @@ -1,11 +1,16 @@ package config +import ( + "path" +) + var ConfigFile = "@SYSCONFDIR@/warewulf/warewulf.conf" type BuildConfig struct { Bindir string `default:"@BINDIR@"` Sysconfdir string `default:"@SYSCONFDIR@"` Localstatedir string `default:"@LOCALSTATEDIR@"` + Cachedir string `default:"@CACHEDIR@"` Ipxesource string `default:"@IPXESOURCE@"` Srvdir string `default:"@SRVDIR@"` Firewallddir string `default:"@FIREWALLDDIR@"` @@ -42,3 +47,11 @@ type WarewulfConf struct { DataStore string `yaml:"datastore" default:"@DATADIR@"` GrubBoot bool `yaml:"grubboot" default:"false"` } + +func (paths BuildConfig) OciBlobCachedir() string { + return path.Join(paths.Cachedir, "warewulf") +} + +func (paths BuildConfig) OverlayProvisiondir() string { + return path.Join(paths.WWProvisiondir, "overlays") +} diff --git a/internal/pkg/config/root_test.go b/internal/pkg/config/root_test.go index 4511e47d..7d185fdf 100644 --- a/internal/pkg/config/root_test.go +++ b/internal/pkg/config/root_test.go @@ -16,7 +16,6 @@ func TestDefaultRootConf(t *testing.T) { assert.True(t, conf.Warewulf.AutobuildOverlays) assert.True(t, conf.Warewulf.EnableHostOverlay) assert.False(t, conf.Warewulf.Syslog) - assert.NotEmpty(t, conf.Warewulf.DataStore) assert.True(t, conf.DHCP.Enabled) assert.Equal(t, "default", conf.DHCP.Template) @@ -43,7 +42,6 @@ func TestDefaultRootConf(t *testing.T) { assert.NotEmpty(t, conf.Paths.Bindir) assert.NotEmpty(t, conf.Paths.Sysconfdir) - assert.NotEmpty(t, conf.Warewulf.DataStore) assert.NotEmpty(t, conf.Paths.Localstatedir) assert.NotEmpty(t, conf.Paths.Srvdir) assert.NotEmpty(t, conf.Paths.Firewallddir) @@ -52,6 +50,7 @@ func TestDefaultRootConf(t *testing.T) { assert.NotEmpty(t, conf.Paths.WWChrootdir) assert.NotEmpty(t, conf.Paths.WWProvisiondir) assert.NotEmpty(t, conf.Paths.WWClientdir) + assert.NotEmpty(t, conf.Paths.Cachedir) } func TestInitializedFromFile(t *testing.T) { diff --git a/internal/pkg/container/imprt.go b/internal/pkg/container/imprt.go index e1ae82b0..449f32ac 100644 --- a/internal/pkg/container/imprt.go +++ b/internal/pkg/container/imprt.go @@ -16,7 +16,7 @@ import ( ) func ImportDocker(uri string, name string, sCtx *types.SystemContext) error { - OciBlobCacheDir := warewulfconf.Get().Warewulf.DataStore + "/oci" + OciBlobCacheDir := warewulfconf.Get().Paths.OciBlobCachedir() err := os.MkdirAll(OciBlobCacheDir, 0755) if err != nil { diff --git a/internal/pkg/overlay/config.go b/internal/pkg/overlay/config.go index 0437f69b..988519a3 100644 --- a/internal/pkg/overlay/config.go +++ b/internal/pkg/overlay/config.go @@ -55,5 +55,5 @@ func OverlayImage(nodeName string, context string, overlayNames []string) string } conf := warewulfconf.Get() - return path.Join(conf.Paths.WWProvisiondir, "overlays/", nodeName, name) + return path.Join(conf.Paths.OverlayProvisiondir(), nodeName, name) } diff --git a/internal/pkg/testenv/testenv.go b/internal/pkg/testenv/testenv.go index 65e3ff94..94d88a83 100644 --- a/internal/pkg/testenv/testenv.go +++ b/internal/pkg/testenv/testenv.go @@ -41,6 +41,7 @@ const Systemddir = "usr/lib/systemd/system" const WWOverlaydir = "var/lib/warewulf/overlays" const WWChrootdir = "var/lib/warewulf/chroots" const WWProvisiondir = "srv/warewulf" +const Cachedir = "var/cache" // New creates a test environment in a temporary directory and configures // Warewulf to use it. @@ -76,6 +77,7 @@ func New(t *testing.T) (env *TestEnv) { conf.Paths.WWOverlaydir = env.GetPath(WWOverlaydir) conf.Paths.WWChrootdir = env.GetPath(WWChrootdir) conf.Paths.WWProvisiondir = env.GetPath(WWProvisiondir) + conf.Paths.Cachedir = env.GetPath(Cachedir) conf.Paths.WWClientdir = "/warewulf" for _, confPath := range []string{ diff --git a/internal/pkg/warewulfd/provision_test.go b/internal/pkg/warewulfd/provision_test.go index 2f644019..ba1bc977 100644 --- a/internal/pkg/warewulfd/provision_test.go +++ b/internal/pkg/warewulfd/provision_test.go @@ -103,10 +103,10 @@ nodes: assert.NoError(t, dbErr) conf.Warewulf.Secure = false - assert.NoError(t, os.MkdirAll(path.Join(conf.Paths.WWProvisiondir, "overlays", "n1"), 0700)) - assert.NoError(t, os.WriteFile(path.Join(conf.Paths.WWProvisiondir, "overlays", "n1", "__SYSTEM__.img"), []byte("system overlay"), 0600)) - assert.NoError(t, os.WriteFile(path.Join(conf.Paths.WWProvisiondir, "overlays", "n1", "__RUNTIME__.img"), []byte("runtime overlay"), 0600)) - assert.NoError(t, os.WriteFile(path.Join(conf.Paths.WWProvisiondir, "overlays", "n1", "o1.img"), []byte("specific overlay"), 0600)) + assert.NoError(t, os.MkdirAll(path.Join(conf.Paths.OverlayProvisiondir(), "n1"), 0700)) + assert.NoError(t, os.WriteFile(path.Join(conf.Paths.OverlayProvisiondir(), "n1", "__SYSTEM__.img"), []byte("system overlay"), 0600)) + assert.NoError(t, os.WriteFile(path.Join(conf.Paths.OverlayProvisiondir(), "n1", "__RUNTIME__.img"), []byte("runtime overlay"), 0600)) + assert.NoError(t, os.WriteFile(path.Join(conf.Paths.OverlayProvisiondir(), "n1", "o1.img"), []byte("specific overlay"), 0600)) wwlog.SetLogLevel(wwlog.DEBUG) for _, tt := range provisionSendTests { diff --git a/internal/pkg/warewulfd/util_test.go b/internal/pkg/warewulfd/util_test.go index ec37bf54..dbb695c8 100644 --- a/internal/pkg/warewulfd/util_test.go +++ b/internal/pkg/warewulfd/util_test.go @@ -37,42 +37,42 @@ var getOverlayFileTests = []struct { node: "", context: "", overlays: []string{"o1", "o2"}, - result: "overlays/node1/o1-o2.img", + result: "node1/o1-o2.img", }, { description: "system overlay for a node points to the node's system overlay image", node: "node1", context: "system", overlays: []string{"o1"}, - result: "overlays/node1/__SYSTEM__.img", + result: "node1/__SYSTEM__.img", }, { description: "runtime overlay for a node points to the node's runtime overlay image", node: "node1", context: "runtime", overlays: nil, - result: "overlays/node1/__RUNTIME__.img", + result: "node1/__RUNTIME__.img", }, { description: "a specific overlay for a node points to that specific overlay image for that node", node: "node1", context: "", overlays: []string{"o1"}, - result: "overlays/node1/o1.img", + result: "node1/o1.img", }, { description: "a specific set of overlays for a node points to a combined overlay image for that node", node: "node1", context: "", overlays: []string{"o1", "o2"}, - result: "overlays/node1/o1-o2.img", + result: "node1/o1-o2.img", }, { description: "a specific set of overlays for a node while also specifying a context points to the contextual overlay image for that node", node: "node1", context: "system", overlays: []string{"o1", "o2"}, - result: "overlays/node1/__SYSTEM__.img", + result: "node1/__SYSTEM__.img", }, } @@ -96,7 +96,7 @@ nodes: result, err := getOverlayFile(nodeInfo, tt.context, tt.overlays, false) assert.NoError(t, err) if tt.result != "" { - tt.result = path.Join(conf.Paths.WWProvisiondir, tt.result) + tt.result = path.Join(conf.Paths.OverlayProvisiondir(), tt.result) } assert.Equal(t, tt.result, result) }) diff --git a/warewulf.spec.in b/warewulf.spec.in index 36c3a113..aa1e3bb3 100644 --- a/warewulf.spec.in +++ b/warewulf.spec.in @@ -128,7 +128,8 @@ make defaults \ FIREWALLDDIR=/usr/lib/firewalld/services \ WWCLIENTDIR=/warewulf \ IPXESOURCE=/usr/share/ipxe \ - DRACUTMODDIR=/usr/lib/dracut/modules.d + DRACUTMODDIR=/usr/lib/dracut/modules.d \ + CACHEDIR=%{_localstatedir}/cache make %if %{api} make api