diff --git a/internal/app/wwctl/kernel/build/root.go b/internal/app/wwctl/kernel/build/root.go index bd504f76..5bce78c6 100644 --- a/internal/app/wwctl/kernel/build/root.go +++ b/internal/app/wwctl/kernel/build/root.go @@ -19,7 +19,7 @@ var ( func init() { baseCmd.PersistentFlags().BoolVarP(&BuildAll, "all", "a", false, "Build all overlays (runtime and system)") - baseCmd.PersistentFlags().BoolVarP(&ByNode, "node", "n", false, "Build overlay for a particular node") + baseCmd.PersistentFlags().BoolVarP(&ByNode, "node", "n", false, "Build overlay for a particular node(s)") } // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/app/wwctl/vnfs/build/main.go b/internal/app/wwctl/vnfs/build/main.go new file mode 100644 index 00000000..e33a51bb --- /dev/null +++ b/internal/app/wwctl/vnfs/build/main.go @@ -0,0 +1,57 @@ +package build + +import ( + "fmt" + "github.com/hpcng/warewulf/internal/pkg/assets" + "github.com/hpcng/warewulf/internal/pkg/vnfs" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/spf13/cobra" + "os" +) + +func CobraRunE(cmd *cobra.Command, args []string) error { + var nodes []assets.NodeInfo + set := make(map[string]int) + + if len(args) == 1 && ByNode == true { + var err error + nodes, err = assets.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 { + set[node.Vnfs] ++ + } + + } else if BuildAll == true { + var err error + nodes, err = assets.FindAllNodes() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not get list of nodes: %s\n", err) + os.Exit(1) + } + + for _, node := range nodes { + set[node.Vnfs] ++ + } + + } 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 := vnfs.Build(v, BuildForce) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + } + + return nil +} \ No newline at end of file diff --git a/internal/app/wwctl/vnfs/build/root.go b/internal/app/wwctl/vnfs/build/root.go new file mode 100644 index 00000000..50dbdfdc --- /dev/null +++ b/internal/app/wwctl/vnfs/build/root.go @@ -0,0 +1,28 @@ +package build + +import "github.com/spf13/cobra" + +var ( + baseCmd = &cobra.Command{ + Use: "build (vnfs location | node search pattern)", + Short: "VNFS Image Build", + Long: "VNFS kernel images", + RunE: CobraRunE, + Args: cobra.RangeArgs(0,1), + + } + BuildForce bool + BuildAll bool + ByNode 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") + baseCmd.PersistentFlags().BoolVarP(&ByNode, "node", "n", false, "Build VNFS for a particular node(s)") +} + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return baseCmd +} \ No newline at end of file diff --git a/internal/app/wwctl/vnfs/root.go b/internal/app/wwctl/vnfs/root.go index 269a3ac6..ab32a8d6 100644 --- a/internal/app/wwctl/vnfs/root.go +++ b/internal/app/wwctl/vnfs/root.go @@ -1,7 +1,7 @@ package vnfs import ( - "fmt" + "github.com/hpcng/warewulf/internal/app/wwctl/vnfs/build" "github.com/spf13/cobra" ) @@ -10,13 +10,12 @@ var ( Use: "vnfs", Short: "VNFS image management", Long: "Virtual Node File System (VNFS) image management", - RunE: CobraRunE, } test bool ) func init() { - baseCmd.PersistentFlags().BoolVarP(&test, "test", "t", false, "Testing.") + baseCmd.AddCommand(build.GetCommand()) } @@ -25,7 +24,3 @@ func GetCommand() *cobra.Command { return baseCmd } -func CobraRunE(cmd *cobra.Command, args []string) error { - fmt.Printf("Vnfs: Hello World\n") - return nil -} \ No newline at end of file diff --git a/internal/pkg/vnfs/containerdir.go b/internal/pkg/vnfs/containerdir.go new file mode 100644 index 00000000..50668bc9 --- /dev/null +++ b/internal/pkg/vnfs/containerdir.go @@ -0,0 +1,64 @@ +package vnfs + +import ( + "fmt" + "github.com/hpcng/warewulf/internal/pkg/config" + "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "os" + "path" +) + +func BuildContainerdir(v VnfsObject, buildForce bool) { + config := config.New() + + if _, err := os.Stat(v.Source()); err != nil { + wwlog.Printf(wwlog.INFO, "%-35s: Skipping (bad path)\n", v.Name()) + return + } + + wwlog.Printf(wwlog.DEBUG, "Checking if there have been any updates to the VNFS directory\n") + if util.PathIsNewer(v.Source(), config.VnfsImage(v.NameClean())) { + if buildForce == false { + wwlog.Printf(wwlog.INFO, "%-35s: Skipping, VNFS is current\n", v.Name()) + return + } + } + + wwlog.Printf(wwlog.DEBUG, "Making the directory: %s\n", path.Dir(config.VnfsImage(v.NameClean()))) + err := os.MkdirAll(path.Dir(config.VnfsImage(v.NameClean())), 0755) + if err != nil { + fmt.Printf("ERROR: %s\n", err) + return + } + + wwlog.Printf(wwlog.DEBUG, "Making the directory: %s\n", path.Dir(config.VnfsChroot(v.NameClean()))) + err = os.MkdirAll(path.Dir(config.VnfsChroot(v.NameClean())), 0755) + if err != nil { + fmt.Printf("ERROR: %s\n", err) + return + } + + wwlog.Printf(wwlog.DEBUG, "Building VNFS image: '%s' -> '%s'\n", v.Source(), config.VnfsImage(v.NameClean())) + err = buildVnfs(v.Source(), config.VnfsImage(v.NameClean())) + if err != nil { + fmt.Printf("ERROR: %s\n", err) + os.Exit(1) + } + + // Setup links from OCI rootfs to chroot path + _ = os.Remove(config.VnfsChroot(v.NameClean()) + "-link") + err = os.Symlink(v.Source(), config.VnfsChroot(v.NameClean())+"-link") + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not create symlink for Chroot: %s\n", err) + os.Exit(1) + } + err = os.Rename(config.VnfsChroot(v.NameClean())+"-link", config.VnfsChroot(v.NameClean())) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not rename link: %s\n", err) + os.Exit(1) + } + + wwlog.Printf(wwlog.INFO, "%-35s: Done\n", v.Name()) + +} diff --git a/internal/pkg/vnfs/docker.go b/internal/pkg/vnfs/docker.go new file mode 100644 index 00000000..4965eb2a --- /dev/null +++ b/internal/pkg/vnfs/docker.go @@ -0,0 +1,101 @@ +package vnfs + +import ( + "context" + "fmt" + "github.com/hpcng/warewulf/internal/pkg/config" + "github.com/hpcng/warewulf/internal/pkg/oci" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "os" + "path" +) + + +func BuildDocker(v VnfsObject, buildForce bool) { + wwlog.Printf(wwlog.VERBOSE, "Building OCI Container: %s\n", v.Source()) + config := config.New() + + OciCacheDir := config.LocalStateDir + "/oci" + VnfsHashDir := config.LocalStateDir + "/oci/vnfs" + + c, err := oci.NewCache(oci.OptSetCachePath(OciCacheDir)) + if err != nil { + fmt.Printf("ERROR: %s\n", err) + os.Exit(1) + } + + wwlog.Printf(wwlog.VERBOSE, "Downloading OCI container layers\n") + sourcePath, err := c.Pull(context.Background(), v.Source(), nil) + if err != nil { + fmt.Printf("ERROR: %s\n", err) + os.Exit(1) + } + + hashDestination := VnfsHashDir + path.Base(sourcePath) + + name, err := os.Readlink(config.VnfsImage(v.NameClean())) + if err == nil { + if name == hashDestination && buildForce == false { + wwlog.Printf(wwlog.INFO, "%-35s: Skipping, VNFS is current\n", v.Name()) + return + } + } + + err = os.MkdirAll(VnfsHashDir, 0755) + if err != nil { + fmt.Printf("ERROR: %s\n", err) + return + } + err = os.MkdirAll(path.Dir(config.VnfsImage(v.NameClean())), 0755) + if err != nil { + fmt.Printf("ERROR: %s\n", err) + return + } + err = os.MkdirAll(path.Dir(config.VnfsChroot(v.NameClean())), 0755) + if err != nil { + fmt.Printf("ERROR: %s\n", err) + return + } + + wwlog.Printf(wwlog.VERBOSE, "Building bootable VNFS image\n") + + err = buildVnfs(sourcePath, hashDestination) + if err != nil { + fmt.Printf("ERROR: %s\n", err) + os.Exit(1) + } + + wwlog.Printf(wwlog.VERBOSE, "Finalizing Build\n") + + // Setup links from OCI image to provision path + _ = os.Remove(config.VnfsImage(v.NameClean()) + "-link") + err = os.Symlink(hashDestination, config.VnfsImage(v.NameClean())+"-link") + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not create symlink for Image: %s\n", err) + os.Exit(1) + } + err = os.Rename(config.VnfsImage(v.NameClean())+"-link", config.VnfsImage(v.NameClean())) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not rename link: %s\n", err) + os.Exit(1) + } + + // Setup links from OCI rootfs to chroot path + _ = os.Remove(config.VnfsChroot(v.NameClean()) + "-link") + err = os.Symlink(sourcePath, config.VnfsChroot(v.NameClean())+"-link") + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not create symlink for Chroot: %s\n", err) + os.Exit(1) + } + err = os.Rename(config.VnfsChroot(v.NameClean())+"-link", config.VnfsChroot(v.NameClean())) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not rename link: %s\n", err) + os.Exit(1) + } + + + wwlog.Printf(wwlog.INFO, "%-35s: Done\n", v.Name()) + + return +} + diff --git a/internal/pkg/vnfs/util.go b/internal/pkg/vnfs/util.go new file mode 100644 index 00000000..91d26a39 --- /dev/null +++ b/internal/pkg/vnfs/util.go @@ -0,0 +1,14 @@ +package vnfs + +import ( + "fmt" + "os/exec" +) + +func buildVnfs(source string, dest string) error { + cmd := fmt.Sprintf("cd %s; find . | cpio --quiet -o -H newc | gzip -c > \"%s\"", source, dest) + + err := exec.Command("/bin/sh", "-c", cmd).Run() + + return err +} diff --git a/internal/pkg/vnfs/vnfs.go b/internal/pkg/vnfs/vnfs.go index aaf360a5..146a2f0e 100644 --- a/internal/pkg/vnfs/vnfs.go +++ b/internal/pkg/vnfs/vnfs.go @@ -1,12 +1,13 @@ package vnfs import ( + "github.com/hpcng/warewulf/internal/pkg/wwlog" "path" "strings" ) type VnfsObject struct { - SourcePath string + SourcePath string } func New(s string) VnfsObject { @@ -48,4 +49,22 @@ func (self *VnfsObject) Source() string { } return self.SourcePath -} \ No newline at end of file +} + +func Build(uri string, force bool) error { + v := New(uri) + + wwlog.Printf(wwlog.VERBOSE, "Building VNFS: %s\n", uri) + if strings.HasPrefix(uri, "/") { + if strings.HasSuffix(uri, "tar.gz") { + //wwlog.Printf(wwlog.WARN, "Building VNFS from local tarball: %s\n", uri) + wwlog.Printf(wwlog.WARN, "Building VNFS from local tarball is not supported yet: %s\n", uri) + } else { + BuildContainerdir(v, force) + } + } else { + BuildDocker(v, force) + } + + return nil +}