diff --git a/clab/config.go b/clab/config.go index bc5f78621..ca05036bc 100644 --- a/clab/config.go +++ b/clab/config.go @@ -144,8 +144,8 @@ type Endpoint struct { EndpointName string } -// ParseIPInfo parses IP information -func (c *CLab) parseIPInfo() error { +// initMgmtNetwork sets management network config +func (c *CLab) initMgmtNetwork() error { if c.Config.Mgmt.Network == "" { c.Config.Mgmt.Network = dockerNetName } @@ -168,8 +168,8 @@ func (c *CLab) parseIPInfo() error { func (c *CLab) ParseTopology() error { log.Info("Parsing topology information ...") log.Debugf("Lab name: %s", c.Config.Name) - // initialize DockerInfo - err := c.parseIPInfo() + // initialize Management network config + err := c.initMgmtNetwork() if err != nil { return err } @@ -679,8 +679,21 @@ func (c *CLab) NewEndpoint(e string) *Endpoint { return endpoint } +// CheckTopologyDefinition runs topology checks and returns any errors found +func (c *CLab) CheckTopologyDefinition() error { + err := c.verifyBridgesExist() + if err != nil { + return err + } + err = c.verifyLinks() + if err != nil { + return err + } + return nil +} + // VerifyBridgeExists verifies if every node of kind=bridge exists on the lab host -func (c *CLab) VerifyBridgesExist() error { +func (c *CLab) verifyBridgesExist() error { for name, node := range c.Nodes { if node.Kind == "bridge" { if _, err := netlink.LinkByName(name); err != nil { @@ -691,6 +704,23 @@ func (c *CLab) VerifyBridgesExist() error { return nil } +func (c *CLab) verifyLinks() error { + endpoints := map[string]struct{}{} + dups := []string{} + for _, lc := range c.Config.Topology.Links { + for _, e := range lc.Endpoints { + if _, ok := endpoints[e]; ok { + dups = append(dups, e) + } + endpoints[e] = struct{}{} + } + } + if len(dups) != 0 { + return fmt.Errorf("endpoints %q appeared more than once in the links section of the topology file", dups) + } + return nil +} + //resolvePath resolves a string path by expanding `~` to home dir or getting Abs path for the given path func resolvePath(p string) (string, error) { if p == "" { diff --git a/clab/config_test.go b/clab/config_test.go index 1146bd830..68d2b0531 100644 --- a/clab/config_test.go +++ b/clab/config_test.go @@ -261,3 +261,37 @@ func TestUserInit(t *testing.T) { }) } } + +func TestVerifyLinks(t *testing.T) { + tests := map[string]struct { + got string + want string + }{ + "two_duplicated_links": { + got: "test_data/topo6.yml", + want: "endpoints [\"lin1:eth1\" \"lin2:eth2\"] appeared more than once in the links section of the topology file", + }, + "no_duplicated_links": { + got: "test_data/topo1.yml", + want: "", + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + opts := []ClabOption{ + WithTopoFile(tc.got), + } + c := NewContainerLab(opts...) + + err := c.verifyLinks() + if err != nil && err.Error() != tc.want { + t.Fatalf("wanted %q got %q", tc.want, err.Error()) + } + if err == nil && tc.want != "" { + t.Fatalf("wanted %q got %q", tc.want, err.Error()) + } + }) + } + +} diff --git a/clab/test_data/topo6.yml b/clab/test_data/topo6.yml new file mode 100644 index 000000000..15e5ad2d7 --- /dev/null +++ b/clab/test_data/topo6.yml @@ -0,0 +1,15 @@ +name: 2l + +topology: + nodes: + lin1: + kind: linux + image: alpine:3 + lin2: + kind: linux + image: alpine:3 + + links: + - endpoints: ["lin1:eth1", lin2:eth1] + - endpoints: ["lin1:eth1", lin2:eth2] + - endpoints: ["lin1:eth3", lin2:eth2] diff --git a/cmd/deploy.go b/cmd/deploy.go index 4f7b2d171..8f627860c 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -54,6 +54,10 @@ var deployCmd = &cobra.Command{ ctx, cancel := context.WithCancel(context.Background()) defer cancel() + if err = c.CheckTopologyDefinition(); err != nil { + return err + } + setFlags(c.Config) log.Debugf("lab Conf: %+v", c.Config) // Parse topology information @@ -71,9 +75,6 @@ var deployCmd = &cobra.Command{ } } - if err = c.VerifyBridgesExist(); err != nil { - return err - } // create lab directory log.Info("Creating lab directory: ", c.Dir.Lab)