Skip to content

Commit

Permalink
kvm/qemu mounting (#598)
Browse files Browse the repository at this point in the history
* add create empty volume cmd

* add list volume and delete volume cmd

* add attach volume cmd and mount on run

`ops volume attach` lets user attach volume to a stopped instance.
as of this commit, `ops run` overwrite this config though.

`ops run --mounts` lets user mount volume to an instance on run, but
does not persist the config. as of this commit, changes made to volume
during run is visible, but gets discarded on subsequent run without
`--skipbuild`

* mount non-empty volume on run

* clean up
  • Loading branch information
ronaudinho authored Sep 22, 2020
1 parent 5c64d11 commit 7ebc83e
Show file tree
Hide file tree
Showing 11 changed files with 774 additions and 12 deletions.
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ func GetRootCommand() *cobra.Command {
rootCmd.AddCommand(LoadCommand())
rootCmd.AddCommand(InstanceCommands())
rootCmd.AddCommand(ImageCommands())
rootCmd.AddCommand(VolumeCommands())
return rootCmd
}
21 changes: 21 additions & 0 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package cmd

import (
"fmt"
"log"
"os"
"path"
"strconv"
"strings"

Expand Down Expand Up @@ -106,10 +108,17 @@ func runCommandHandler(cmd *cobra.Command, args []string) {
panic(err)
}

mounts, err := cmd.Flags().GetStringArray("mounts")
if err != nil {
panic(err)
}

c := unWarpConfig(config)

c.Args = append(c.Args, cmdargs...)
c.Program = args[0]
curdir, _ := os.Getwd()
c.ProgramPath = path.Join(curdir, args[0])

if len(cmdenvs) > 0 {
if len(c.Env) == 0 {
Expand Down Expand Up @@ -147,6 +156,16 @@ func runCommandHandler(cmd *cobra.Command, args []string) {
}
setDefaultImageName(cmd, c)

if len(mounts) > 0 {
for _, mount := range mounts {
vol := api.NewVolume(c)
err := vol.AttachOnRun(mount)
if err != nil {
log.Fatal(err)
}
}
}

if !skipbuild {
buildImages(c)
}
Expand Down Expand Up @@ -190,6 +209,7 @@ func RunCommand() *cobra.Command {
var bridged bool
var nightly bool
var tap string
var mounts []string

var skipbuild bool
var manifestName string
Expand Down Expand Up @@ -222,6 +242,7 @@ func RunCommand() *cobra.Command {
cmdRun.PersistentFlags().StringVarP(&manifestName, "manifest-name", "m", "", "save manifest to file")
cmdRun.PersistentFlags().BoolVar(&accel, "accel", true, "use cpu virtualization extension")
cmdRun.PersistentFlags().IntVarP(&smp, "smp", "", 1, "number of threads to use")
cmdRun.PersistentFlags().StringArrayVar(&mounts, "mounts", nil, "mount <volume_id:mount_path>")

return cmdRun
}
141 changes: 141 additions & 0 deletions cmd/volume.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package cmd

import (
"log"
"path"

api "github.com/nanovms/ops/lepton"
"github.com/spf13/cobra"
)

func volumeCreateCommandHandler(cmd *cobra.Command, args []string) {
name := args[0]
if name == "" {
log.Fatal("name required")
}
data, _ := cmd.Flags().GetString("data")
size, _ := cmd.Flags().GetString("size")
config, _ := cmd.Flags().GetString("config")
provider, _ := cmd.Flags().GetString("provider")
nightly, _ := cmd.Flags().GetBool("nightly")

conf := unWarpConfig(config)
conf.NightlyBuild = nightly
var err error
var version string
if conf.NightlyBuild {
version, err = downloadNightlyImages(conf)
} else {
version, err = downloadReleaseImages()
}
if err != nil {
log.Fatal(err)
}
if conf.Mkfs == "" {
conf.Mkfs = path.Join(api.GetOpsHome(), version, "mkfs")
}

vol := api.NewVolume(conf)
err = vol.Create(name, data, size, provider)
if err != nil {
log.Fatal(err)
}
}

func volumeCreateCommand() *cobra.Command {
var data string
var size string
cmdVolumeCreate := &cobra.Command{
Use: "create <volume_name>",
Short: "create volume",
Run: volumeCreateCommandHandler,
Args: cobra.MinimumNArgs(1),
}
cmdVolumeCreate.PersistentFlags().StringVarP(&data, "data", "d", "", "volume data source")
cmdVolumeCreate.PersistentFlags().StringVarP(&size, "size", "s", "", "volume initial size")
return cmdVolumeCreate
}

func volumeListCommandHandler(cmd *cobra.Command, args []string) {
config, _ := cmd.Flags().GetString("config")
conf := unWarpConfig(config)
vol := api.NewVolume(conf)
err := vol.GetAll()
if err != nil {
log.Fatal(err)
}
}

func volumeListCommand() *cobra.Command {
cmdVolumeList := &cobra.Command{
Use: "list",
Short: "list volume",
Run: volumeListCommandHandler,
}
return cmdVolumeList
}

func volumeDeleteCommandHandler(cmd *cobra.Command, args []string) {
id := args[0]
config, _ := cmd.Flags().GetString("config")
conf := unWarpConfig(config)
vol := api.NewVolume(conf)
err := vol.Delete(id)
if err != nil {
log.Fatal(err)
}
}

func volumeDeleteCommand() *cobra.Command {
cmdVolumeDelete := &cobra.Command{
Use: "delete <volume_id>",
Short: "delete volume",
Run: volumeDeleteCommandHandler,
Args: cobra.MinimumNArgs(1),
}
return cmdVolumeDelete
}

func volumeAttachCommandHandler(cmd *cobra.Command, args []string) {
image := args[0]
id := args[1]
mount := args[2]
config, _ := cmd.Flags().GetString("config")
conf := unWarpConfig(config)
vol := api.NewVolume(conf)
err := vol.Attach(image, id, mount)
if err != nil {
log.Fatal(err)
}
}

func volumeAttachCommand() *cobra.Command {
cmdVolumeAttach := &cobra.Command{
Use: "attach <image_name> <volume_id> <mount_path>",
Short: "attach volume",
Run: volumeAttachCommandHandler,
Args: cobra.MinimumNArgs(3),
}
return cmdVolumeAttach
}

// VolumeCommands handles volumes related operations
func VolumeCommands() *cobra.Command {
var config string
var provider string
var nightly bool
cmdVolume := &cobra.Command{
Use: "volume",
Short: "manage nanos volumes",
ValidArgs: []string{"create, list, delete, attach"},
Args: cobra.OnlyValidArgs,
}
cmdVolume.PersistentFlags().StringVarP(&config, "config", "c", "", "ops config file")
cmdVolume.PersistentFlags().StringVarP(&provider, "provider", "p", "", "cloud provider")
cmdVolume.PersistentFlags().BoolVarP(&nightly, "nightly", "n", false, "nightly build")
cmdVolume.AddCommand(volumeCreateCommand())
cmdVolume.AddCommand(volumeListCommand())
cmdVolume.AddCommand(volumeDeleteCommand())
cmdVolume.AddCommand(volumeAttachCommand())
return cmdVolume
}
3 changes: 3 additions & 0 deletions lepton/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Config struct {
Debugflags []string
NoTrace []string
Program string
ProgramPath string // original path of the program to refer to on attach/detach
Version string
Boot string
Kernel string
Expand All @@ -24,6 +25,7 @@ type Config struct {
BaseVolumeSz string // optional base volume sz
ManifestName string // save manifest to
RebootOnExit bool // Reboot on Failure Exit
Mounts map[string]string
}

// ProviderConfig give provider details
Expand All @@ -50,6 +52,7 @@ type RunConfig struct {
Accel bool
UDP bool // enable UDP
OnPrem bool // true if in a multi-instance/tenant on-prem env
Mounts []string
}

// RuntimeConfig constructs runtime config
Expand Down
10 changes: 10 additions & 0 deletions lepton/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ func GetOpsHome() string {
opshome := path.Join(home, ".ops")
images := path.Join(opshome, "images")
instances := path.Join(opshome, "instances")
manifests := path.Join(opshome, "manifests")
volumes := path.Join(opshome, "volumes")

if _, err := os.Stat(images); os.IsNotExist(err) {
os.MkdirAll(images, 0755)
Expand All @@ -82,6 +84,14 @@ func GetOpsHome() string {
os.MkdirAll(instances, 0755)
}

if _, err := os.Stat(manifests); os.IsNotExist(err) {
os.MkdirAll(manifests, 0755)
}

if _, err := os.Stat(volumes); os.IsNotExist(err) {
os.MkdirAll(volumes, 0755)
}

return opshome
}

Expand Down
53 changes: 50 additions & 3 deletions lepton/image.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package lepton

import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
Expand All @@ -17,11 +18,32 @@ import (
"github.com/go-errors/errors"
)

var localImageDir = path.Join(GetOpsHome(), "images")

// BuildImage builds a unikernel image for user
// supplied ELF binary.
func BuildImage(c Config) error {
var err error
m, err := BuildManifest(&c)
if err != nil {
return errors.Wrap(err, 1)
}

if err = buildImage(&c, m); err != nil {
return errors.Wrap(err, 1)
}

err = saveImageConfig(c)
if err != nil {
return err
}

return nil
}

// rebuildImage rebuilds a unikernel image for user
// supplied ELF binary after volume attach/detach
func rebuildImage(c Config) error {
c.Program = c.ProgramPath
m, err := BuildManifest(&c)
if err != nil {
return errors.Wrap(err, 1)
Expand All @@ -31,6 +53,11 @@ func BuildImage(c Config) error {
return errors.Wrap(err, 1)
}

err = saveImageConfig(c)
if err != nil {
return err
}

return nil
}

Expand Down Expand Up @@ -246,24 +273,28 @@ func addFromConfig(m *Manifest, c *Config) error {
m.AddNoTrace(syscallName)
}

m.AddUserProgram(c.Program)
m.AddEnvironmentVariable("USER", "root")
m.AddEnvironmentVariable("PWD", "/")
for k, v := range c.Env {
m.AddEnvironmentVariable(k, v)
}

for k, v := range c.Mounts {
m.AddMount(k, v)
}

return nil
}

// BuildManifest builds manifest using config
func BuildManifest(c *Config) (*Manifest, error) {

m := NewManifest(c.TargetRoot)

err := addFromConfig(m, c)
if err != nil {
return nil, errors.Wrap(err, 1)
}
m.AddUserProgram(c.Program)

deps, err := getSharedLibs(c.TargetRoot, c.Program)
if err != nil {
Expand Down Expand Up @@ -525,3 +556,19 @@ func lookupFile(targetRoot string, path string) (string, error) {

return path, err
}

// saveImageConfig saves image config as JSON
// for volume attach/detach purposes
func saveImageConfig(c Config) error {
b, err := json.Marshal(c)
if err != nil {
return errors.Wrap(err, 1)
}
imgFile := path.Base(c.RunConfig.Imagename)
mnfName := strings.TrimSuffix(imgFile, "img") + "json"
err = ioutil.WriteFile(path.Join(localManifestDir, mnfName), b, 0644)
if err != nil {
return errors.Wrap(err, 1)
}
return nil
}
Loading

0 comments on commit 7ebc83e

Please sign in to comment.