Repository: nanovms/ops (go)

Issue:
use config mounts to attach volumes
Mounts only works with command flag.

Pull request:
fix volumes mounting from configuration
(closes #935)

Patch:
diff --git a/README.md b/README.md
index 403bcdd..9733ddd 100644
--- a/README.md
+++ b/README.md
@@ -33,15 +33,19 @@ Installing from source follows these general steps:
 
 Install dependencies:
 
-    - `make deps`
+```sh
+make deps
+```
 
 Build:
 
-    - `make build`
+```sh
+make build
+```
 
 osx notes:
 
-```
+```sh
 GO111MODULE=on go build -ldflags "-w"
 ```
  
@@ -70,7 +74,7 @@ console.log('Server running at http://127.0.0.1:8083/');
 Then you can run it like so:
 
 ```bash
-ops load node_v11.5.0 -p 8083 -f -n -a hi.js
+ops pkg load node_v11.5.0 -p 8083 -f -n -a hi.js
 ```
 
 Want to push your app out to the cloud?
@@ -111,27 +115,30 @@ You can always find more pre-made packages via:
 ops pkg list
 ```
 
-
 # Build a bootable image
 `ops build <app>`
 
 # Package and run
-    ops run <app>
-    OR
-    ops run -p <port> <app>
+```sh
+ops run <app>
+# or
+ops run -p <port> <app>
+```
 
 # Using a config file
-    ops run -p <port> -c <file> <app>
+```sh
+ops run -p <port> -c <file> <app>
+```
 
 # Example config file
 
 ops config files are plain JSON, below is an example 
 
 ```JSON
-  {
-    "Args":["one","two"],
-    "Dirs":["myapp/static"]
-  }
+{
+  "Args":["one","two"],
+  "Dirs":["myapp/static"]
+}
 ```
 
 ## Setup networking
@@ -152,18 +159,18 @@ version and the release channel you are using.
 Also - if it doesn't work on the main release you can try the nightly -
 the main release can tail the nightly by many weeks sometimes.
 
-```
+```sh
 ops version
 ```
 
 get the release channel (or nightly)
-```
+```sh
 ls .ops/
 ```
 
 if using a package
 get the package hash:
-```
+```sh
 cat .ops/packages/manifest.json| jq '."gnatsd_1.4.1"'
 ```
 
diff --git a/aws/aws_volume.go b/aws/aws_volume.go
index 18d29fe..d551019 100644
--- a/aws/aws_volume.go
+++ b/aws/aws_volume.go
@@ -140,7 +140,7 @@ func (a *AWS) DeleteVolume(ctx *lepton.Context, name string) error {
 }
 
 // AttachVolume attaches a volume to an instance
-func (a *AWS) AttachVolume(ctx *lepton.Context, image, name, mount string) error {
+func (a *AWS) AttachVolume(ctx *lepton.Context, image, name string) error {
 	input := &ec2.AttachVolumeInput{
 		Device:     aws.String("/dev/sdf"),
 		InstanceId: aws.String(image),
diff --git a/azure/azure_volume.go b/azure/azure_volume.go
index ee294bb..8d6bdf3 100644
--- a/azure/azure_volume.go
+++ b/azure/azure_volume.go
@@ -136,7 +136,7 @@ func (a *Azure) DeleteVolume(ctx *lepton.Context, name string) error {
 }
 
 // AttachVolume attaches a volume to an instance
-func (a *Azure) AttachVolume(ctx *lepton.Context, image, name, mount string) error {
+func (a *Azure) AttachVolume(ctx *lepton.Context, image, name string) error {
 	vmClient := a.getVMClient()
 
 	vm, err := vmClient.Get(context.TODO(), a.groupName, image, compute.InstanceView)
diff --git a/cmd/cmd_update.go b/cmd/cmd_update.go
index a71de20..54aedd5 100644
--- a/cmd/cmd_update.go
+++ b/cmd/cmd_update.go
@@ -3,6 +3,7 @@ package cmd
 import (
 	"fmt"
 	"os"
+	"path"
 	"runtime"
 
 	api "github.com/nanovms/ops/lepton"
@@ -28,7 +29,8 @@ func updateCommandHandler(cmd *cobra.Command, args []string) {
 		fmt.Println("Updates ops to latest release.")
 	}
 	local, remote := api.LocalReleaseVersion, api.LatestReleaseVersion
-	if local == "0.0" || parseVersion(local, 4) != parseVersion(remote, 4) {
+	_, err = os.Stat(path.Join(api.GetOpsHome(), local))
+	if local == "0.0" || parseVersion(local, 4) != parseVersion(remote, 4) || os.IsNotExist(err) {
 		err = api.DownloadReleaseImages(remote)
 		if err != nil {
 			fmt.Println(err)
diff --git a/cmd/cmd_volume.go b/cmd/cmd_volume.go
index 397be36..5cfefcb 100644
--- a/cmd/cmd_volume.go
+++ b/cmd/cmd_volume.go
@@ -58,8 +58,6 @@ func volumeCreateCommandHandler(cmd *cobra.Command, args []string) {
 		exitWithError(err.Error())
 	}
 
-	c.BuildDir = api.LocalVolumeDir
-
 	p, ctx, err := getProviderAndContext(c, c.CloudConfig.Platform)
 	if err != nil {
 		log.Fatal(err)
@@ -94,8 +92,6 @@ func volumeListCommandHandler(cmd *cobra.Command, args []string) {
 		log.Fatal(err)
 	}
 
-	c.BuildDir = api.LocalVolumeDir
-
 	volumes, err := p.GetAllVolumes(ctx)
 	if err != nil {
 		log.Fatal(err)
@@ -127,8 +123,6 @@ func volumeDeleteCommandHandler(cmd *cobra.Command, args []string) {
 		log.Fatal(err)
 	}
 
-	c.BuildDir = api.LocalVolumeDir
-
 	err = p.DeleteVolume(ctx, name)
 	if err != nil {
 		log.Fatal(err)
@@ -137,7 +131,7 @@ func volumeDeleteCommandHandler(cmd *cobra.Command, args []string) {
 
 func volumeAttachCommand() *cobra.Command {
 	cmdVolumeAttach := &cobra.Command{
-		Use:   "attach <image_name> <volume_name> <mount_path>",
+		Use:   "attach <image_name> <volume_name>",
 		Short: "attach volume",
 		Run:   volumeAttachCommandHandler,
 		Args:  cobra.MinimumNArgs(3),
@@ -148,7 +142,6 @@ func volumeAttachCommand() *cobra.Command {
 func volumeAttachCommandHandler(cmd *cobra.Command, args []string) {
 	image := args[0]
 	name := args[1]
-	mount := args[2]
 
 	c, err := getVolumeCommandDefaultConfig(cmd)
 	if err != nil {
@@ -160,7 +153,7 @@ func volumeAttachCommandHandler(cmd *cobra.Command, args []string) {
 		log.Fatal(err)
 	}
 
-	err = p.AttachVolume(ctx, image, name, mount)
+	err = p.AttachVolume(ctx, image, name)
 	if err != nil {
 		log.Fatal(err)
 	}
diff --git a/cmd/flags_build_image.go b/cmd/flags_build_image.go
index 45e1e1b..2c83ddc 100644
--- a/cmd/flags_build_image.go
+++ b/cmd/flags_build_image.go
@@ -9,7 +9,6 @@ import (
 	"github.com/nanovms/ops/types"
 
 	"github.com/nanovms/ops/lepton"
-	api "github.com/nanovms/ops/lepton"
 	"github.com/nanovms/ops/onprem"
 	"github.com/spf13/pflag"
 )
@@ -51,7 +50,7 @@ func (flags *BuildImageCommandFlags) MergeToConfig(c *types.Config) (err error)
 	}
 
 	if c.RunConfig.Imagename != "" {
-		imageName := strings.Split(c.RunConfig.Imagename, ".")[0]
+		imageName := strings.Split(path.Base(c.RunConfig.Imagename), ".")[0]
 		if imageName == "" {
 			imageName = lepton.GenerateImageName(filepath.Base(c.Program))
 			c.CloudConfig.ImageName = fmt.Sprintf("%v-image", filepath.Base(c.Program))
@@ -70,14 +69,10 @@ func (flags *BuildImageCommandFlags) MergeToConfig(c *types.Config) (err error)
 	}
 
 	if flags.Mounts != nil {
-		// borrow BuildDir from config
-		bd := c.BuildDir
-		c.BuildDir = api.LocalVolumeDir
 		err = onprem.AddMounts(flags.Mounts, c)
 		if err != nil {
-			exitWithError(err.Error())
+			return
 		}
-		c.BuildDir = bd
 	}
 
 	if len(flags.CmdArgs) > 0 {
diff --git a/cmd/flags_config.go b/cmd/flags_config.go
index 5b73d85..8015a82 100644
--- a/cmd/flags_config.go
+++ b/cmd/flags_config.go
@@ -8,6 +8,8 @@ import (
 	"os/user"
 	"strings"
 
+	"github.com/nanovms/ops/lepton"
+	"github.com/nanovms/ops/onprem"
 	"github.com/nanovms/ops/types"
 	"github.com/spf13/pflag"
 )
@@ -37,12 +39,19 @@ func (flags *ConfigCommandFlags) MergeToConfig(c *types.Config) (err error) {
 			err = fmt.Errorf("error config: %v", err)
 			return
 		}
+
+		c.VolumesDir = lepton.LocalVolumeDir
+		if c.Mounts != nil {
+			err = onprem.AddMountsFromConfig(c)
+		}
+
 		return
 	}
 
 	if c == nil {
 		*c = *unWarpDefaultConfig()
 	}
+	c.VolumesDir = lepton.LocalVolumeDir
 
 	return
 
diff --git a/digitalocean/digital_ocean_volume.go b/digitalocean/digital_ocean_volume.go
index 337313d..144b105 100644
--- a/digitalocean/digital_ocean_volume.go
+++ b/digitalocean/digital_ocean_volume.go
@@ -19,7 +19,7 @@ func (do *DigitalOcean) DeleteVolume(ctx *lepton.Context, name string) error {
 }
 
 // AttachVolume is a stub to satisfy VolumeService interface
-func (do *DigitalOcean) AttachVolume(ctx *lepton.Context, image, name, mount string) error {
+func (do *DigitalOcean) AttachVolume(ctx *lepton.Context, image, name string) error {
 	return nil
 }
 
diff --git a/gcp/gcp_volume.go b/gcp/gcp_volume.go
index 3ed20a1..2a31fa5 100644
--- a/gcp/gcp_volume.go
+++ b/gcp/gcp_volume.go
@@ -135,12 +135,12 @@ func (g *GCloud) DeleteVolume(ctx *lepton.Context, name string) error {
 }
 
 // AttachVolume attaches Compute Engine Disk volume to existing instance
-func (g *GCloud) AttachVolume(ctx *lepton.Context, image, name, mount string) error {
+func (g *GCloud) AttachVolume(ctx *lepton.Context, image, name string) error {
 	config := ctx.Config()
 
 	disk := &compute.AttachedDisk{
 		AutoDelete: false,
-		DeviceName: mount,
+		DeviceName: name,
 		Source:     fmt.Sprintf("zones/%s/disks/%s", config.CloudConfig.Zone, name),
 	}
 	op, err := g.Service.Instances.AttachDisk(config.CloudConfig.ProjectID, config.CloudConfig.Zone, image, disk).Context(context.TODO()).Do()
diff --git a/hyperv/provider.go b/hyperv/provider.go
index 641ef3d..1b4ae0c 100644
--- a/hyperv/provider.go
+++ b/hyperv/provider.go
@@ -48,7 +48,7 @@ func (p *Provider) DeleteVolume(ctx *lepton.Context, name string) error {
 }
 
 // AttachVolume is a stub
-func (p *Provider) AttachVolume(ctx *lepton.Context, image, name, mount string) error {
+func (p *Provider) AttachVolume(ctx *lepton.Context, image, name string) error {
 	return errors.New("Unsupported")
 }
 
diff --git a/lepton/provider.go b/lepton/provider.go
index c31dc80..36ef863 100644
--- a/lepton/provider.go
+++ b/lepton/provider.go
@@ -55,7 +55,7 @@ type VolumeService interface {
 	CreateVolume(ctx *Context, name, data, size, provider string) (NanosVolume, error)
 	GetAllVolumes(ctx *Context) (*[]NanosVolume, error)
 	DeleteVolume(ctx *Context, name string) error
-	AttachVolume(ctx *Context, image, name, mount string) error
+	AttachVolume(ctx *Context, image, name string) error
 	DetachVolume(ctx *Context, image, name string) error
 }
 
diff --git a/lepton/volume.go b/lepton/volume.go
index 4e14440..2ea20fc 100644
--- a/lepton/volume.go
+++ b/lepton/volume.go
@@ -60,7 +60,7 @@ func CreateLocalVolume(config *types.Config, name, data, size, provider string)
 
 	mkfsCommand.SetLabel(name)
 	tmp := fmt.Sprintf("%s.raw", name)
-	tmpPath := path.Join(config.BuildDir, tmp)
+	tmpPath := path.Join(config.VolumesDir, tmp)
 	mkfsCommand.SetFileSystemPath(tmpPath)
 
 	if config.BaseVolumeSz != "" && size == "" {
@@ -77,14 +77,14 @@ func CreateLocalVolume(config *types.Config, name, data, size, provider string)
 	uuid := mkfsCommand.GetUUID()
 
 	raw := fmt.Sprintf("%s%s%s.raw", name, VolumeDelimiter, uuid)
-	rawPath := path.Join(config.BuildDir, raw)
+	rawPath := path.Join(config.VolumesDir, raw)
 	err = os.Rename(tmpPath, rawPath)
 	if err != nil {
 		fmt.Printf("volume: UUID: failed adding UUID info for volume %s\n", name)
 		fmt.Printf("rename the file to %s%s%s should you want to attach it by UUID\n", name, VolumeDelimiter, uuid)
 		fmt.Printf("symlink the file to %s should you want to attach it by label\n", name)
 	} else {
-		symlinkVolume(config.BuildDir, name, uuid)
+		symlinkVolume(config.VolumesDir, name, uuid)
 	}
 
 	vol = NanosVolume{
diff --git a/onprem/onprem_volume.go b/onprem/onprem_volume.go
index 3286f3a..fb7f7a4 100644
--- a/onprem/onprem_volume.go
+++ b/onprem/onprem_volume.go
@@ -24,7 +24,7 @@ func (op *OnPrem) CreateVolume(ctx *lepton.Context, name, data, size, provider s
 
 // GetAllVolumes prints list of all onprem nanos-managed volumes
 func (op *OnPrem) GetAllVolumes(ctx *lepton.Context) (*[]lepton.NanosVolume, error) {
-	vols, err := GetVolumes(ctx.Config().BuildDir, nil)
+	vols, err := GetVolumes(ctx.Config().VolumesDir, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -39,7 +39,7 @@ func (op *OnPrem) DeleteVolume(ctx *lepton.Context, name string) error {
 		"id":    name,
 	}
 
-	buildDir := ctx.Config().BuildDir
+	buildDir := ctx.Config().VolumesDir
 
 	volumes, err := GetVolumes(buildDir, query)
 	if err != nil {
@@ -67,9 +67,9 @@ func (op *OnPrem) DeleteVolume(ctx *lepton.Context, name string) error {
 // on `ops image create --mount`, it simply creates a mount path
 // with the given volume label
 // label can refer to volume UUID or volume label
-func (op *OnPrem) AttachVolume(ctx *lepton.Context, image, name, mount string) error {
+func (op *OnPrem) AttachVolume(ctx *lepton.Context, image, name string) error {
 	fmt.Println("not implemented")
-	fmt.Println("use <ops run> or <ops load> with --mounts flag instead")
+	fmt.Println("use <ops run> or <ops pkg load> with --mounts flag instead")
 	fmt.Println("alternatively, use <ops image create -t onprem> with --mounts flag")
 	fmt.Println("and run it with <ops instance create -t onprem>")
 	return nil
@@ -231,7 +231,7 @@ func AddMounts(mounts []string, config *types.Config) error {
 
 		query["id"] = lm[0]
 		query["label"] = lm[0]
-		vols, err := GetVolumes(config.BuildDir, query)
+		vols, err := GetVolumes(config.VolumesDir, query)
 		if err != nil {
 			return err
 		}
@@ -263,7 +263,7 @@ func AddMountsFromConfig(config *types.Config) error {
 	for label := range config.Mounts {
 		query["id"] = label
 		query["label"] = label
-		vols, err := GetVolumes(config.BuildDir, query)
+		vols, err := GetVolumes(config.VolumesDir, query)
 		if err != nil {
 			return err
 		}
diff --git a/openstack/openstack_volume.go b/openstack/openstack_volume.go
index 45db6a6..904e326 100644
--- a/openstack/openstack_volume.go
+++ b/openstack/openstack_volume.go
@@ -174,7 +174,7 @@ func (o *OpenStack) DeleteVolume(ctx *lepton.Context, name string) error {
 }
 
 // AttachVolume is a stub to satisfy VolumeService interface
-func (o *OpenStack) AttachVolume(ctx *lepton.Context, image, name, mount string) error {
+func (o *OpenStack) AttachVolume(ctx *lepton.Context, image, name string) error {
 	computeClient, err := o.getComputeClient()
 	if err != nil {
 		return err
@@ -196,7 +196,7 @@ func (o *OpenStack) AttachVolume(ctx *lepton.Context, image, name, mount string)
 	}
 
 	createOpts := volumeattach.CreateOpts{
-		Device:   "/dev/" + mount,
+		Device:   "/dev/" + name,
 		VolumeID: volume.ID,
 	}
 
diff --git a/types/config.go b/types/config.go
index 3c54ba3..6edc730 100644
--- a/types/config.go
+++ b/types/config.go
@@ -74,6 +74,9 @@ type Config struct {
 
 	// Version
 	Version string
+
+	// VolumesDir is the directory used to store and fetch volumes
+	VolumesDir string
 }
 
 // ProviderConfig give provider details
diff --git a/upcloud/provider_volume.go b/upcloud/provider_volume.go
index fbb4d42..e9726fa 100644
--- a/upcloud/provider_volume.go
+++ b/upcloud/provider_volume.go
@@ -97,7 +97,7 @@ func (p *Provider) DeleteVolume(ctx *lepton.Context, name string) (err error) {
 }
 
 // AttachVolume attaches a storage to an upcloud server
-func (p *Provider) AttachVolume(ctx *lepton.Context, image, name, mount string) (err error) {
+func (p *Provider) AttachVolume(ctx *lepton.Context, image, name string) (err error) {
 	instance, err := p.getInstanceByName(ctx, image)
 	if err != nil {
 		return
diff --git a/vsphere/vsphere_volume.go b/vsphere/vsphere_volume.go
index 1923819..b9bc7e3 100644
--- a/vsphere/vsphere_volume.go
+++ b/vsphere/vsphere_volume.go
@@ -19,7 +19,7 @@ func (v *Vsphere) DeleteVolume(ctx *lepton.Context, name string) error {
 }
 
 // AttachVolume is a stub to satisfy VolumeService interface
-func (v *Vsphere) AttachVolume(ctx *lepton.Context, image, name, mount string) error {
+func (v *Vsphere) AttachVolume(ctx *lepton.Context, image, name string) error {
 	return nil
 }
 
diff --git a/vultr/vultr_volume.go b/vultr/vultr_volume.go
index 2bddb96..12cefda 100644
--- a/vultr/vultr_volume.go
+++ b/vultr/vultr_volume.go
@@ -19,7 +19,7 @@ func (v *Vultr) DeleteVolume(ctx *lepton.Context, name string) error {
 }
 
 // AttachVolume is a stub to satisfy VolumeService interface
-func (v *Vultr) AttachVolume(ctx *lepton.Context, image, name, mount string) error {
+func (v *Vultr) AttachVolume(ctx *lepton.Context, image, name string) error {
 	return nil
 }
 


Test patch:
diff --git a/cmd/flags_build_image_test.go b/cmd/flags_build_image_test.go
index 337a766..6276f61 100644
--- a/cmd/flags_build_image_test.go
+++ b/cmd/flags_build_image_test.go
@@ -49,7 +49,9 @@ func TestBuildImageFlagsMergeToConfig(t *testing.T) {
 	opsPath := lepton.GetOpsHome() + "/" + lepton.LocalReleaseVersion
 	imagesPath := lepton.GetOpsHome() + "/images"
 
-	c := &types.Config{}
+	c := &types.Config{
+		VolumesDir: lepton.LocalVolumeDir,
+	}
 	expected := &types.Config{
 		Boot:   opsPath + "/boot.img",
 		Kernel: opsPath + "/kernel.img",
@@ -67,9 +69,12 @@ func TestBuildImageFlagsMergeToConfig(t *testing.T) {
 		TargetRoot: "unix",
 		Env:        map[string]string{"test": "1234"},
 		NameServer: "8.8.8.8",
+		VolumesDir: lepton.LocalVolumeDir,
 	}
 
-	buildImageFlags.MergeToConfig(c)
+	err := buildImageFlags.MergeToConfig(c)
+
+	assert.Nil(t, err)
 
 	c.RunConfig.Mounts = nil
 
diff --git a/cmd/flags_config_test.go b/cmd/flags_config_test.go
index 909f670..8ed4bd4 100644
--- a/cmd/flags_config_test.go
+++ b/cmd/flags_config_test.go
@@ -5,6 +5,7 @@ import (
 	"testing"
 
 	"github.com/nanovms/ops/cmd"
+	"github.com/nanovms/ops/lepton"
 	"github.com/nanovms/ops/types"
 	"github.com/spf13/pflag"
 	"github.com/stretchr/testify/assert"
@@ -31,6 +32,7 @@ func TestConfigFlagsMergeToConfig(t *testing.T) {
 		RunConfig: types.RunConfig{
 			Memory: "2G",
 		},
+		VolumesDir: lepton.LocalVolumeDir,
 	}
 
 	writeConfigToFile(expected, configFileName)
diff --git a/onprem/onprem_volume_test.go b/onprem/onprem_volume_test.go
index 4486835..b6286b4 100644
--- a/onprem/onprem_volume_test.go
+++ b/onprem/onprem_volume_test.go
@@ -49,7 +49,7 @@ func TestOnPremVolume(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	testVolumeConfig.BuildDir = tmp
+	testVolumeConfig.VolumesDir = tmp
 	testVolume2.Data = tmpdata
 	count := new(int)
 	*count = 0
@@ -78,7 +78,7 @@ func testCreateVolume(t *testing.T, name string, vol *lepton.NanosVolume, count
 
 func testGetVolumes(t *testing.T, name string, count *int) {
 	t.Run(name, func(t *testing.T) {
-		vols, err := onprem.GetVolumes(testVolumeConfig.BuildDir, nil)
+		vols, err := onprem.GetVolumes(testVolumeConfig.VolumesDir, nil)
 		if err != nil {
 			t.Error(err)
 			return
@@ -129,7 +129,7 @@ func TestOnPremVolume_AddMounts(t *testing.T) {
 		t.Fatal(err)
 	}
 	defer os.RemoveAll(dir)
-	config := &types.Config{BuildDir: dir}
+	config := &types.Config{VolumesDir: dir}
 
 	tests := []struct {
 		title   string
diff --git a/upcloud/provider_volume_test.go b/upcloud/provider_volume_test.go
index 81011f7..cd886c5 100644
--- a/upcloud/provider_volume_test.go
+++ b/upcloud/provider_volume_test.go
@@ -64,7 +64,7 @@ func TestAttachVolume(t *testing.T) {
 		StartServer(&request.StartServerRequest{UUID: serverID}).
 		Return(&upcloud.ServerDetails{Server: upcloud.Server{UUID: serverID, Title: serverName}}, nil)
 
-	err := p.AttachVolume(NewMockContext(), serverName, volumeName, "files")
+	err := p.AttachVolume(NewMockContext(), serverName, volumeName)
 
 	assert.Nil(t, err)
 }