From a75e7709b8e0ea4d7f68b247c8c19db48666e91c Mon Sep 17 00:00:00 2001 From: Roman Dodin Date: Wed, 15 Jan 2025 15:12:55 +0100 Subject: [PATCH] Added centralized magic var replacer and support replacer for execs (#2395) added centralized magic var replacer and support replacer for execs --- clab/config.go | 33 ++++++++++++++--- clab/config_test.go | 35 ++++++++++++++++++- .../clab-topo2/node1/{somefile => node1} | 0 clab/test_data/topo14.yml | 3 ++ clab/test_data/topo2.yml | 2 +- 5 files changed, 66 insertions(+), 7 deletions(-) rename clab/test_data/clab-topo2/node1/{somefile => node1} (100%) diff --git a/clab/config.go b/clab/config.go index f8c020c38..48c92990d 100644 --- a/clab/config.go +++ b/clab/config.go @@ -239,7 +239,7 @@ func (c *CLab) createNodeCfg(nodeName string, nodeDef *types.NodeDefinition, idx if err != nil { return nil, err } - err = c.resolveBindPaths(binds, c.TopoPaths.NodeDir(nodeName)) + err = c.resolveBindPaths(binds, nodeName) if err != nil { return nil, err } @@ -253,6 +253,8 @@ func (c *CLab) createNodeCfg(nodeName string, nodeDef *types.NodeDefinition, idx nodeCfg.Config = c.Config.Topology.GetNodeConfigDispatcher(nodeCfg.ShortName) + c.processNodeExecs(nodeCfg) + return nodeCfg, nil } @@ -261,7 +263,7 @@ func (c *CLab) createNodeCfg(nodeName string, nodeDef *types.NodeDefinition, idx // Returns an absolute path to the startup-config file. func (c *CLab) processStartupConfig(nodeCfg *types.NodeConfig) error { // replace __clabNodeName__ magic var in startup-config path with node short name - r := strings.NewReplacer(nodeNameVar, nodeCfg.ShortName) + r := c.magicVarReplacer(nodeCfg.ShortName) p := r.Replace(c.Config.Topology.GetNodeStartupConfig(nodeCfg.ShortName)) // embedded config is a config that is defined as a multi-line string in the topology file @@ -498,7 +500,7 @@ func (c *CLab) verifyContainersUniqueness(ctx context.Context) error { // it allows host path to have `~` and relative path to an absolute path // the list of binds will be changed in place. // if the host path doesn't exist, the error will be returned. -func (c *CLab) resolveBindPaths(binds []string, nodeDir string) error { +func (c *CLab) resolveBindPaths(binds []string, nodeName string) error { // checks are skipped when, for example, the destroy operation is run if !c.checkBindsPaths { return nil @@ -513,8 +515,8 @@ func (c *CLab) resolveBindPaths(binds []string, nodeDir string) error { // volume, in this case we don't need to resolve the path continue } - // replace special variable - r := strings.NewReplacer(clabDirVar, c.TopoPaths.TopologyLabDir(), nodeDirVar, nodeDir) + // replace special variables + r := c.magicVarReplacer(nodeName) hp := r.Replace(elems[0]) hp = utils.ResolvePath(hp, c.TopoPaths.TopologyFileDir()) @@ -649,3 +651,24 @@ func addEnvVarsToNodeCfg(c *CLab, nodeCfg *types.NodeConfig) error { return nil } + +// processNodeExecs replaces (in place) magic variables in node execs. +func (c *CLab) processNodeExecs(nodeCfg *types.NodeConfig) { + for i, e := range nodeCfg.Exec { + r := c.magicVarReplacer(nodeCfg.ShortName) + nodeCfg.Exec[i] = r.Replace(e) + } +} + +// magicVarReplacer returns a string replacer that replaces all supported magic variables. +func (c *CLab) magicVarReplacer(nodeName string) *strings.Replacer { + if nodeName == "" { + return &strings.Replacer{} + } + + return strings.NewReplacer( + clabDirVar, c.TopoPaths.TopologyLabDir(), + nodeDirVar, c.TopoPaths.NodeDir(nodeName), + nodeNameVar, nodeName, + ) +} diff --git a/clab/config_test.go b/clab/config_test.go index 6ca9b6109..31527a947 100644 --- a/clab/config_test.go +++ b/clab/config_test.go @@ -78,7 +78,7 @@ func TestBindsInit(t *testing.T) { want: []string{ "node1.lic:/dst1", "kind.lic:/dst2", - "${PWD}/test_data/clab-topo2/node1/somefile:/somefile", + "${PWD}/test_data/clab-topo2/node1/node1:/somefile", }, }, "kind_and_node_binds": { @@ -738,3 +738,36 @@ func TestStartupConfigInit(t *testing.T) { }) } } + +func TestExecInit(t *testing.T) { + tests := map[string]struct { + got string + node string + want []string + }{ + "node_exec": { + got: "test_data/topo14.yml", + node: "node1", + want: []string{ + "echo \"Hello world\"", + "echo \"Hello node node1\"", + }, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + opts := []ClabOption{ + WithTopoPath(tc.got, ""), + } + c, err := NewContainerLab(opts...) + if err != nil { + t.Error(err) + } + + if d := cmp.Diff(c.Nodes[tc.node].Config().Exec, tc.want); d != "" { + t.Errorf("execs do not match %s", d) + } + }) + } +} diff --git a/clab/test_data/clab-topo2/node1/somefile b/clab/test_data/clab-topo2/node1/node1 similarity index 100% rename from clab/test_data/clab-topo2/node1/somefile rename to clab/test_data/clab-topo2/node1/node1 diff --git a/clab/test_data/topo14.yml b/clab/test_data/topo14.yml index 93fa6721c..41947a168 100644 --- a/clab/test_data/topo14.yml +++ b/clab/test_data/topo14.yml @@ -4,3 +4,6 @@ topology: node1: kind: nokia_srlinux startup-config: ./configs/fabric/__clabNodeName__.cfg + exec: + - echo "Hello world" + - echo "Hello node __clabNodeName__" diff --git a/clab/test_data/topo2.yml b/clab/test_data/topo2.yml index 13d2904cd..95b9b2af0 100644 --- a/clab/test_data/topo2.yml +++ b/clab/test_data/topo2.yml @@ -15,7 +15,7 @@ topology: binds: - node1.lic:/dst1 - kind.lic:/dst2 - - __clabNodeDir__/somefile:/somefile + - __clabNodeDir__/__clabNodeName__:/somefile node2: kind: nokia_srlinux type: ixr10