mirror of
https://github.com/containers/podman.git
synced 2025-06-27 13:38:49 +08:00
add network connect|disconnect compat endpoints
this enables the ability to connect and disconnect a container from a given network. it is only for the compatibility layer. some code had to be refactored to avoid circular imports. additionally, tests are being deferred temporarily due to some incompatibility/bug in either docker-py or our stack. Signed-off-by: baude <bbaude@redhat.com>
This commit is contained in:
@ -1296,10 +1296,6 @@ func (s *BoltState) NetworkDisconnect(ctr *Container, network string) error {
|
||||
}
|
||||
|
||||
ctrAliasesBkt := dbCtr.Bucket(aliasesBkt)
|
||||
if ctrAliasesBkt == nil {
|
||||
return errors.Wrapf(define.ErrNoAliases, "container %s has no network aliases", ctr.ID())
|
||||
}
|
||||
|
||||
ctrNetworksBkt := dbCtr.Bucket(networksBkt)
|
||||
if ctrNetworksBkt == nil {
|
||||
return errors.Wrapf(define.ErrNoSuchNetwork, "container %s is not connected to any CNI networks, so cannot disconnect", ctr.ID())
|
||||
@ -1313,13 +1309,15 @@ func (s *BoltState) NetworkDisconnect(ctr *Container, network string) error {
|
||||
return errors.Wrapf(err, "error removing container %s from network %s", ctr.ID(), network)
|
||||
}
|
||||
|
||||
bktExists := ctrAliasesBkt.Bucket([]byte(network))
|
||||
if bktExists == nil {
|
||||
return nil
|
||||
}
|
||||
if ctrAliasesBkt != nil {
|
||||
bktExists := ctrAliasesBkt.Bucket([]byte(network))
|
||||
if bktExists == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := ctrAliasesBkt.DeleteBucket([]byte(network)); err != nil {
|
||||
return errors.Wrapf(err, "error removing container %s network aliases for network %s", ctr.ID(), network)
|
||||
if err := ctrAliasesBkt.DeleteBucket([]byte(network)); err != nil {
|
||||
return errors.Wrapf(err, "error removing container %s network aliases for network %s", ctr.ID(), network)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -1088,3 +1088,17 @@ func (c *Container) networks() ([]string, error) {
|
||||
|
||||
return networks, err
|
||||
}
|
||||
|
||||
// networksByNameIndex provides us with a map of container networks where key
|
||||
// is network name and value is the index position
|
||||
func (c *Container) networksByNameIndex() (map[string]int, error) {
|
||||
networks, err := c.networks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
networkNamesByIndex := make(map[string]int, len(networks))
|
||||
for index, name := range networks {
|
||||
networkNamesByIndex[name] = index
|
||||
}
|
||||
return networkNamesByIndex, nil
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
"github.com/containers/podman/v2/libpod"
|
||||
"github.com/containers/common/pkg/config"
|
||||
"github.com/containers/podman/v2/pkg/domain/entities"
|
||||
"github.com/containers/podman/v2/pkg/rootless"
|
||||
"github.com/containers/podman/v2/pkg/util"
|
||||
@ -16,25 +16,21 @@ import (
|
||||
)
|
||||
|
||||
// Create the CNI network
|
||||
func Create(name string, options entities.NetworkCreateOptions, r *libpod.Runtime) (*entities.NetworkCreateReport, error) {
|
||||
func Create(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (*entities.NetworkCreateReport, error) {
|
||||
var fileName string
|
||||
if err := isSupportedDriver(options.Driver); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config, err := r.GetConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Acquire a lock for CNI
|
||||
l, err := acquireCNILock(filepath.Join(config.Engine.TmpDir, LockFileName))
|
||||
l, err := acquireCNILock(filepath.Join(runtimeConfig.Engine.TmpDir, LockFileName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer l.releaseCNILock()
|
||||
if len(options.MacVLAN) > 0 {
|
||||
fileName, err = createMacVLAN(r, name, options)
|
||||
fileName, err = createMacVLAN(name, options, runtimeConfig)
|
||||
} else {
|
||||
fileName, err = createBridge(r, name, options)
|
||||
fileName, err = createBridge(name, options, runtimeConfig)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -81,17 +77,17 @@ func validateBridgeOptions(options entities.NetworkCreateOptions) error {
|
||||
}
|
||||
|
||||
// createBridge creates a CNI network
|
||||
func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) {
|
||||
func createBridge(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) {
|
||||
var (
|
||||
ipamRanges [][]IPAMLocalHostRangeConf
|
||||
err error
|
||||
routes []IPAMRoute
|
||||
)
|
||||
isGateway := true
|
||||
ipMasq := true
|
||||
runtimeConfig, err := r.GetConfig()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// validate options
|
||||
err = validateBridgeOptions(options)
|
||||
if err != nil {
|
||||
if err := validateBridgeOptions(options); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@ -102,8 +98,6 @@ func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreate
|
||||
subnet := &options.Subnet
|
||||
ipRange := &options.Range
|
||||
gateway := options.Gateway
|
||||
var ipamRanges [][]IPAMLocalHostRangeConf
|
||||
var routes []IPAMRoute
|
||||
if subnet.IP != nil {
|
||||
// if network is provided, does it conflict with existing CNI or live networks
|
||||
err = ValidateUserNetworkIsAvailable(runtimeConfig, subnet)
|
||||
@ -201,7 +195,7 @@ func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreate
|
||||
return cniPathName, err
|
||||
}
|
||||
|
||||
func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) {
|
||||
func createMacVLAN(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) {
|
||||
var (
|
||||
plugins []CNIPlugins
|
||||
)
|
||||
@ -210,17 +204,12 @@ func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreat
|
||||
return "", err
|
||||
}
|
||||
|
||||
config, err := r.GetConfig()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Make sure the host-device exists
|
||||
if !util.StringInSlice(options.MacVLAN, liveNetNames) {
|
||||
return "", errors.Errorf("failed to find network interface %q", options.MacVLAN)
|
||||
}
|
||||
if len(name) > 0 {
|
||||
netNames, err := GetNetworkNamesFromFileSystem(config)
|
||||
netNames, err := GetNetworkNamesFromFileSystem(runtimeConfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -228,7 +217,7 @@ func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreat
|
||||
return "", errors.Errorf("the network name %s is already used", name)
|
||||
}
|
||||
} else {
|
||||
name, err = GetFreeDeviceName(config)
|
||||
name, err = GetFreeDeviceName(runtimeConfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -241,7 +230,7 @@ func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreat
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cniPathName := filepath.Join(GetCNIConfDir(config), fmt.Sprintf("%s.conflist", name))
|
||||
cniPathName := filepath.Join(GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name))
|
||||
err = ioutil.WriteFile(cniPathName, b, 0644)
|
||||
return cniPathName, err
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
@ -20,6 +21,7 @@ import (
|
||||
cnitypes "github.com/containernetworking/cni/pkg/types/current"
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/containers/podman/v2/libpod/define"
|
||||
"github.com/containers/podman/v2/libpod/network"
|
||||
"github.com/containers/podman/v2/pkg/errorhandling"
|
||||
"github.com/containers/podman/v2/pkg/netns"
|
||||
"github.com/containers/podman/v2/pkg/rootless"
|
||||
@ -981,3 +983,139 @@ func (w *logrusDebugWriter) Write(p []byte) (int, error) {
|
||||
logrus.Debugf("%s%s", w.prefix, string(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// DisconnectContainerFromNetwork removes a container from its CNI network
|
||||
func (r *Runtime) DisconnectContainerFromNetwork(nameOrID, netName string, force bool) error {
|
||||
ctr, err := r.LookupContainer(nameOrID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
networks, err := ctr.networksByNameIndex()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exists, err := network.Exists(r.config, netName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return errors.Wrap(define.ErrNoSuchNetwork, netName)
|
||||
}
|
||||
|
||||
index, nameExists := networks[netName]
|
||||
if !nameExists && len(networks) > 0 {
|
||||
return errors.Errorf("container %s is not connected to network %s", nameOrID, netName)
|
||||
}
|
||||
|
||||
ctr.lock.Lock()
|
||||
defer ctr.lock.Unlock()
|
||||
if err := ctr.syncContainer(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
podConfig := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), []string{netName}, ctr.config.PortMappings, nil, nil)
|
||||
if err := r.netPlugin.TearDownPod(podConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.state.NetworkDisconnect(ctr, netName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// update network status
|
||||
networkStatus := ctr.state.NetworkStatus
|
||||
// if len is one and we confirmed earlier that the container is in
|
||||
// fact connected to the network, then just return an empty slice
|
||||
if len(networkStatus) == 1 {
|
||||
ctr.state.NetworkStatus = make([]*cnitypes.Result, 0)
|
||||
} else {
|
||||
// clip out the index of the network
|
||||
networkStatus[len(networkStatus)-1], networkStatus[index] = networkStatus[index], networkStatus[len(networkStatus)-1]
|
||||
// shorten the slice by one
|
||||
ctr.state.NetworkStatus = networkStatus[:len(networkStatus)-1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConnectContainerToNetwork connects a container to a CNI network
|
||||
func (r *Runtime) ConnectContainerToNetwork(nameOrID, netName string, aliases []string) error {
|
||||
ctr, err := r.LookupContainer(nameOrID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
networks, err := ctr.networksByNameIndex()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exists, err := network.Exists(r.config, netName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return errors.Wrap(define.ErrNoSuchNetwork, netName)
|
||||
}
|
||||
|
||||
_, nameExists := networks[netName]
|
||||
if !nameExists && len(networks) > 0 {
|
||||
return errors.Errorf("container %s is not connected to network %s", nameOrID, netName)
|
||||
}
|
||||
|
||||
ctr.lock.Lock()
|
||||
defer ctr.lock.Unlock()
|
||||
if err := ctr.syncContainer(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.state.NetworkConnect(ctr, netName, aliases); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
podConfig := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), []string{netName}, ctr.config.PortMappings, nil, nil)
|
||||
podConfig.Aliases = make(map[string][]string, 1)
|
||||
podConfig.Aliases[netName] = aliases
|
||||
results, err := r.netPlugin.SetUpPod(podConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return errors.New("when adding aliases, results must be of length 1")
|
||||
}
|
||||
|
||||
networkResults := make([]*cnitypes.Result, 0)
|
||||
for _, r := range results {
|
||||
resultCurrent, err := cnitypes.GetResult(r.Result)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error parsing CNI plugin result %q: %v", r.Result, err)
|
||||
}
|
||||
networkResults = append(networkResults, resultCurrent)
|
||||
}
|
||||
|
||||
// update network status
|
||||
networkStatus := ctr.state.NetworkStatus
|
||||
// if len is one and we confirmed earlier that the container is in
|
||||
// fact connected to the network, then just return an empty slice
|
||||
if len(networkStatus) == 0 {
|
||||
ctr.state.NetworkStatus = append(ctr.state.NetworkStatus, networkResults...)
|
||||
} else {
|
||||
// build a list of network names so we can sort and
|
||||
// get the new name's index
|
||||
var networkNames []string
|
||||
for netName := range networks {
|
||||
networkNames = append(networkNames, netName)
|
||||
}
|
||||
networkNames = append(networkNames, netName)
|
||||
// sort
|
||||
sort.Strings(networkNames)
|
||||
// get index of new network name
|
||||
index := sort.SearchStrings(networkNames, netName)
|
||||
// Append a zero value to to the slice
|
||||
networkStatus = append(networkStatus, &cnitypes.Result{})
|
||||
// populate network status
|
||||
copy(networkStatus[index+1:], networkStatus[index:])
|
||||
networkStatus[index] = networkResults[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user