diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/boltdb_state.go | 18 | ||||
-rw-r--r-- | libpod/container.go | 14 | ||||
-rw-r--r-- | libpod/define/pod_inspect.go | 2 | ||||
-rw-r--r-- | libpod/filters/containers.go | 163 | ||||
-rw-r--r-- | libpod/network/create.go | 43 | ||||
-rw-r--r-- | libpod/networking_linux.go | 138 | ||||
-rw-r--r-- | libpod/pod_api.go | 2 |
7 files changed, 284 insertions, 96 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index be0adfe6a..dcb2ff751 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -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 diff --git a/libpod/container.go b/libpod/container.go index 333e1d848..9009a4ec8 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -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 +} diff --git a/libpod/define/pod_inspect.go b/libpod/define/pod_inspect.go index a4115eb92..2fa91166f 100644 --- a/libpod/define/pod_inspect.go +++ b/libpod/define/pod_inspect.go @@ -67,7 +67,7 @@ type InspectPodInfraConfig struct { StaticIP net.IP // StaticMAC is a static MAC address that will be assigned to the infra // container and then used by the pod. - StaticMAC net.HardwareAddr + StaticMAC string // NoManageResolvConf indicates that the pod will not manage resolv.conf // and instead each container will handle their own. NoManageResolvConf bool diff --git a/libpod/filters/containers.go b/libpod/filters/containers.go index da1b5b263..2520c4f30 100644 --- a/libpod/filters/containers.go +++ b/libpod/filters/containers.go @@ -1,7 +1,6 @@ package lpfilters import ( - "regexp" "strconv" "strings" "time" @@ -11,101 +10,133 @@ import ( "github.com/containers/podman/v2/pkg/timetype" "github.com/containers/podman/v2/pkg/util" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) // GenerateContainerFilterFuncs return ContainerFilter functions based of filter. -func GenerateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) { +func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) { switch filter { case "id": + // we only have to match one ID return func(c *libpod.Container) bool { - return strings.Contains(c.ID(), filterValue) + return util.StringMatchRegexSlice(c.ID(), filterValues) }, nil case "label": - var filterArray = strings.SplitN(filterValue, "=", 2) - var filterKey = filterArray[0] - if len(filterArray) > 1 { - filterValue = filterArray[1] - } else { - filterValue = "" - } + // we have to match that all given labels exits on that container return func(c *libpod.Container) bool { - for labelKey, labelValue := range c.Labels() { - if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { - return true + labels := c.Labels() + for _, filterValue := range filterValues { + matched := false + filterArray := strings.SplitN(filterValue, "=", 2) + filterKey := filterArray[0] + if len(filterArray) > 1 { + filterValue = filterArray[1] + } else { + filterValue = "" + } + for labelKey, labelValue := range labels { + if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { + matched = true + break + } + } + if !matched { + return false } } - return false + return true }, nil case "name": + // we only have to match one name return func(c *libpod.Container) bool { - match, err := regexp.MatchString(filterValue, c.Name()) - if err != nil { - logrus.Errorf("Failed to compile regex for 'name' filter: %v", err) - return false - } - return match + return util.StringMatchRegexSlice(c.Name(), filterValues) }, nil case "exited": - exitCode, err := strconv.ParseInt(filterValue, 10, 32) - if err != nil { - return nil, errors.Wrapf(err, "exited code out of range %q", filterValue) + var exitCodes []int32 + for _, exitCode := range filterValues { + ec, err := strconv.ParseInt(exitCode, 10, 32) + if err != nil { + return nil, errors.Wrapf(err, "exited code out of range %q", ec) + } + exitCodes = append(exitCodes, int32(ec)) } return func(c *libpod.Container) bool { ec, exited, err := c.ExitCode() - if ec == int32(exitCode) && err == nil && exited { - return true + if err == nil && exited { + for _, exitCode := range exitCodes { + if ec == exitCode { + return true + } + } } return false }, nil case "status": - if !util.StringInSlice(filterValue, []string{"created", "running", "paused", "stopped", "exited", "unknown"}) { - return nil, errors.Errorf("%s is not a valid status", filterValue) + for _, filterValue := range filterValues { + if !util.StringInSlice(filterValue, []string{"created", "running", "paused", "stopped", "exited", "unknown"}) { + return nil, errors.Errorf("%s is not a valid status", filterValue) + } } return func(c *libpod.Container) bool { status, err := c.State() if err != nil { return false } - if filterValue == "stopped" { - filterValue = "exited" - } state := status.String() if status == define.ContainerStateConfigured { state = "created" } else if status == define.ContainerStateStopped { state = "exited" } - return state == filterValue + for _, filterValue := range filterValues { + if filterValue == "stopped" { + filterValue = "exited" + } + if state == filterValue { + return true + } + } + return false }, nil case "ancestor": // This needs to refine to match docker // - ancestor=(<image-name>[:tag]|<image-id>| ⟨image@digest⟩) - containers created from an image or a descendant. return func(c *libpod.Container) bool { - containerConfig := c.Config() - if strings.Contains(containerConfig.RootfsImageID, filterValue) || strings.Contains(containerConfig.RootfsImageName, filterValue) { - return true + for _, filterValue := range filterValues { + containerConfig := c.Config() + if strings.Contains(containerConfig.RootfsImageID, filterValue) || strings.Contains(containerConfig.RootfsImageName, filterValue) { + return true + } } return false }, nil case "before": - ctr, err := r.LookupContainer(filterValue) - if err != nil { - return nil, errors.Errorf("unable to find container by name or id of %s", filterValue) + var createTime time.Time + for _, filterValue := range filterValues { + ctr, err := r.LookupContainer(filterValue) + if err != nil { + return nil, err + } + containerConfig := ctr.Config() + if createTime.IsZero() || createTime.After(containerConfig.CreatedTime) { + createTime = containerConfig.CreatedTime + } } - containerConfig := ctr.Config() - createTime := containerConfig.CreatedTime return func(c *libpod.Container) bool { cc := c.Config() return createTime.After(cc.CreatedTime) }, nil case "since": - ctr, err := r.LookupContainer(filterValue) - if err != nil { - return nil, errors.Errorf("unable to find container by name or id of %s", filterValue) + var createTime time.Time + for _, filterValue := range filterValues { + ctr, err := r.LookupContainer(filterValue) + if err != nil { + return nil, err + } + containerConfig := ctr.Config() + if createTime.IsZero() || createTime.After(containerConfig.CreatedTime) { + createTime = containerConfig.CreatedTime + } } - containerConfig := ctr.Config() - createTime := containerConfig.CreatedTime return func(c *libpod.Container) bool { cc := c.Config() return createTime.Before(cc.CreatedTime) @@ -115,17 +146,27 @@ func GenerateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) return func(c *libpod.Container) bool { containerConfig := c.Config() var dest string - arr := strings.Split(filterValue, ":") - source := arr[0] - if len(arr) == 2 { - dest = arr[1] - } - for _, mount := range containerConfig.Spec.Mounts { - if dest != "" && (mount.Source == source && mount.Destination == dest) { - return true + for _, filterValue := range filterValues { + arr := strings.SplitN(filterValue, ":", 2) + source := arr[0] + if len(arr) == 2 { + dest = arr[1] } - if dest == "" && mount.Source == source { - return true + for _, mount := range containerConfig.Spec.Mounts { + if dest != "" && (mount.Source == source && mount.Destination == dest) { + return true + } + if dest == "" && mount.Source == source { + return true + } + } + for _, vname := range containerConfig.NamedVolumes { + if dest != "" && (vname.Name == source && vname.Dest == dest) { + return true + } + if dest == "" && vname.Name == source { + return true + } } } return false @@ -136,10 +177,18 @@ func GenerateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) if err != nil { return false } - return hcStatus == filterValue + for _, filterValue := range filterValues { + if hcStatus == filterValue { + return true + } + } + return false }, nil case "until": - ts, err := timetype.GetTimestamp(filterValue, time.Now()) + if len(filterValues) != 1 { + return nil, errors.Errorf("specify exactly one timestamp for %s", filter) + } + ts, err := timetype.GetTimestamp(filterValues[0], time.Now()) if err != nil { return nil, err } diff --git a/libpod/network/create.go b/libpod/network/create.go index c11904ecf..387f4fcd3 100644 --- a/libpod/network/create.go +++ b/libpod/network/create.go @@ -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 } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index fed90cfc3..3882e095a 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -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 +} diff --git a/libpod/pod_api.go b/libpod/pod_api.go index 87ac5c07a..845948dd3 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -535,7 +535,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { infraConfig = new(define.InspectPodInfraConfig) infraConfig.HostNetwork = p.config.InfraContainer.HostNetwork infraConfig.StaticIP = p.config.InfraContainer.StaticIP - infraConfig.StaticMAC = p.config.InfraContainer.StaticMAC + infraConfig.StaticMAC = p.config.InfraContainer.StaticMAC.String() infraConfig.NoManageResolvConf = p.config.InfraContainer.UseImageResolvConf infraConfig.NoManageHosts = p.config.InfraContainer.UseImageHosts |