Skip to content

Commit

Permalink
Merge pull request kata-containers#287 from caoruidong/hotplug
Browse files Browse the repository at this point in the history
api: add sandbox hotplug network
  • Loading branch information
Sebastien Boeuf authored Aug 16, 2018
2 parents b473dc4 + 7beb309 commit 26f3107
Show file tree
Hide file tree
Showing 28 changed files with 1,930 additions and 228 deletions.
8 changes: 4 additions & 4 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@

[[constraint]]
name = "github.com/intel/govmm"
revision = "6ff20ae2f409df976574d0139b5ec2fa3e314769"
revision = "eda239928bfa12b214e9c93192d548cccf4e7f1e"

[[constraint]]
name = "github.com/kata-containers/agent"
revision = "eec68398287d9491fe648a8e54fb942cf6b6d934"
revision = "e6d26c1bbbaf1c7292b1ee5dfa17ca13159f73b7"

[[constraint]]
name = "github.com/containerd/cri-containerd"
Expand Down
1 change: 1 addition & 0 deletions cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ var runtimeCommands = []cli.Command{
// Kata Containers specific extensions
kataCheckCLICommand,
kataEnvCLICommand,
kataNetworkCLICommand,
factoryCLICommand,
}

Expand Down
203 changes: 203 additions & 0 deletions cli/network.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// Copyright (c) 2018 Huawei Corporation.
//
// SPDX-License-Identifier: Apache-2.0
//

package main

import (
"encoding/json"
"fmt"
"os"

"github.com/kata-containers/agent/protocols/grpc"
vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)

type networkType int

const (
// interfaceType for interface operation
interfaceType networkType = iota

routeType
)

var kataNetworkCLICommand = cli.Command{
Name: "kata-network",
Usage: "manage interfaces and routes for container",
Subcommands: []cli.Command{
addIfaceCommand,
delIfaceCommand,
listIfacesCommand,
updateRoutesCommand,
listRoutesCommand,
},
Action: func(context *cli.Context) error {
return cli.ShowSubcommandHelp(context)
},
}

var addIfaceCommand = cli.Command{
Name: "add-iface",
Usage: "add an interface to a container",
ArgsUsage: `add-iface <container-id> file or - for stdin`,
Flags: []cli.Flag{},
Action: func(context *cli.Context) error {
return networkModifyCommand(context.Args().First(), context.Args().Get(1), interfaceType, true)
},
}

var delIfaceCommand = cli.Command{
Name: "del-iface",
Usage: "delete an interface from a container",
ArgsUsage: `del-iface <container-id> file or - for stdin`,
Flags: []cli.Flag{},
Action: func(context *cli.Context) error {
return networkModifyCommand(context.Args().First(), context.Args().Get(1), interfaceType, false)
},
}

var listIfacesCommand = cli.Command{
Name: "list-ifaces",
Usage: "list network interfaces in a container",
ArgsUsage: `list-ifaces <container-id>`,
Flags: []cli.Flag{},
Action: func(context *cli.Context) error {
return networkListCommand(context.Args().First(), interfaceType)
},
}

var updateRoutesCommand = cli.Command{
Name: "update-routes",
Usage: "update routes of a container",
ArgsUsage: `update-routes <container-id> file or - for stdin`,
Flags: []cli.Flag{},
Action: func(context *cli.Context) error {
return networkModifyCommand(context.Args().First(), context.Args().Get(1), routeType, true)
},
}

var listRoutesCommand = cli.Command{
Name: "list-routes",
Usage: "list network routes in a container",
ArgsUsage: `list-routes <container-id>`,
Flags: []cli.Flag{},
Action: func(context *cli.Context) error {
return networkListCommand(context.Args().First(), routeType)
},
}

func networkModifyCommand(containerID, input string, opType networkType, add bool) (err error) {
status, sandboxID, err := getExistingContainerInfo(containerID)
if err != nil {
return err
}

containerID = status.ID

kataLog = kataLog.WithFields(logrus.Fields{
"container": containerID,
"sandbox": sandboxID,
})

setExternalLoggers(kataLog)

// container MUST be running
if status.State.State != vc.StateRunning {
return fmt.Errorf("container %s is not running", containerID)
}

var (
f *os.File
output = defaultOutputFile
)

if input == "-" {
f = os.Stdin
} else {
f, err = os.Open(input)
if err != nil {
return err
}
defer f.Close()
}
switch opType {
case interfaceType:
var inf, resultingInf *grpc.Interface
if err = json.NewDecoder(f).Decode(&inf); err != nil {
return err
}
if add {
resultingInf, err = vci.AddInterface(sandboxID, inf)
if err != nil {
kataLog.WithField("resulting-interface", fmt.Sprintf("%+v", resultingInf)).
WithError(err).Error("add interface failed")
}
} else {
resultingInf, err = vci.RemoveInterface(sandboxID, inf)
if err != nil {
kataLog.WithField("resulting-interface", fmt.Sprintf("%+v", resultingInf)).
WithError(err).Error("delete interface failed")
}
}
json.NewEncoder(output).Encode(resultingInf)
case routeType:
var routes, resultingRoutes []*grpc.Route
if err = json.NewDecoder(f).Decode(&routes); err != nil {
return err
}
resultingRoutes, err = vci.UpdateRoutes(sandboxID, routes)
json.NewEncoder(output).Encode(resultingRoutes)
if err != nil {
kataLog.WithField("resulting-routes", fmt.Sprintf("%+v", resultingRoutes)).
WithError(err).Error("update routes failed")
}
}
return err
}

