diff --git a/clab/config.go b/clab/config.go index 7285912fa..5b9106510 100644 --- a/clab/config.go +++ b/clab/config.go @@ -194,10 +194,15 @@ func (c *cLab) kindInitialization(nodeCfg *NodeConfig) string { } func (c *cLab) bindsInit(nodeCfg *NodeConfig) []string { - if len(nodeCfg.Binds) != 0 { + switch { + case len(nodeCfg.Binds) != 0: return nodeCfg.Binds + case len(c.Config.Topology.Kinds[nodeCfg.Kind].Binds) != 0: + return c.Config.Topology.Kinds[nodeCfg.Kind].Binds + case len(c.Config.Topology.Defaults.Binds) != 0: + return c.Config.Topology.Defaults.Binds } - return c.Config.Topology.Kinds[nodeCfg.Kind].Binds + return nil } // portsInit produces the nat.PortMap out of the slice of string representation of port bindings @@ -299,7 +304,14 @@ func (c *cLab) NewNode(nodeName string, nodeCfg NodeConfig, idx int) error { // Kind initialization is either coming from `topology.nodes` section or from `topology.defaults` // normalize the data to lower case to compare node.Kind = strings.ToLower(c.kindInitialization(&nodeCfg)) - node.Binds = c.bindsInit(&nodeCfg) + + // initialize bind mounts + binds := c.bindsInit(&nodeCfg) + err := resolveBindPaths(binds) + if err != nil { + return err + } + node.Binds = binds ps, pb, err := c.portsInit(&nodeCfg) if err != nil { @@ -508,3 +520,19 @@ func resolvePath(p string) (string, error) { } return p, nil } + +// resolveBindPaths resolves the host paths in a bind string, such as /hostpath:/remotepath(:options) string +// it allows host path to have `~` and returns absolute path for a relative path +func resolveBindPaths(binds []string) error { + for i := range binds { + // host path is a first element in a /hostpath:/remotepath(:options) string + elems := strings.Split(binds[i], ":") + hp, err := resolvePath(elems[0]) + if err != nil { + return err + } + elems[0] = hp + binds[i] = strings.Join(elems, ":") + } + return nil +} diff --git a/clab/config_test.go b/clab/config_test.go index bebe60dd5..3727354f0 100644 --- a/clab/config_test.go +++ b/clab/config_test.go @@ -1,7 +1,7 @@ package clab import ( - "path/filepath" + "reflect" "strings" "testing" ) @@ -54,7 +54,51 @@ func TestLicenseInit(t *testing.T) { } } -func abspath(s string) string { - p, _ := filepath.Abs(s) - return p +func TestBindsInit(t *testing.T) { + tests := map[string]struct { + got string + want []string + }{ + "node_sing_bind": { + got: "test_data/topo1.yml", + want: []string{"/node/src:/dst"}, + }, + "node_many_binds": { + got: "test_data/topo2.yml", + want: []string{"/node/src1:/dst1", "/node/src2:/dst2"}, + }, + "kind_binds": { + got: "test_data/topo5.yml", + want: []string{"/kind/src:/dst"}, + }, + "default_binds": { + got: "test_data/topo3.yml", + want: []string{"/default/src:/dst"}, + }, + "node_binds_override": { + got: "test_data/topo4.yml", + want: []string{"/node/src:/dst"}, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + opts := []ClabOption{ + WithTopoFile(tc.got), + } + c := NewContainerLab(opts...) + if err := c.ParseTopology(); err != nil { + t.Fatal(err) + } + + nodeCfg := c.Config.Topology.Nodes["node1"] + node := Node{} + node.Kind = strings.ToLower(c.kindInitialization(&nodeCfg)) + + binds := c.bindsInit(&nodeCfg) + if !reflect.DeepEqual(binds, tc.want) { + t.Fatalf("wanted %q got %q", tc.want, binds) + } + }) + } } diff --git a/clab/test_data/topo1.yml b/clab/test_data/topo1.yml index 5d763d4be..b8ffd637e 100644 --- a/clab/test_data/topo1.yml +++ b/clab/test_data/topo1.yml @@ -5,3 +5,5 @@ topology: kind: srl type: ixr6 license: node1.lic + binds: + - /node/src:/dst diff --git a/clab/test_data/topo2.yml b/clab/test_data/topo2.yml index 275de87ef..4aaf63eb1 100644 --- a/clab/test_data/topo2.yml +++ b/clab/test_data/topo2.yml @@ -7,3 +7,6 @@ topology: node1: kind: srl type: ixr6 + binds: + - /node/src1:/dst1 + - /node/src2:/dst2 diff --git a/clab/test_data/topo3.yml b/clab/test_data/topo3.yml index 6996e1f5d..6f2979772 100644 --- a/clab/test_data/topo3.yml +++ b/clab/test_data/topo3.yml @@ -2,6 +2,8 @@ name: topo3 topology: defaults: license: default.lic + binds: + - /default/src:/dst nodes: node1: kind: srl diff --git a/clab/test_data/topo4.yml b/clab/test_data/topo4.yml index 8e47f1566..056ac8956 100644 --- a/clab/test_data/topo4.yml +++ b/clab/test_data/topo4.yml @@ -1,12 +1,18 @@ -name: topo3 +name: topo4 topology: defaults: license: default.lic + binds: + - /default/src:/dst kinds: srl: license: kind.lic + binds: + - /kind/src:/dst nodes: node1: kind: srl type: ixr6 license: node1.lic + binds: + - /node/src:/dst diff --git a/clab/test_data/topo5.yml b/clab/test_data/topo5.yml new file mode 100644 index 000000000..7c6e1ad26 --- /dev/null +++ b/clab/test_data/topo5.yml @@ -0,0 +1,11 @@ +name: topo5 +topology: + kinds: + srl: + binds: + - /kind/src:/dst + nodes: + node1: + kind: srl + type: ixr6 + license: node1.lic