diff --git a/clab/clab.go b/clab/clab.go index b85695e9a..217e5ec9d 100644 --- a/clab/clab.go +++ b/clab/clab.go @@ -60,13 +60,16 @@ func WithRuntime(name string, d bool, dur time.Duration, gracefulShutdown bool) return func(c *CLab) { if rInit, ok := runtime.ContainerRuntimes[name]; ok { c.Runtime = rInit() - c.Runtime.Init( + err := c.Runtime.Init( runtime.WithConfig(&runtime.RuntimeConfig{ Timeout: dur, Debug: d, }), runtime.WithMgmtNet(c.Config.Mgmt), ) + if err != nil { + log.Fatalf("failed to init the container runtime: %s", err) + } return } log.Fatalf("unknown container runtime %q", name) diff --git a/go.mod b/go.mod index ba76d4d09..8aa573f65 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/morikuni/aec v1.0.0 // indirect github.com/olekukonko/tablewriter v0.0.5-0.20201029120751-42e21c7531a3 github.com/opencontainers/runtime-spec v1.0.3-0.20210303205135-43e4633e40c1 + github.com/pkg/errors v0.9.1 // indirect github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.0.0 github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5 diff --git a/runtime/containerd/containerd.go b/runtime/containerd/containerd.go index 85c33aca6..fc4ed1791 100644 --- a/runtime/containerd/containerd.go +++ b/runtime/containerd/containerd.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "os" + "path" "strconv" "strings" "syscall" @@ -21,6 +22,7 @@ import ( "github.com/docker/go-units" "github.com/google/shlex" "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/srl-labs/containerlab/runtime" "github.com/srl-labs/containerlab/types" @@ -37,6 +39,8 @@ const ( defaultTimeout = 30 * time.Second ) +var cniPath string + func init() { runtime.Register(dockerRuntimeName, func() runtime.ContainerRuntime { return &ContainerdRuntime{ @@ -45,15 +49,27 @@ func init() { }) } -func (c *ContainerdRuntime) Init(opts ...runtime.RuntimeOption) { +func (c *ContainerdRuntime) Init(opts ...runtime.RuntimeOption) error { var err error + var ok bool c.client, err = containerd.New("/run/containerd/containerd.sock") if err != nil { - log.Fatalf("failed to create containerd client: %v", err) + return err + } + if cniPath, ok = os.LookupEnv("CNI_BIN"); !ok { + cniPath = cniBin + } + binaries := []string{"tuning", "bridge", "host-local"} + for _, binary := range binaries { + binary = path.Join(cniPath, binary) + if _, err := os.Stat(binary); err != nil { + return errors.WithMessagef(err, "CNI binaries not found. [ %s ] are required.", strings.Join(binaries, ",")) + } } for _, o := range opts { o(c) } + return nil } type ContainerdRuntime struct { @@ -301,10 +317,6 @@ func (c *ContainerdRuntime) CreateContainer(ctx context.Context, node *types.Nod 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 { - cniPath = cniBin - } cnic := libcni.NewCNIConfigWithCacheDir([]string{cniPath}, cniCache, nil) diff --git a/runtime/docker/docker.go b/runtime/docker/docker.go index 1879f5c6f..8055f2ae4 100644 --- a/runtime/docker/docker.go +++ b/runtime/docker/docker.go @@ -50,7 +50,7 @@ type DockerRuntime struct { gracefulShutdown bool } -func (c *DockerRuntime) Init(opts ...runtime.RuntimeOption) { +func (c *DockerRuntime) Init(opts ...runtime.RuntimeOption) error { var err error c.Client, err = dockerC.NewClientWithOpts(dockerC.FromEnv, dockerC.WithAPIVersionNegotiation()) if err != nil { @@ -59,6 +59,7 @@ func (c *DockerRuntime) Init(opts ...runtime.RuntimeOption) { for _, o := range opts { o(c) } + return nil } func (c *DockerRuntime) WithConfig(cfg *runtime.RuntimeConfig) { diff --git a/runtime/runtime.go b/runtime/runtime.go index dc72d0f3c..1a45d6774 100644 --- a/runtime/runtime.go +++ b/runtime/runtime.go @@ -18,7 +18,7 @@ const ( type ContainerRuntime interface { // Intializes the Container runtime struct - Init(...RuntimeOption) + Init(...RuntimeOption) error // Adds custom configuration items to the container runtime struct WithConfig(*RuntimeConfig) // Set the network management details (generated by the config.go)