Repository: nanovms/ops (go)

Issue:
instance deploy returns cleanly with no error msg if deploying missing image locally
```ops instance somethingrandom``` for instance should state that there is no image named 'somethingrandom' therefore it couldn't deploy it

ran into this when I forgot to put it on a cloud target

Pull request description:
support static ipv6 (closes #1055)
The network addresses flags are now handled along with build image flags because they affect the images manifest file. Until now static addresses were only supported when we wanted to launch an instance locally (for example, in run command). After this change, ops is able to upload images with static addresses to cloud providers.

Patch:
diff --git a/cmd/flags_build_image.go b/cmd/flags_build_image.go
index 9e03fb0..3ec5c80 100644
--- a/cmd/flags_build_image.go
+++ b/cmd/flags_build_image.go
@@ -15,12 +15,16 @@ import (
 
 // BuildImageCommandFlags consolidates all command flags required to build an image in one struct
 type BuildImageCommandFlags struct {
-	Type       string
-	CmdArgs    []string
-	CmdEnvs    []string
-	ImageName  string
-	Mounts     []string
-	TargetRoot string
+	Type        string
+	CmdArgs     []string
+	CmdEnvs     []string
+	ImageName   string
+	Mounts      []string
+	TargetRoot  string
+	IPAddress   string
+	IPv6Address string
+	Netmask     string
+	Gateway     string
 }
 
 // MergeToConfig overrides configuration passed by argument with command flags values
@@ -88,6 +92,22 @@ func (flags *BuildImageCommandFlags) MergeToConfig(c *types.Config) (err error)
 		c.Args = append([]string{c.Program}, c.Args...)
 	}
 
+	if flags.IPAddress != "" && isIPAddressValid(flags.IPAddress) {
+		c.RunConfig.IPAddress = flags.IPAddress
+	}
+
+	if flags.IPv6Address != "" {
+		c.RunConfig.IPv6Address = flags.IPv6Address
+	}
+
+	if flags.Gateway != "" && isIPAddressValid(flags.Gateway) {
+		c.RunConfig.Gateway = flags.Gateway
+	}
+
+	if flags.Netmask != "" && isIPAddressValid(flags.Netmask) {
+		c.RunConfig.NetMask = flags.Netmask
+	}
+
 	return
 }
 
@@ -126,6 +146,26 @@ func NewBuildImageCommandFlags(cmdFlags *pflag.FlagSet) (flags *BuildImageComman
 		exitWithError(err.Error())
 	}
 
+	flags.Gateway, err = cmdFlags.GetString("gateway")
+	if err != nil {
+		exitWithError(err.Error())
+	}
+
+	flags.IPAddress, err = cmdFlags.GetString("ip-address")
+	if err != nil {
+		exitWithError(err.Error())
+	}
+
+	flags.IPv6Address, err = cmdFlags.GetString("ipv6-address")
+	if err != nil {
+		exitWithError(err.Error())
+	}
+
+	flags.Netmask, err = cmdFlags.GetString("netmask")
+	if err != nil {
+		exitWithError(err.Error())
+	}
+
 	return
 }
 
