From 5f28d760beaa6468a36d98c6217726e3fa6577e4 Mon Sep 17 00:00:00 2001 From: Roman Dodin Date: Sun, 10 Jan 2021 09:54:42 +0100 Subject: [PATCH] added user configuration for containers (#224) * added user configuration * documented cmd option --- clab/config.go | 25 ++++++++++++++++++--- clab/config_test.go | 48 ++++++++++++++++++++++++++++++++++++++++ clab/test_data/topo1.yml | 1 + clab/test_data/topo2.yml | 1 + clab/test_data/topo3.yml | 1 + clab/test_data/topo4.yml | 3 +++ docs/manual/nodes.md | 34 ++++++++++++++++++++++++++++ 7 files changed, 110 insertions(+), 3 deletions(-) diff --git a/clab/config.go b/clab/config.go index 3c88f2318..cd622c886 100644 --- a/clab/config.go +++ b/clab/config.go @@ -64,7 +64,8 @@ type NodeConfig struct { MgmtIPv4 string `yaml:"mgmt_ipv4,omitempty"` // user-defined IPv4 address in the management network MgmtIPv6 string `yaml:"mgmt_ipv6,omitempty"` // user-defined IPv6 address in the management network - Env map[string]string `yaml:"env,omitempty"` // environment variables + Env map[string]string `yaml:"env,omitempty"` // environment variables + User string `yaml:"user,omitempty"` // linux user used in a container } // Topology represents a lab topology @@ -325,6 +326,20 @@ func (c *cLab) positionInitialization(nodeCfg *NodeConfig, kind string) string { return c.Config.Topology.Defaults.Position } +func (c *cLab) userInit(nodeCfg *NodeConfig, kind string) string { + switch { + case nodeCfg.User != "": + return nodeCfg.User + + case c.Config.Topology.Kinds[kind].User != "": + return c.Config.Topology.Kinds[kind].User + + case c.Config.Topology.Defaults.User != "": + return c.Config.Topology.Defaults.User + } + return "" +} + // NewNode initializes a new node object func (c *cLab) NewNode(nodeName string, nodeCfg NodeConfig, idx int) error { // initialize a new node @@ -361,6 +376,8 @@ func (c *cLab) NewNode(nodeName string, nodeCfg NodeConfig, idx int) error { // initialize passed env variables which will be merged with kind specific ones envs := c.envInit(&nodeCfg, node.Kind) + user := c.userInit(&nodeCfg, node.Kind) + switch node.Kind { case "ceos": // initialize the global parameters with defaults, can be overwritten later @@ -383,7 +400,7 @@ func (c *cLab) NewNode(nodeName string, nodeCfg NodeConfig, idx int) error { "MGMT_INTF": "eth0"} node.Env = mergeStringMaps(kindEnv, envs) - node.User = "root" + node.User = user node.Group = c.groupInitialization(&nodeCfg, node.Kind) node.NodeType = nodeCfg.Type @@ -431,10 +448,11 @@ func (c *cLab) NewNode(nodeName string, nodeCfg NodeConfig, idx int) error { // initialize specifc container information node.Cmd = "sudo bash -c /opt/srlinux/bin/sr_linux" + kindEnv := map[string]string{"SRLINUX": "1"} node.Env = mergeStringMaps(kindEnv, envs) - node.User = "root" + node.User = user node.Sysctls = make(map[string]string) node.Sysctls["net.ipv4.ip_forward"] = "0" @@ -468,6 +486,7 @@ func (c *cLab) NewNode(nodeName string, nodeCfg NodeConfig, idx int) error { node.NodeType = c.typeInit(&nodeCfg, node.Kind) node.Position = c.positionInitialization(&nodeCfg, node.Kind) node.Cmd = c.cmdInit(&nodeCfg, node.Kind) + node.User = user node.Sysctls = make(map[string]string) node.Sysctls["net.ipv6.conf.all.disable_ipv6"] = "0" diff --git a/clab/config_test.go b/clab/config_test.go index dcd2f1686..1146bd830 100644 --- a/clab/config_test.go +++ b/clab/config_test.go @@ -213,3 +213,51 @@ func TestEnvInit(t *testing.T) { }) } } + +func TestUserInit(t *testing.T) { + tests := map[string]struct { + got string + node string + want string + }{ + "user_defined_at_node_level": { + got: "test_data/topo1.yml", + node: "node2", + want: "custom", + }, + "user_defined_at_kind_level": { + got: "test_data/topo2.yml", + node: "node2", + want: "customkind", + }, + "user_defined_at_defaults_level": { + got: "test_data/topo3.yml", + node: "node1", + want: "customglobal", + }, + "user_defined_at_node_and_kind_and_default_level": { + got: "test_data/topo4.yml", + node: "node1", + want: "customnode", + }, + } + + 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[tc.node] + kind := strings.ToLower(c.kindInitialization(&nodeCfg)) + user := c.userInit(&nodeCfg, kind) + if user != tc.want { + t.Fatalf("wanted %q got %q", tc.want, user) + } + }) + } +} diff --git a/clab/test_data/topo1.yml b/clab/test_data/topo1.yml index 0c15e3a6b..ab7b3dd25 100644 --- a/clab/test_data/topo1.yml +++ b/clab/test_data/topo1.yml @@ -13,3 +13,4 @@ topology: node2: kind: srl license: node1.lic + user: custom diff --git a/clab/test_data/topo2.yml b/clab/test_data/topo2.yml index 9eb304c41..44c48d0d0 100644 --- a/clab/test_data/topo2.yml +++ b/clab/test_data/topo2.yml @@ -6,6 +6,7 @@ topology: type: ixrd2 env: env1: val1 + user: customkind nodes: node1: kind: srl diff --git a/clab/test_data/topo3.yml b/clab/test_data/topo3.yml index 1f17a693e..fd0544481 100644 --- a/clab/test_data/topo3.yml +++ b/clab/test_data/topo3.yml @@ -7,6 +7,7 @@ topology: type: ixrd2 env: env1: val1 + user: customglobal nodes: node1: kind: srl diff --git a/clab/test_data/topo4.yml b/clab/test_data/topo4.yml index 577dbe0ee..3b3b5d039 100644 --- a/clab/test_data/topo4.yml +++ b/clab/test_data/topo4.yml @@ -8,6 +8,7 @@ topology: env1: global env2: global env3: global + user: customglobal kinds: srl: license: kind.lic @@ -16,6 +17,7 @@ topology: env: env2: kind env4: kind + user: customkind nodes: node1: kind: srl @@ -26,3 +28,4 @@ topology: env: env1: node env5: node + user: customnode diff --git a/docs/manual/nodes.md b/docs/manual/nodes.md index 6c12b84b0..6a108de69 100644 --- a/docs/manual/nodes.md +++ b/docs/manual/nodes.md @@ -19,6 +19,10 @@ topology: - 80:8080 - 55555:43555/udp - 55554:43554/tcp + user: test + env: + ENV1: VAL1 + cmd: /bin/bash script.sh ``` ### kind @@ -107,4 +111,34 @@ topology: node1: env: ENV1: 1 # ENV1=1 will be set for node1 +``` + +### user +To set a user which will be used to run a containerized process use the `user` configuration option. Can be defined at `node`, `kind` and `global` levels. + +```yaml +topology: + defaults: + user: alice # alice user will be used for all nodes unless set on kind or node levels + kinds: + srl: + user: bob # bob user will be used for nodes of kind srl unless it is set on node level + nodes: + node1: + user: clab # clab user will be used for node1 +``` + +### cmd +It is possible to set/override the command of the container image with `cmd` configuration option. It accepts the "shell" form and can be set on all levels. + +```yaml +topology: + defaults: + cmd: bash cmd.sh + kinds: + srl: + cmd: bash cmd2.sh + nodes: + node1: + cmd: bash cmd3.sh ``` \ No newline at end of file