Skip to content

Commit

Permalink
rebased
Browse files Browse the repository at this point in the history
  • Loading branch information
networkop committed Jul 4, 2021
1 parent d213e78 commit 6229386
Show file tree
Hide file tree
Showing 65 changed files with 2,541 additions and 312 deletions.
80 changes: 58 additions & 22 deletions clab/clab.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package clab

import (
"context"
"fmt"
"os"
"sync"
"time"
Expand All @@ -20,13 +21,14 @@ import (
)

type CLab struct {
Config *Config
TopoFile *TopoFile
m *sync.RWMutex
Nodes map[string]nodes.Node
Links map[int]*types.Link
Runtime runtime.ContainerRuntime
Dir *Directory
Config *Config
TopoFile *TopoFile
m *sync.RWMutex
Nodes map[string]nodes.Node
Links map[int]*types.Link
Runtimes map[string]runtime.ContainerRuntime
globalRuntime string
Dir *Directory

debug bool
timeout time.Duration
Expand Down Expand Up @@ -66,10 +68,11 @@ func WithRuntime(name string, d bool, dur time.Duration, gracefulShutdown bool)
default:
name = runtime.DockerRuntime
}
c.globalRuntime = name

if rInit, ok := runtime.ContainerRuntimes[name]; ok {
c.Runtime = rInit()
err := c.Runtime.Init(
r := rInit()
err := r.Init(
runtime.WithConfig(&runtime.RuntimeConfig{
Timeout: dur,
Debug: d,
Expand All @@ -79,6 +82,9 @@ func WithRuntime(name string, d bool, dur time.Duration, gracefulShutdown bool)
if err != nil {
log.Fatalf("failed to init the container runtime: %s", err)
}

c.Runtimes[name] = r

return
}
log.Fatalf("unknown container runtime %q", name)
Expand Down Expand Up @@ -109,7 +115,7 @@ func WithGracefulShutdown(gracefulShutdown bool) ClabOption {
}

// NewContainerLab function defines a new container lab
func NewContainerLab(opts ...ClabOption) *CLab {
func NewContainerLab(opts ...ClabOption) (*CLab, error) {
c := &CLab{
Config: &Config{
Mgmt: new(types.MgmtNet),
Expand All @@ -119,13 +125,18 @@ func NewContainerLab(opts ...ClabOption) *CLab {
m: new(sync.RWMutex),
Nodes: make(map[string]nodes.Node),
Links: make(map[int]*types.Link),
Runtimes: make(map[string]runtime.ContainerRuntime),
}

for _, o := range opts {
o(c)
}

return c
if err := c.parseTopology(); err != nil {
return nil, err
}

return c, nil
}

// initMgmtNetwork sets management network config
Expand All @@ -150,6 +161,10 @@ func (c *CLab) initMgmtNetwork() error {
return nil
}

func (c *CLab) GlobalRuntime() runtime.ContainerRuntime {
return c.Runtimes[c.globalRuntime]
}

func (c *CLab) CreateNodes(ctx context.Context, workers uint) {
wg := new(sync.WaitGroup)
wg.Add(int(workers))
Expand All @@ -173,7 +188,7 @@ func (c *CLab) CreateNodes(ctx context.Context, workers uint) {
continue
}
// Deploy
err = node.Deploy(ctx, c.Runtime)
err = node.Deploy(ctx)
if err != nil {
log.Errorf("failed deploy phase for node %q: %v", node.Config().ShortName, err)
continue
Expand Down Expand Up @@ -247,37 +262,58 @@ func (c *CLab) CreateLinks(ctx context.Context, workers uint, postdeploy bool) {
wg.Wait()
}

func (c *CLab) DeleteNodes(ctx context.Context, workers uint, containers []types.GenericContainer) {
func (c *CLab) DeleteNodes(ctx context.Context, workers uint, deleteCandidates map[string]nodes.Node) {
wg := new(sync.WaitGroup)

ctrChan := make(chan *types.GenericContainer)
nodeChan := make(chan nodes.Node)
wg.Add(int(workers))
for i := uint(0); i < workers; i++ {
go func(i uint) {
defer wg.Done()
for {
select {
case cont := <-ctrChan:
if cont == nil {
case n := <-nodeChan:
if n == nil {
log.Debugf("Worker %d terminating...", i)
return
}
err := c.Runtime.DeleteContainer(ctx, cont)
err := n.Delete(ctx)
if err != nil {
log.Errorf("could not remove container '%s': %v", cont.ID, err)
log.Errorf("could not remove container %q: %v", n.GetName(), err)
}
case <-ctx.Done():
return
}
}
}(i)
}
for _, ctr := range containers {
ctr := ctr
ctrChan <- &ctr
for _, n := range deleteCandidates {
nodeChan <- n
}
close(ctrChan)
close(nodeChan)

wg.Wait()

}

func (c *CLab) ListContainers(ctx context.Context, labels []*types.GenericFilter) ([]types.GenericContainer, error) {
var containers []types.GenericContainer

for _, r := range c.Runtimes {
ctrs, err := r.ListContainers(ctx, labels)
if err != nil {
return containers, fmt.Errorf("could not list containers: %v", err)
}
containers = append(containers, ctrs...)
}
return containers, nil
}

func (c *CLab) GetNodeRuntime(query string) (runtime.ContainerRuntime, error) {
for _, node := range c.Nodes {
if node.GetName() == query {
return node.GetRuntime(), nil
}
}
return nil, fmt.Errorf("could not find a container matching name %q", query)
}
43 changes: 28 additions & 15 deletions clab/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ var kinds = []string{
"ovs-bridge",
"mysocketio",
"host",
"cvx",
}

// Config defines lab configuration as it is provided in the YAML file
Expand All @@ -68,7 +69,7 @@ type Config struct {
}

// ParseTopology parses the lab topology
func (c *CLab) ParseTopology() error {
func (c *CLab) parseTopology() error {
log.Infof("Parsing & checking topology file: %s", c.TopoFile.fullName)
log.Debugf("Lab name: %s", c.Config.Name)

Expand Down Expand Up @@ -103,6 +104,12 @@ func (c *CLab) ParseTopology() error {
// i represents the endpoint integer and l provide the link struct
c.Links[i] = c.NewLink(l)
}

// update runtimes in case overridden by nodes
for _, node := range c.Nodes {
c.Runtimes[node.GetRuntime().GetName()] = node.GetRuntime()
}

return nil
}

Expand All @@ -120,7 +127,8 @@ func (c *CLab) NewNode(nodeName string, nodeDef *types.NodeDefinition, idx int)
}
n := nodeInitializer()
// Init
err = n.Init(nodeCfg, nodes.WithMgmtNet(c.Config.Mgmt))

err = n.Init(nodeCfg, nodes.WithRuntime(c.GlobalRuntime()), nodes.WithMgmtNet(c.Config.Mgmt))
if err != nil {
log.Errorf("failed to initialize node %q: %v", nodeCfg.ShortName, err)
return fmt.Errorf("failed to initialize node %q: %v", nodeCfg.ShortName, err)
Expand Down Expand Up @@ -161,6 +169,9 @@ func (c *CLab) createNodeCfg(nodeName string, nodeDef *types.NodeDefinition, idx
Publish: c.Config.Topology.GetNodePublish(nodeName),
Sysctls: make(map[string]string),
Endpoints: make([]*types.Endpoint, 0),
Sandbox: nodeDef.Sandbox,
Kernel: nodeDef.Kernel,
Runtime: nodeDef.Runtime,
}

log.Debugf("node config: %+v", nodeCfg)
Expand Down Expand Up @@ -323,21 +334,22 @@ func (c *CLab) verifyLinks() error {
// VerifyImages will check if image referred in the node config
// either pullable or is available in the local image store
func (c *CLab) VerifyImages(ctx context.Context) error {
images := map[string]struct{}{}

images := make(map[string]string)

for _, node := range c.Nodes {
// skip image verification for bridge kinds
if node.Config().Kind == "bridge" || node.Config().Kind == "ovs-bridge" {
continue
}
if node.Config().Image == "" {
return fmt.Errorf("missing required image for node %s", node.Config().ShortName)

for _, imageName := range node.GetImages() {
if imageName == "" {
log.Errorf("missing required image for node %q", node.Config().ShortName)
}
images[imageName] = node.GetRuntime().GetName()
}

images[node.Config().Image] = struct{}{}
}

for image := range images {
err := c.Runtime.PullImageIfRequired(ctx, image)
for image, runtimeName := range images {
err := c.Runtimes[runtimeName].PullImageIfRequired(ctx, image)
if err != nil {
return err
}
Expand All @@ -351,10 +363,11 @@ func (c *CLab) VerifyContainersUniqueness(ctx context.Context) error {
nctx, cancel := context.WithTimeout(ctx, c.timeout)
defer cancel()

containers, err := c.Runtime.ListContainers(nctx, nil)
containers, err := c.ListContainers(nctx, nil)
if err != nil {
return fmt.Errorf("could not list containers: %v", err)
return err
}

if len(containers) == 0 {
return nil
}
Expand All @@ -380,7 +393,7 @@ func (c *CLab) VerifyContainersUniqueness(ctx context.Context) error {
}
}

return err
return nil
}

// verifyHostIfaces ensures that host interfaces referenced in the topology
Expand Down
42 changes: 23 additions & 19 deletions clab/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ func TestLicenseInit(t *testing.T) {
opts := []ClabOption{
WithTopoFile(tc.got),
}
c := NewContainerLab(opts...)
if err := c.ParseTopology(); err != nil {
c, err := NewContainerLab(opts...)
if err != nil {
t.Fatal(err)
}

// fmt.Println(c.Config.Topology.Defaults)
// fmt.Println(c.Config.Topology.Kinds)
// fmt.Println(c.Config.Topology.Nodes)
Expand Down Expand Up @@ -90,8 +91,8 @@ func TestBindsInit(t *testing.T) {
opts := []ClabOption{
WithTopoFile(tc.got),
}
c := NewContainerLab(opts...)
if err := c.ParseTopology(); err != nil {
c, err := NewContainerLab(opts...)
if err != nil {
t.Fatal(err)
}

Expand All @@ -102,7 +103,7 @@ func TestBindsInit(t *testing.T) {
// binds := c.bindsInit(nodeCfg)
binds := c.Config.Topology.GetNodeBinds("node1")
// resolve wanted paths as the binds paths are resolved as part of the c.ParseTopology
err := resolveBindPaths(tc.want, node.LabDir)
err = resolveBindPaths(tc.want, node.LabDir)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -182,10 +183,11 @@ func TestTypeInit(t *testing.T) {
opts := []ClabOption{
WithTopoFile(tc.got),
}
c := NewContainerLab(opts...)
if err := c.ParseTopology(); err != nil {
c, err := NewContainerLab(opts...)
if err != nil {
t.Fatal(err)
}

if !reflect.DeepEqual(c.Nodes[tc.node].Config().NodeType, tc.want) {
t.Fatalf("wanted %q got %q", tc.want, c.Nodes[tc.node].Config().NodeType)
}
Expand Down Expand Up @@ -239,8 +241,8 @@ func TestEnvInit(t *testing.T) {
opts := []ClabOption{
WithTopoFile(tc.got),
}
c := NewContainerLab(opts...)
if err := c.ParseTopology(); err != nil {
c, err := NewContainerLab(opts...)
if err != nil {
t.Fatal(err)
}

Expand Down Expand Up @@ -288,8 +290,8 @@ func TestUserInit(t *testing.T) {
opts := []ClabOption{
WithTopoFile(tc.got),
}
c := NewContainerLab(opts...)
if err := c.ParseTopology(); err != nil {
c, err := NewContainerLab(opts...)
if err != nil {
t.Fatal(err)
}

Expand Down Expand Up @@ -324,9 +326,12 @@ func TestVerifyLinks(t *testing.T) {
opts := []ClabOption{
WithTopoFile(tc.got),
}
c := NewContainerLab(opts...)
c, err := NewContainerLab(opts...)
if err != nil {
t.Fatal(err)
}

err := c.verifyLinks()
err = c.verifyLinks()
if err != nil && err.Error() != tc.want {
t.Fatalf("wanted %q got %q", tc.want, err.Error())
}
Expand Down Expand Up @@ -406,8 +411,8 @@ func TestLabelsInit(t *testing.T) {
opts := []ClabOption{
WithTopoFile(tc.got),
}
c := NewContainerLab(opts...)
if err := c.ParseTopology(); err != nil {
c, err := NewContainerLab(opts...)
if err != nil {
t.Fatal(err)
}

Expand All @@ -428,13 +433,12 @@ func TestVerifyRootNetnsInterfaceUniqueness(t *testing.T) {
opts := []ClabOption{
WithTopoFile("test_data/topo7-dup-rootnetns.yml"),
}
c := NewContainerLab(opts...)

if err := c.ParseTopology(); err != nil {
c, err := NewContainerLab(opts...)
if err != nil {
t.Fatal(err)
}

err := c.verifyRootNetnsInterfaceUniqueness()
err = c.verifyRootNetnsInterfaceUniqueness()
if err == nil {
t.Fatalf("expected duplicate rootns links error")
}
Expand Down
Loading

0 comments on commit 6229386

Please sign in to comment.