@@ -137,6 +177,10 @@ func PersistBuildImageCommandFlags(cmdFlags *pflag.FlagSet) {
 	cmdFlags.StringP("imagename", "i", "", "image name")
 	cmdFlags.StringArray("mounts", nil, "mount <volume_id:mount_path>")
 	cmdFlags.StringArrayP("args", "a", nil, "command line arguments")
+	cmdFlags.String("ip-address", "", "static ip address")
+	cmdFlags.String("ipv6-address", "", "static ipv6 address")
+	cmdFlags.String("gateway", "", "network gateway")
+	cmdFlags.String("netmask", "", "network mask")
 }
 
 func setNanosBaseImage(c *types.Config) {
diff --git a/cmd/flags_run_local_instance.go b/cmd/flags_run_local_instance.go
index c83b61b..ae7a677 100644
--- a/cmd/flags_run_local_instance.go
+++ b/cmd/flags_run_local_instance.go
@@ -20,10 +20,7 @@ type RunLocalInstanceCommandFlags struct {
 	BridgeName     string
 	Debug          bool
 	Force          bool
-	Gateway        string
 	GDBPort        int
-	IPAddress      string
-	Netmask        string
 	NoTrace        []string
 	Ports          []string
 	SkipBuild      bool
@@ -83,18 +80,6 @@ func (flags *RunLocalInstanceCommandFlags) MergeToConfig(c *types.Config) (err e
 		c.RunConfig.BridgeName = flags.BridgeName
 	}
 
-	if flags.IPAddress != "" && isIPAddressValid(flags.IPAddress) {
-		c.RunConfig.IPAddress = flags.IPAddress
-	}
-
-	if flags.Gateway != "" && isIPAddressValid(flags.Gateway) {
-		c.RunConfig.Gateway = flags.Gateway
-	}
-
-	if flags.Netmask != "" && isIPAddressValid(flags.Netmask) {
-		c.RunConfig.NetMask = flags.Netmask
-	}
-
 	if len(flags.NoTrace) > 0 {
 		c.NoTrace = flags.NoTrace
 	}
@@ -163,26 +148,11 @@ func NewRunLocalInstanceCommandFlags(cmdFlags *pflag.FlagSet) (flags *RunLocalIn
 		exitWithError(err.Error())
 	}
 
-	flags.Gateway, err = cmdFlags.GetString("gateway")
-	if err != nil {
-		exitWithError(err.Error())
-	}
-
 	flags.GDBPort, err = cmdFlags.GetInt("gdbport")
 	if err != nil {
 		exitWithError(err.Error())
 	}
 
-	flags.IPAddress, err = cmdFlags.GetString("ip-address")
-	if err != nil {
-		exitWithError(err.Error())
-	}
-
-	flags.Netmask, err = cmdFlags.GetString("netmask")
-	if err != nil {
-		exitWithError(err.Error())
-	}
-
 	flags.NoTrace, err = cmdFlags.GetStringArray("no-trace")
 	if err != nil {
 		exitWithError(err.Error())
@@ -243,9 +213,6 @@ func PersistRunLocalInstanceCommandFlags(cmdFlags *pflag.FlagSet) {
 	cmdFlags.BoolP("bridged", "b", false, "bridge networking")
 	cmdFlags.StringP("bridgename", "", "", "bridge name")
 	cmdFlags.StringP("tapname", "t", "", "tap device name")
-	cmdFlags.String("ip-address", "", "static ip address")
-	cmdFlags.String("gateway", "", "network gateway")
-	cmdFlags.String("netmask", "255.255.255.0", "network mask")
 	cmdFlags.BoolP("skipbuild", "s", false, "skip building image")
 	cmdFlags.Bool("accel", true, "use cpu virtualization extension")
 	cmdFlags.IntP("smp", "", 1, "number of threads to use")
diff --git a/fs/manifest.go b/fs/manifest.go
index 9e05b2a..006bee6 100644
--- a/fs/manifest.go
+++ b/fs/manifest.go
@@ -18,6 +18,7 @@ type link struct {
 // ManifestNetworkConfig has network configuration to set static IP
 type ManifestNetworkConfig struct {
 	IP      string
+	IPv6    string
 	Gateway string
 	NetMask string
 }
@@ -46,6 +47,7 @@ func (m *Manifest) AddNetworkConfig(networkConfig *ManifestNetworkConfig) {
 	m.root["ipaddr"] = networkConfig.IP
 	m.root["netmask"] = networkConfig.NetMask
 	m.root["gateway"] = networkConfig.Gateway
+	m.root["ip6addr"] = networkConfig.IPv6
 }
 
 // AddUserProgram adds user program
diff --git a/lepton/image.go b/lepton/image.go
index 568ed10..ea9e419 100644
--- a/lepton/image.go
+++ b/lepton/image.go
@@ -289,9 +289,10 @@ func setManifestFromConfig(m *fs.Manifest, c *types.Config) error {
 		m.AddMount(k, v)
 	}
 
-	if c.RunConfig.IPAddress != "" {
+	if c.RunConfig.IPAddress != "" || c.RunConfig.IPv6Address != "" {
 		m.AddNetworkConfig(&fs.ManifestNetworkConfig{
 			IP:      c.RunConfig.IPAddress,
+			IPv6:    c.RunConfig.IPv6Address,
 			Gateway: c.RunConfig.Gateway,
 			NetMask: c.RunConfig.NetMask,
 		})
@@ -320,9 +321,10 @@ func BuildManifest(c *types.Config) (*fs.Manifest, error) {
 		m.AddLibrary(libpath)
 	}
 
-	if c.RunConfig.IPAddress != "" {
+	if c.RunConfig.IPAddress != "" || c.RunConfig.IPv6Address != "" {
 		m.AddNetworkConfig(&fs.ManifestNetworkConfig{
 			IP:      c.RunConfig.IPAddress,
+			IPv6:    c.RunConfig.IPv6Address,
 			Gateway: c.RunConfig.Gateway,
 			NetMask: c.RunConfig.NetMask,
 		})
diff --git a/types/config.go b/types/config.go
index 0a72206..b4a7565 100644
--- a/types/config.go
+++ b/types/config.go
@@ -181,6 +181,9 @@ type RunConfig struct {
 	// IPAddress
 	IPAddress string
 
+	// IPv6Address
+	IPv6Address string
+
 	// Klibs
 	Klibs []string
 


Test patch:
diff --git a/cmd/flags_build_image_test.go b/cmd/flags_build_image_test.go
index d9436f2..d074094 100644
--- a/cmd/flags_build_image_test.go
+++ b/cmd/flags_build_image_test.go
@@ -13,73 +13,116 @@ import (
 
 func TestCreateBuildImageFlags(t *testing.T) {
 
-	flagSet := newBuildImageFlagSet()
+	t.Run("should map flags to struct", func(t *testing.T) {
+		flagSet := newBuildImageFlagSet()
+
+		flagSet.Set("envs", "test=1234")
+		flagSet.Set("target-root", "unix")
+		flagSet.Set("imagename", "test-image")
+		flagSet.Set("mounts", "label:path,label2:path2")
+		flagSet.Set("args", "a b c d")
+		flagSet.Set("ip-address", "192.168.0.1")
+		flagSet.Set("ipv6-address", "FE80::46F:65FF:FE9C:4861")
+		flagSet.Set("gateway", "192.168.1.254")
+		flagSet.Set("netmask", "255.255.0.0")
+
+		buildImageFlags := cmd.NewBuildImageCommandFlags(flagSet)
+
+		assert.Equal(t, buildImageFlags.CmdEnvs, []string{"test=1234"})
+		assert.Equal(t, buildImageFlags.TargetRoot, "unix")
+		assert.Equal(t, buildImageFlags.ImageName, "test-image")
+		assert.Equal(t, buildImageFlags.Mounts, []string{"label:path,label2:path2"})
+		assert.Equal(t, buildImageFlags.CmdArgs, []string{"a b c d"})
+		assert.Equal(t, buildImageFlags.IPAddress, "192.168.0.1")
+		assert.Equal(t, buildImageFlags.IPv6Address, "FE80::46F:65FF:FE9C:4861")
+		assert.Equal(t, buildImageFlags.Gateway, "192.168.1.254")
+		assert.Equal(t, buildImageFlags.Netmask, "255.255.0.0")
+	})
+}
 
-	flagSet.Set("envs", "test=1234")
-	flagSet.Set("target-root", "unix")
-	flagSet.Set("imagename", "test-image")
-	flagSet.Set("mounts", "label:path,label2:path2")
-	flagSet.Set("args", "a b c d")
+func TestBuildImageFlagsMergeToConfig(t *testing.T) {
 
-	buildImageFlags := cmd.NewBuildImageCommandFlags(flagSet)
+	t.Run("should merge flags into configuration", func(t *testing.T) {
+		volumeName := buildVolume("vol")
+		defer removeVolume(volumeName)
 
-	assert.Equal(t, buildImageFlags.CmdEnvs, []string{"test=1234"})
-	assert.Equal(t, buildImageFlags.TargetRoot, "unix")
-	assert.Equal(t, buildImageFlags.ImageName, "test-image")
-	assert.Equal(t, buildImageFlags.Mounts, []string{"label:path,label2:path2"})
-	assert.Equal(t, buildImageFlags.CmdArgs, []string{"a b c d"})
-}
+		flagSet := pflag.NewFlagSet("test", 0)
 
-func TestBuildImageFlagsMergeToConfig(t *testing.T) {
-	volumeName := buildVolume("vol")
-	defer removeVolume(volumeName)
+		cmd.PersistBuildImageCommandFlags(flagSet)
 
-	flagSet := pflag.NewFlagSet("test", 0)
+		flagSet.Set("args", "a b c d")
+		flagSet.Set("envs", "test=1234")
+		flagSet.Set("target-root", "unix")
+		flagSet.Set("imagename", "test-image")
+		flagSet.Set("mounts", volumeName+":/files")
 
-	cmd.PersistBuildImageCommandFlags(flagSet)
+		buildImageFlags := cmd.NewBuildImageCommandFlags(flagSet)
+
+		opsPath := lepton.GetOpsHome() + "/" + lepton.LocalReleaseVersion
+		imagesPath := lepton.GetOpsHome() + "/images"
+
+		c := &types.Config{
+			VolumesDir: lepton.LocalVolumeDir,
+			Program:    "MyTestApp",
+		}
+		expected := &types.Config{
+			Boot:   opsPath + "/boot.img",
+			Kernel: opsPath + "/kernel.img",
+			Mounts: map[string]string{
+				volumeName: "/files",
+			},
+			CloudConfig: types.ProviderConfig{
+				ImageName: "test-image",
+			},
+			RunConfig: types.RunConfig{
+				Imagename: imagesPath + "/test-image.img",
+			},
+			Args:       []string{"MyTestApp", "a b c d"},
+			Program:    "MyTestApp",
+			TargetRoot: "unix",
+			Env:        map[string]string{"test": "1234"},
+			NameServer: "8.8.8.8",
+			VolumesDir: lepton.LocalVolumeDir,
+		}
+
+		err := buildImageFlags.MergeToConfig(c)
+
+		assert.Nil(t, err)
+
+		c.RunConfig.Mounts = nil
+
+		assert.Equal(t, expected, c)
+	})
+
+	t.Run("should clear ip/gateway/netmask if they are not valid ip addresses", func(t *testing.T) {
+		flagSet := newBuildImageFlagSet()
+
+		runLocalInstanceFlags := cmd.NewBuildImageCommandFlags(flagSet)
+		runLocalInstanceFlags.IPAddress = "tomato"
+		runLocalInstanceFlags.Gateway = "potato"
+		runLocalInstanceFlags.Netmask = "cheese"
+
+		c := &types.Config{}
+
+		err := runLocalInstanceFlags.MergeToConfig(c)
+
+		assert.Nil(t, err, nil)
+
+		opsPath := lepton.GetOpsHome() + "/" + lepton.LocalReleaseVersion
+
+		expected := &types.Config{
+			Boot:        opsPath + "/boot.img",
+			Kernel:      opsPath + "/kernel.img",
+			Mounts:      map[string]string{},
+			CloudConfig: types.ProviderConfig{},
+			RunConfig:   types.RunConfig{},
+			Args:        []string{},
+			NameServer:  "8.8.8.8",
+		}
+
+		assert.Equal(t, expected, c)
+	})
 
-	flagSet.Set("args", "a b c d")
-	flagSet.Set("envs", "test=1234")
-	flagSet.Set("target-root", "unix")
-	flagSet.Set("imagename", "test-image")
-	flagSet.Set("mounts", volumeName+":/files")
-
-	buildImageFlags := cmd.NewBuildImageCommandFlags(flagSet)
-
-	opsPath := lepton.GetOpsHome() + "/" + lepton.LocalReleaseVersion
-	imagesPath := lepton.GetOpsHome() + "/images"
-
-	c := &types.Config{
-		VolumesDir: lepton.LocalVolumeDir,
-		Program:    "MyTestApp",
-	}
-	expected := &types.Config{
-		Boot:   opsPath + "/boot.img",
-		Kernel: opsPath + "/kernel.img",
-		Mounts: map[string]string{
-			volumeName: "/files",
-		},
-		CloudConfig: types.ProviderConfig{
-			ImageName: "test-image",
-		},
-		RunConfig: types.RunConfig{
-			Imagename: imagesPath + "/test-image.img",
-		},
-		Args:       []string{"MyTestApp", "a b c d"},
-		Program:    "MyTestApp",
-		TargetRoot: "unix",
-		Env:        map[string]string{"test": "1234"},
-		NameServer: "8.8.8.8",
-		VolumesDir: lepton.LocalVolumeDir,
-	}
-
-	err := buildImageFlags.MergeToConfig(c)
-
-	assert.Nil(t, err)
-
-	c.RunConfig.Mounts = nil
-
-	assert.Equal(t, expected, c)
 }
 
 func newBuildImageFlagSet() (flagSet *pflag.FlagSet) {
diff --git a/cmd/flags_run_local_instance_test.go b/cmd/flags_run_local_instance_test.go
index 194839e..753ca16 100644
--- a/cmd/flags_run_local_instance_test.go
+++ b/cmd/flags_run_local_instance_test.go
@@ -24,9 +24,6 @@ func TestCreateRunLocalInstanceFlags(t *testing.T) {
 	assert.Equal(t, runLocalInstanceFlags.BridgeName, "br1")
 
 	assert.Equal(t, runLocalInstanceFlags.TapName, "tap1")
-	assert.Equal(t, runLocalInstanceFlags.IPAddress, "192.168.0.1")
-	assert.Equal(t, runLocalInstanceFlags.Gateway, "192.168.1.254")
-	assert.Equal(t, runLocalInstanceFlags.Netmask, "255.255.0.0")
 	assert.Equal(t, runLocalInstanceFlags.SkipBuild, true)
 	assert.Equal(t, runLocalInstanceFlags.Accel, false)
 	assert.Equal(t, runLocalInstanceFlags.Smp, 2)
@@ -46,44 +43,6 @@ func TestRunLocalInstanceFlagsMergeToConfig(t *testing.T) {
 
 		assert.Nil(t, err, nil)
 
-		expected := &types.Config{
-			BuildDir:   "",
-			Debugflags: []string{"trace", "debugsyscalls", "futex_trace", "fault", "syscall_summary"},
-			Force:      true,
-			NoTrace:    []string{"a"},
-			RunConfig: types.RunConfig{
-				Accel:      true,
-				Bridged:    true,
-				BridgeName: "br1",
-				CPUs:       2,
-				Debug:      false,
-				Gateway:    "192.168.1.254",
-				GdbPort:    1234,
-				IPAddress:  "192.168.0.1",
-				Mounts:     []string(nil),
-				NetMask:    "255.255.0.0",
-				Ports:      []string{"80", "81", "82-85"},
-				TapName:    "tap1",
-				Verbose:    true,
-			},
-		}
-
-		assert.Equal(t, expected, c)
-	})
-
-	t.Run("should clear ip/gateway/netmask if they are not valid ip addresses", func(t *testing.T) {
-		runLocalInstanceFlags := newRunLocalInstanceFlagSet("false")
-		runLocalInstanceFlags.Debug = false
-		runLocalInstanceFlags.IPAddress = "tomato"
-		runLocalInstanceFlags.Gateway = "potato"
-		runLocalInstanceFlags.Netmask = "cheese"
-
-		c := &types.Config{}
-
-		err := runLocalInstanceFlags.MergeToConfig(c)
-
-		assert.Nil(t, err, nil)
-
 		expected := &types.Config{
 			BuildDir:   "",
 			Debugflags: []string{"trace", "debugsyscalls", "futex_trace", "fault", "syscall_summary"},
@@ -128,10 +87,9 @@ func TestRunLocalInstanceFlagsMergeToConfig(t *testing.T) {
 		expected := &types.Config{
 			Debugflags: []string{},
 			RunConfig: types.RunConfig{
-				Accel:   true,
-				CPUs:    1,
-				Ports:   []string{"80", "90", "81", "82-85"},
-				NetMask: "255.255.255.0",
+				Accel: true,
+				CPUs:  1,
+				Ports: []string{"80", "90", "81", "82-85"},
 			},
 		}
 
@@ -155,9 +113,6 @@ func newRunLocalInstanceFlagSet(debug string) *cmd.RunLocalInstanceCommandFlags
 	flagSet.Set("bridged", "true")
 	flagSet.Set("bridgename", "br1")
 	flagSet.Set("tapname", "tap1")
-	flagSet.Set("ip-address", "192.168.0.1")
-	flagSet.Set("gateway", "192.168.1.254")
-	flagSet.Set("netmask", "255.255.0.0")
 	flagSet.Set("skipbuild", "true")
 	flagSet.Set("manifest-name", "manifest.json")
 	flagSet.Set("accel", "true")