diff --git a/README.md b/README.md index bf73582b..47b51b27 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ There are three major groups of data to provision: for all needed kernel images and then go through and build them all. The caveat is that all these kernels must be installed to your host controller node. -1. VNFS: The "VNFS" is the "Virtual Node File System", and that is the template that +1. Container/VNFS: The "VNFS" is the "Virtual Node File System", and that is the template that nodes will be provisioned to boot into. Warewulf v4 can support standard "chroot" style VNFS formats (e.g. same as Warewulf 3 and/or Singularity Sandboxes) as well as OCI (Open Container Initiative) formats which include Docker containers and containers diff --git a/internal/app/warewulfd/response/container.go b/internal/app/warewulfd/response/container.go new file mode 100644 index 00000000..da58d804 --- /dev/null +++ b/internal/app/warewulfd/response/container.go @@ -0,0 +1,34 @@ +package response + +import ( + "github.com/hpcng/warewulf/internal/pkg/container" + "log" + "net/http" +) + +func ContainerSend(w http.ResponseWriter, req *http.Request) { + + node, err := getSanity(req) + if err != nil { + w.WriteHeader(404) + log.Panicln(err) + return + } + + if node.ContainerName.Defined() == true { + containerImage := container.ImageFile(node.ContainerName.Get()) + + err = sendFile(w, containerImage, node.Id.Get()) + if err != nil { + log.Printf("ERROR1: %s\n", err) + w.WriteHeader(503) + } else { + log.Printf("SEND: %15s: %s\n", node.Id.Get(), containerImage) + } + } else { + w.WriteHeader(503) + log.Printf("ERROR: No Container set for node %s\n", node.Id.Get()) + } + + return +} diff --git a/internal/app/wwctl/container/build/main.go b/internal/app/wwctl/container/build/main.go new file mode 100644 index 00000000..81b6bb66 --- /dev/null +++ b/internal/app/wwctl/container/build/main.go @@ -0,0 +1,86 @@ +package build + +import ( + "github.com/hpcng/warewulf/internal/pkg/container" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/spf13/cobra" + "os" +) + +func CobraRunE(cmd *cobra.Command, args []string) error { + var containers []string + + if BuildAll == true { + containers, _ = container.ListSources() + } else { + containers = args + } + + for _, c := range containers { + if container.ValidSource(c) == false { + wwlog.Printf(wwlog.ERROR, "VNFS name does not exist: %s\n", c) + os.Exit(1) + } + + container.Build(c, BuildForce) + } + + /* + var nodes []node.NodeInfo + set := make(map[string]int) + + n, err := node.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err) + os.Exit(1) + } + + if len(args) == 1 && ByNode == true { + var err error + nodes, err = n.SearchByName(args[0]) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not find nodes for search term: %s\n", args[0]) + os.Exit(1) + } + + for _, node := range nodes { + if node.Vnfs.Defined() == true { + set[node.Vnfs.Get()]++ + } + } + + } else if BuildAll == true { + var err error + nodes, err = n.FindAllNodes() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not get list of nodes: %s\n", err) + os.Exit(1) + } + + for _, node := range nodes { + if node.Vnfs.Defined() == true { + wwlog.Printf(wwlog.VERBOSE, "Adding VNFS to list: %s (%s)\n", node.Vnfs.Get(), node.Id.Get()) + set[node.Vnfs.Get()]++ + } + } + + } else if len(args) == 1 { + set[args[0]]++ + } else { + cmd.Usage() + os.Exit(1) + } + + for v := range set { + fmt.Printf("Building VNFS: %s\n", v) + err := container.Build(v, BuildForce) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + } + + */ + + return nil +} diff --git a/internal/app/wwctl/container/build/root.go b/internal/app/wwctl/container/build/root.go new file mode 100644 index 00000000..f174053d --- /dev/null +++ b/internal/app/wwctl/container/build/root.go @@ -0,0 +1,25 @@ +package build + +import "github.com/spf13/cobra" + +var ( + baseCmd = &cobra.Command{ + Use: "build (container location | node search pattern)", + Short: "VNFS Image Build", + Long: "VNFS kernel images", + RunE: CobraRunE, + Args: cobra.RangeArgs(0, 1), + } + BuildForce bool + BuildAll bool +) + +func init() { + baseCmd.PersistentFlags().BoolVarP(&BuildAll, "all", "a", false, "(re)Build all VNFS images for all nodes") + baseCmd.PersistentFlags().BoolVarP(&BuildForce, "force", "f", false, "Force rebuild, even if it isn't necessary") +} + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/app/wwctl/container/list/main.go b/internal/app/wwctl/container/list/main.go new file mode 100644 index 00000000..132b118f --- /dev/null +++ b/internal/app/wwctl/container/list/main.go @@ -0,0 +1,89 @@ +package list + +import ( + "fmt" + "github.com/hpcng/warewulf/internal/pkg/container" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/spf13/cobra" + "os" +) + +func CobraRunE(cmd *cobra.Command, args []string) error { + + sources, err := container.ListSources() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + + nconfig, _ := node.New() + nodes, _ := nconfig.FindAllNodes() + nodemap := make(map[string]int) + + for _, n := range nodes { + nodemap[n.ContainerName.Get()]++ + } + + fmt.Printf("%-35s %-6s %-6s\n", "VNFS NAME", "BUILT", "NODES") + for _, source := range sources { + image := container.ImageFile(source) + + if nodemap[source] == 0 { + nodemap[source] = 0 + } + fmt.Printf("%-35s %-6t %-6d\n", source, util.IsFile(image), nodemap[source]) + + } + + /* + nconfig, _ := node.New() + nodes, _ := nconfig.FindAllNodes() + nodemap := make(map[string]int) + + for _, n := range nodes { + nodemap[n.Vnfs.Get()]++ + } + + images, _ := ioutil.ReadDir(config.VnfsImageParentDir()) + + fmt.Printf("%-38s %-16s %s\n", "VNFS Name", "VNFS SIZE(k)", "NODES") + fmt.Println(strings.Repeat("=", 80)) + + for _, file := range images { + v, err := container.Load(file.Name()) + if err == nil { + var vnfs_size int64 + if util.IsFile(config.VnfsImage(file.Name())) { + s, _ := os.Stat(config.VnfsImage(file.Name())) + vnfs_size = s.Size() / 1024 + } + + if nodemap[v.Source] > 0 { + fmt.Printf("%-38s %-16d %d\n", v.Source, vnfs_size, nodemap[v.Source]) + } else { + fmt.Printf("%-38s %-16d %d\n", v.Source, vnfs_size, 0) + } + + } + continue + + if util.IsDir(path.Join(config.VnfsImageParentDir(), file.Name())) { + var vnfs_size int64 + if util.IsFile(config.VnfsImage(file.Name())) { + s, _ := os.Stat(config.VnfsImage(file.Name())) + vnfs_size = s.Size() / 1024 + } + + if nodemap[file.Name()] > 0 { + fmt.Printf("%-38s %-16d %d\n", file.Name(), vnfs_size, nodemap[file.Name()]) + } else { + fmt.Printf("%-38s %-16d %d\n", file.Name(), vnfs_size, 0) + } + } + } + */ + + return nil +} diff --git a/internal/app/wwctl/container/list/root.go b/internal/app/wwctl/container/list/root.go new file mode 100644 index 00000000..80216bb9 --- /dev/null +++ b/internal/app/wwctl/container/list/root.go @@ -0,0 +1,25 @@ +package list + +import "github.com/spf13/cobra" + +var ( + baseCmd = &cobra.Command{ + Use: "list", + Short: "List VNFS images", + Long: "List VNFS images ", + RunE: CobraRunE, + } + SystemOverlay bool + BuildAll bool +) + +func init() { + //baseCmd.PersistentFlags().BoolVarP(&SystemOverlay, "system", "s", false, "Show System Overlays as well") + //baseCmd.PersistentFlags().BoolVarP(&BuildAll, "all", "a", false, "Build all overlays (runtime and system)") + +} + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/app/wwctl/container/pull/main.go b/internal/app/wwctl/container/pull/main.go new file mode 100644 index 00000000..f9a9919d --- /dev/null +++ b/internal/app/wwctl/container/pull/main.go @@ -0,0 +1,59 @@ +package pull + +import ( + "fmt" + "github.com/hpcng/warewulf/internal/pkg/container" + "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/spf13/cobra" + "os" + "path" +) + +func CobraRunE(cmd *cobra.Command, args []string) error { + var name string + uri := args[0] + + if len(args) == 2 { + name = args[1] + } else { + name = path.Base(uri) + fmt.Printf("Setting VNFS name: %s\n", name) + } + + if container.ValidName(name) == false { + wwlog.Printf(wwlog.ERROR, "VNFS name contains illegal characters: %s\n", name) + os.Exit(1) + } + + fullPath := container.SourceDir(name) + + if util.IsDir(fullPath) == true { + if SetForce == true { + wwlog.Printf(wwlog.WARN, "Overwriting existing VNFS\n") + err := os.RemoveAll(fullPath) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + } else if SetUpdate == true { + wwlog.Printf(wwlog.WARN, "Updating existing VNFS\n") + } else { + wwlog.Printf(wwlog.ERROR, "VNFS Name exists, specify --force, --update, or choose a different name: %s\n", name) + os.Exit(1) + } + } + + err := container.PullURI(uri, name) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not pull image: %s\n", err) + os.Exit(1) + } + + if SetBuild == true { + fmt.Printf("Building container: %s\n", name) + container.Build(name, false) + } + + return nil +} diff --git a/internal/app/wwctl/container/pull/root.go b/internal/app/wwctl/container/pull/root.go new file mode 100644 index 00000000..84ed3d04 --- /dev/null +++ b/internal/app/wwctl/container/pull/root.go @@ -0,0 +1,28 @@ +package pull + +import "github.com/spf13/cobra" + +var ( + baseCmd = &cobra.Command{ + Use: "pull", + Short: "Pull Source OCI VNFS images", + Long: "Pull Source OCI VNFS images ", + RunE: CobraRunE, + Args: cobra.MinimumNArgs(1), + } + SetForce bool + SetUpdate bool + SetBuild bool +) + +func init() { + baseCmd.PersistentFlags().BoolVarP(&SetForce, "force", "f", false, "Force overwrite of an existing container") + baseCmd.PersistentFlags().BoolVarP(&SetUpdate, "update", "u", false, "Update and overwrite an existing container") + baseCmd.PersistentFlags().BoolVarP(&SetBuild, "build", "b", false, "Build container when after pulling") + +} + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/app/wwctl/container/root.go b/internal/app/wwctl/container/root.go new file mode 100644 index 00000000..3044c66a --- /dev/null +++ b/internal/app/wwctl/container/root.go @@ -0,0 +1,29 @@ +package container + +import ( + "github.com/hpcng/warewulf/internal/app/wwctl/container/build" + "github.com/hpcng/warewulf/internal/app/wwctl/container/list" + "github.com/hpcng/warewulf/internal/app/wwctl/container/pull" + "github.com/spf13/cobra" +) + +var ( + baseCmd = &cobra.Command{ + Use: "container", + Short: "VNFS image management", + Long: "Virtual Node File System (VNFS) image management", + } + test bool +) + +func init() { + baseCmd.AddCommand(build.GetCommand()) + baseCmd.AddCommand(list.GetCommand()) + baseCmd.AddCommand(pull.GetCommand()) + +} + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return baseCmd +}