Skip to content

Commit

Permalink
Merge remote-tracking branch 'wim/master' into containerd
Browse files Browse the repository at this point in the history
  • Loading branch information
steiler committed Jun 15, 2021
2 parents 81a7b91 + 6c4d97e commit 3f113be
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 76 deletions.
17 changes: 17 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
BIN_DIR = $(shell pwd)/bin
BINARY = $(shell pwd)/bin/containerlab

all: build

build:
mkdir -p $(BIN_DIR)
go build -o $(BINARY) main.go

test:
go test -race ./... -v

lint:
golangci-lint run

clint:
docker run -it --rm -v $$(pwd):/app -w /app golangci/golangci-lint:v1.40.1 golangci-lint run -v
55 changes: 53 additions & 2 deletions clab/clab.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/containernetworking/plugins/pkg/ns"
log "github.com/sirupsen/logrus"
"github.com/srl-labs/containerlab/runtime"
_ "github.com/srl-labs/containerlab/runtime/all"
"github.com/srl-labs/containerlab/types"
"github.com/srl-labs/containerlab/utils"
)
Expand Down Expand Up @@ -57,8 +58,18 @@ func WithTimeout(dur time.Duration) ClabOption {

func WithRuntime(name string, d bool, dur time.Duration, gracefulShutdown bool) ClabOption {
return func(c *CLab) {
c.Runtime = runtime.NewRuntime(name, d, dur, gracefulShutdown)
c.Runtime.SetMgmtNet(c.Config.Mgmt)
if rInit, ok := runtime.ContainerRuntimes[name]; ok {
c.Runtime = rInit()
c.Runtime.Init(
runtime.WithConfig(&runtime.RuntimeConfig{
Timeout: dur,
Debug: d,
}),
runtime.WithMgmtNet(c.Config.Mgmt),
)
return
}
log.Fatalf("unknown container runtime %q", name)
}
}

Expand Down Expand Up @@ -310,6 +321,46 @@ 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) {
wg := new(sync.WaitGroup)

ctrChan := make(chan *types.GenericContainer)
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 {
log.Debugf("Worker %d terminating...", i)
return
}
name := cont.ID
if len(cont.Names) > 0 {
name = strings.TrimLeft(cont.Names[0], "/")
}
err := c.Runtime.DeleteContainer(ctx, cont)
if err != nil {
log.Errorf("could not remove container '%s': %v", name, err)
}
case <-ctx.Done():
return
}
}
}(i)
}
for _, ctr := range containers {
ctr := ctr
ctrChan <- &ctr
}
close(ctrChan)

wg.Wait()

}