func networkListCommand(containerID string, opType networkType) (err error) {
status, sandboxID, err := getExistingContainerInfo(containerID)
if err != nil {
return err
}

containerID = status.ID

kataLog = kataLog.WithFields(logrus.Fields{
"container": containerID,
"sandbox": sandboxID,
})

setExternalLoggers(kataLog)

// container MUST be running
if status.State.State != vc.StateRunning {
return fmt.Errorf("container %s is not running", containerID)
}

var file = defaultOutputFile

switch opType {
case interfaceType:
var interfaces []*grpc.Interface
interfaces, err = vci.ListInterfaces(sandboxID)
if err != nil {
kataLog.WithField("existing-interfaces", fmt.Sprintf("%+v", interfaces)).
WithError(err).Error("list interfaces failed")
}
json.NewEncoder(file).Encode(interfaces)
case routeType:
var routes []*grpc.Route
routes, err = vci.ListRoutes(sandboxID)
if err != nil {
kataLog.WithField("resulting-routes", fmt.Sprintf("%+v", routes)).
WithError(err).Error("update routes failed")
}
json.NewEncoder(file).Encode(routes)
}
return err
}
88 changes: 88 additions & 0 deletions cli/network_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (c) 2018 Huawei Corporation.
//
// SPDX-License-Identifier: Apache-2.0
//

package main

import (
"flag"
"io/ioutil"
"os"
"testing"

"github.com/kata-containers/agent/protocols/grpc"
vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/stretchr/testify/assert"
)

var (
testAddInterfaceFuncReturnNil = func(sandboxID string, inf *grpc.Interface) (*grpc.Interface, error) {
return nil, nil
}
testRemoveInterfaceFuncReturnNil = func(sandboxID string, inf *grpc.Interface) (*grpc.Interface, error) {
return nil, nil
}
testListInterfacesFuncReturnNil = func(sandboxID string) ([]*grpc.Interface, error) {
return nil, nil
}
testUpdateRoutsFuncReturnNil = func(sandboxID string, routes []*grpc.Route) ([]*grpc.Route, error) {
return nil, nil
}
testListRoutesFuncReturnNil = func(sandboxID string) ([]*grpc.Route, error) {
return nil, nil
}
)

func TestNetworkCliFunction(t *testing.T) {
assert := assert.New(t)

state := vc.State{
State: vc.StateRunning,
}

testingImpl.AddInterfaceFunc = testAddInterfaceFuncReturnNil
testingImpl.RemoveInterfaceFunc = testRemoveInterfaceFuncReturnNil
testingImpl.ListInterfacesFunc = testListInterfacesFuncReturnNil
testingImpl.UpdateRoutesFunc = testUpdateRoutsFuncReturnNil
testingImpl.ListRoutesFunc = testListRoutesFuncReturnNil

path, err := createTempContainerIDMapping(testContainerID, testSandboxID)
assert.NoError(err)
defer os.RemoveAll(path)

testingImpl.StatusContainerFunc = func(sandboxID, containerID string) (vc.ContainerStatus, error) {
return newSingleContainerStatus(testContainerID, state, map[string]string{}), nil
}

defer func() {
testingImpl.AddInterfaceFunc = nil
testingImpl.RemoveInterfaceFunc = nil
testingImpl.ListInterfacesFunc = nil
testingImpl.UpdateRoutesFunc = nil
testingImpl.ListRoutesFunc = nil
testingImpl.StatusContainerFunc = nil
}()

set := flag.NewFlagSet("", 0)
execCLICommandFunc(assert, addIfaceCommand, set, true)

set.Parse([]string{testContainerID})
execCLICommandFunc(assert, listIfacesCommand, set, false)
execCLICommandFunc(assert, listRoutesCommand, set, false)

f, err := ioutil.TempFile("", "interface")
defer os.Remove(f.Name())
assert.NoError(err)
assert.NotNil(f)
f.WriteString("{}")

set.Parse([]string{testContainerID, f.Name()})
execCLICommandFunc(assert, addIfaceCommand, set, false)
execCLICommandFunc(assert, delIfaceCommand, set, false)

f.Seek(0, 0)
f.WriteString("[{}]")
f.Close()
execCLICommandFunc(assert, updateRoutesCommand, set, false)
}
2 changes: 2 additions & 0 deletions vendor/github.com/intel/govmm/qemu/image.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 26f3107

Please sign in to comment.