func disableTxOffload(n *types.Node) error {
// skip this if node runs in host mode
if strings.ToLower(n.NetworkMode) == "host" {
Expand Down
2 changes: 1 addition & 1 deletion clab/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func TestBindsInitNodeDir(t *testing.T) {
// extract host filesystem path
bind_part := strings.Split(tc.want, ":")
// create folder from filesystem path
os.MkdirAll(bind_part[0], os.ModePerm)
_ = os.MkdirAll(bind_part[0], os.ModePerm)

binds := []string{tc.bind}
err := resolveBindPaths(binds, tc.nodeDir)
Expand Down
36 changes: 1 addition & 35 deletions cmd/destroy.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"os"
"path/filepath"
"strings"
"sync"

log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -193,40 +192,7 @@ func destroyLab(ctx context.Context, c *clab.CLab) (err error) {
}

log.Infof("Destroying lab: %s", c.Config.Name)
ctrChan := make(chan *types.GenericContainer)
wg := new(sync.WaitGroup)
wg.Add(int(maxWorkers))
for i := uint(0); i < maxWorkers; i++ {

go func(i uint) {
defer wg.Done()
for {
select {
case cont := <-ctrChan:
if cont == nil {
log.Debugf("Worker %d terminating...", i)
return
}
if len(cont.Names) > 0 {
name = strings.TrimLeft(cont.Names[0], "/")
}
err := c.Runtime.DeleteContainer(ctx, cont)
if err != nil {
log.Errorf("could not remove container %s: %v", name, err)
}
case <-ctx.Done():
return
}
}
}(i)
}
for _, ctr := range containers {
ctr := ctr
ctrChan <- &ctr
}
close(ctrChan)

wg.Wait()
c.DeleteNodes(ctx, maxWorkers, containers)

// remove the lab directories
if cleanup {
Expand Down
3 changes: 3 additions & 0 deletions runtime/all/all.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package all

import _ "github.com/srl-labs/containerlab/runtime/docker"
48 changes: 33 additions & 15 deletions runtime/containerd/containerd.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/google/shlex"
"github.com/opencontainers/runtime-spec/specs-go"
log "github.com/sirupsen/logrus"
"github.com/srl-labs/containerlab/runtime"
"github.com/srl-labs/containerlab/types"
"github.com/srl-labs/containerlab/utils"
)
Expand All @@ -31,39 +32,56 @@ const (

cniBin = "/opt/cni/bin"
cniCache = "/opt/cni/cache"

dockerRuntimeName = "containerd"
defaultTimeout = 30 * time.Second
)

func init() {
runtime.Register(dockerRuntimeName, func() runtime.ContainerRuntime {
return &ContainerdRuntime{
Mgmt: new(types.MgmtNet),
}
})
}

func (c *ContainerdRuntime) Init(opts ...runtime.RuntimeOption) {
var err error
c.client, err = containerd.New("/run/containerd/containerd.sock")
if err != nil {
log.Fatalf("failed to create docker client: %v", err)
}
for _, o := range opts {
o(c)
}
}

type ContainerdRuntime struct {
client *containerd.Client
timeout time.Duration
Mgmt types.MgmtNet
Mgmt *types.MgmtNet
debug bool
gracefulShutdown bool
}

func NewContainerdRuntime(d bool, dur time.Duration, gracefulShutdown bool) *ContainerdRuntime {
c, err := containerd.New("/run/containerd/containerd.sock")
if err != nil {
log.Fatalf("failed to create containerd client: %v", err)
}

return &ContainerdRuntime{
client: c,
debug: d,
timeout: dur,
gracefulShutdown: gracefulShutdown,
func (c *ContainerdRuntime) WithConfig(cfg *runtime.RuntimeConfig) {
c.timeout = cfg.Timeout
c.debug = cfg.Debug
c.gracefulShutdown = cfg.GracefulShutdown
if c.timeout <= 0 {
c.timeout = defaultTimeout
}
}

func (c *ContainerdRuntime) SetMgmtNet(n *types.MgmtNet) {
func (c *ContainerdRuntime) WithMgmtNet(n *types.MgmtNet) {
if n.Bridge == "" {
netname := "clab"
if n.Network == "" {
netname = n.Network
}
n.Bridge = "br-" + netname
}
c.Mgmt = *n
c.Mgmt = n
}

func (c *ContainerdRuntime) CreateNet(ctx context.Context) error {
Expand Down Expand Up @@ -281,7 +299,7 @@ func (c *ContainerdRuntime) CreateContainer(ctx context.Context, node *types.Nod
return nil
}

func cniInit(cId, ifName string, mgmtNet types.MgmtNet) (*libcni.CNIConfig, *libcni.NetworkConfigList, *libcni.RuntimeConf, error) {
func cniInit(cId, ifName string, mgmtNet *types.MgmtNet) (*libcni.CNIConfig, *libcni.NetworkConfigList, *libcni.RuntimeConf, error) {
// allow overwriting cni plugin binary path via ENV var
cniPath, ok := os.LookupEnv("CNI_BIN")
if !ok {
Expand Down
39 changes: 28 additions & 11 deletions runtime/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,24 @@ import (
"github.com/docker/docker/pkg/stdcopy"
"github.com/google/shlex"
log "github.com/sirupsen/logrus"
"github.com/srl-labs/containerlab/runtime"
"github.com/srl-labs/containerlab/types"
"github.com/srl-labs/containerlab/utils"
)

const sysctlBase = "/proc/sys"
const (
dockerRuntimeName = "docker"
sysctlBase = "/proc/sys"
defaultTimeout = 30 * time.Second
)

func init() {
runtime.Register(dockerRuntimeName, func() runtime.ContainerRuntime {
return &DockerRuntime{
Mgmt: new(types.MgmtNet),
}
})
}

type DockerRuntime struct {
Client *dockerC.Client
Expand All @@ -37,23 +50,27 @@ type DockerRuntime struct {
gracefulShutdown bool
}

func NewDockerRuntime(d bool, dur time.Duration, gracefulShutdown bool) *DockerRuntime {
c, err := dockerC.NewClientWithOpts(dockerC.FromEnv, dockerC.WithAPIVersionNegotiation())
func (c *DockerRuntime) Init(opts ...runtime.RuntimeOption) {
var err error
c.Client, err = dockerC.NewClientWithOpts(dockerC.FromEnv, dockerC.WithAPIVersionNegotiation())
if err != nil {
log.Fatalf("failed to create docker client: %v", err)
}
for _, o := range opts {
o(c)
}
}

return &DockerRuntime{
Client: c,
Mgmt: new(types.MgmtNet),
// TODO: undo this hard-coding
timeout: dur,
debug: d,
gracefulShutdown: gracefulShutdown,
func (c *DockerRuntime) WithConfig(cfg *runtime.RuntimeConfig) {
c.timeout = cfg.Timeout
c.debug = cfg.Debug
c.gracefulShutdown = cfg.GracefulShutdown
if c.timeout <= 0 {
c.timeout = defaultTimeout
}
}

func (c *DockerRuntime) SetMgmtNet(n *types.MgmtNet) {
func (c *DockerRuntime) WithMgmtNet(n *types.MgmtNet) {
c.Mgmt = n
}

Expand Down
41 changes: 29 additions & 12 deletions runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@ package runtime

import (
"context"
"log"
"time"

"github.com/srl-labs/containerlab/runtime/containerd"
"github.com/srl-labs/containerlab/runtime/docker"
"github.com/srl-labs/containerlab/types"
)

Expand All @@ -20,8 +17,12 @@ const (
)

type ContainerRuntime interface {
// Intializes the Container runtime struct
Init(...RuntimeOption)
// Adds custom configuration items to the container runtime struct
WithConfig(*RuntimeConfig)
// Set the network management details (generated by the config.go)
SetMgmtNet(*types.MgmtNet)
WithMgmtNet(*types.MgmtNet)
// Create container (bridge) network
CreateNet(context.Context) error
// Delete container (bridge) network
Expand All @@ -48,14 +49,30 @@ type ContainerRuntime interface {
DeleteContainer(context.Context, *types.GenericContainer) error
}

func NewRuntime(name string, d bool, dur time.Duration, gracefulShutdown bool) ContainerRuntime {
switch name {
case DockerRuntime:
return docker.NewDockerRuntime(d, dur, gracefulShutdown)
case ContainerdRuntime:
return containerd.NewContainerdRuntime(d, dur, gracefulShutdown)
type Initializer func() ContainerRuntime

type RuntimeOption func(ContainerRuntime)

type RuntimeConfig struct {
Timeout time.Duration
GracefulShutdown bool
Debug bool
}

var ContainerRuntimes = map[string]Initializer{}

func Register(name string, initFn Initializer) {
ContainerRuntimes[name] = initFn
}

func WithConfig(cfg *RuntimeConfig) RuntimeOption {
return func(r ContainerRuntime) {
r.WithConfig(cfg)
}
}

log.Fatalf("Unexpected runtime name: %s", name)
return nil
func WithMgmtNet(mgmt *types.MgmtNet) RuntimeOption {
return func(r ContainerRuntime) {
r.WithMgmtNet(mgmt)
}
}

0 comments on commit 3f113be

Please sign in to comment.