mirror of
https://github.com/containers/podman.git
synced 2025-06-20 09:03:43 +08:00
podman network label support
Add label support for podman network create. Use the `args` field in the cni config file to store the podman labels. Use `podman_labels` as key name and store the labels as map[string]string. For reference: https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md#args-in-network-config https://github.com/containernetworking/cni/blob/spec-v0.4.0/SPEC.md#network-configuration Example snippet: ``` ... "args": { "podman_labels": { "key1":"value1", "key2":"value2" } } ... ``` Make podman network list support several filters. Supported filters are name, plugin, driver and label. Filters with different keys work exclusive. Several label filters work exclusive and the other filter keys are working inclusive. Also adjust the compat api to support labels in network create and list. Breaking changes: - podman network ls -f shortform is used for --filter instead --format This matches docker and other podman commands (container ps, volume ps) - libpod network list endpoint filter parameter is removed. Instead the filters paramter should be used as json encoded map[string][]string. Signed-off-by: Paul Holzinger <paul.holzinger@web.de>
This commit is contained in:
@ -6,9 +6,11 @@ import (
|
||||
|
||||
"github.com/containers/common/pkg/completion"
|
||||
"github.com/containers/podman/v2/cmd/podman/common"
|
||||
"github.com/containers/podman/v2/cmd/podman/parse"
|
||||
"github.com/containers/podman/v2/cmd/podman/registry"
|
||||
"github.com/containers/podman/v2/libpod/define"
|
||||
"github.com/containers/podman/v2/pkg/domain/entities"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -27,6 +29,7 @@ var (
|
||||
|
||||
var (
|
||||
networkCreateOptions entities.NetworkCreateOptions
|
||||
labels []string
|
||||
)
|
||||
|
||||
func networkCreateFlags(cmd *cobra.Command) {
|
||||
@ -50,6 +53,10 @@ func networkCreateFlags(cmd *cobra.Command) {
|
||||
flags.StringVar(&networkCreateOptions.MacVLAN, macvlanFlagName, "", "create a Macvlan connection based on this device")
|
||||
_ = cmd.RegisterFlagCompletionFunc(macvlanFlagName, completion.AutocompleteNone)
|
||||
|
||||
labelFlagName := "label"
|
||||
flags.StringArrayVar(&labels, labelFlagName, nil, "set metadata on a network")
|
||||
_ = cmd.RegisterFlagCompletionFunc(labelFlagName, completion.AutocompleteNone)
|
||||
|
||||
// TODO not supported yet
|
||||
// flags.StringVar(&networkCreateOptions.IPamDriver, "ipam-driver", "", "IP Address Management Driver")
|
||||
|
||||
@ -81,6 +88,11 @@ func networkCreate(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
name = args[0]
|
||||
}
|
||||
var err error
|
||||
networkCreateOptions.Labels, err = parse.GetAllLabels([]string{}, labels)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to parse labels")
|
||||
}
|
||||
response, err := registry.ContainerEngine().NetworkCreate(registry.Context(), name, networkCreateOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"github.com/containers/podman/v2/cmd/podman/validate"
|
||||
"github.com/containers/podman/v2/libpod/network"
|
||||
"github.com/containers/podman/v2/pkg/domain/entities"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
@ -35,17 +36,18 @@ var (
|
||||
|
||||
var (
|
||||
networkListOptions entities.NetworkListOptions
|
||||
filters []string
|
||||
)
|
||||
|
||||
func networkListFlags(flags *pflag.FlagSet) {
|
||||
formatFlagName := "format"
|
||||
flags.StringVarP(&networkListOptions.Format, formatFlagName, "f", "", "Pretty-print networks to JSON or using a Go template")
|
||||
flags.StringVar(&networkListOptions.Format, formatFlagName, "", "Pretty-print networks to JSON or using a Go template")
|
||||
_ = networklistCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat)
|
||||
|
||||
flags.BoolVarP(&networkListOptions.Quiet, "quiet", "q", false, "display only names")
|
||||
|
||||
filterFlagName := "filter"
|
||||
flags.StringVarP(&networkListOptions.Filter, filterFlagName, "", "", "Provide filter values (e.g. 'name=podman')")
|
||||
flags.StringArrayVarP(&filters, filterFlagName, "f", nil, "Provide filter values (e.g. 'name=podman')")
|
||||
_ = networklistCommand.RegisterFlagCompletionFunc(filterFlagName, common.AutocompleteNetworkFilters)
|
||||
|
||||
}
|
||||
@ -61,14 +63,14 @@ func init() {
|
||||
}
|
||||
|
||||
func networkList(cmd *cobra.Command, args []string) error {
|
||||
// validate the filter pattern.
|
||||
if len(networkListOptions.Filter) > 0 {
|
||||
tokens := strings.Split(networkListOptions.Filter, "=")
|
||||
if len(tokens) != 2 {
|
||||
return fmt.Errorf("invalid filter syntax : %s", networkListOptions.Filter)
|
||||
networkListOptions.Filters = make(map[string][]string)
|
||||
for _, f := range filters {
|
||||
split := strings.SplitN(f, "=", 2)
|
||||
if len(split) == 1 {
|
||||
return errors.Errorf("invalid filter %q", f)
|
||||
}
|
||||
networkListOptions.Filters[split[0]] = append(networkListOptions.Filters[split[0]], split[1])
|
||||
}
|
||||
|
||||
responses, err := registry.ContainerEngine().NetworkList(registry.Context(), networkListOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -93,6 +95,7 @@ func networkList(cmd *cobra.Command, args []string) error {
|
||||
"CNIVersion": "version",
|
||||
"Version": "version",
|
||||
"Plugins": "plugins",
|
||||
"Labels": "labels",
|
||||
})
|
||||
renderHeaders := true
|
||||
row := "{{.Name}}\t{{.Version}}\t{{.Plugins}}\n"
|
||||
@ -144,3 +147,11 @@ func (n ListPrintReports) Version() string {
|
||||
func (n ListPrintReports) Plugins() string {
|
||||
return network.GetCNIPlugins(n.NetworkConfigList)
|
||||
}
|
||||
|
||||
func (n ListPrintReports) Labels() string {
|
||||
list := make([]string, 0, len(n.NetworkListReport.Labels))
|
||||
for k, v := range n.NetworkListReport.Labels {
|
||||
list = append(list, k+"="+v)
|
||||
}
|
||||
return strings.Join(list, ",")
|
||||
}
|
||||
|
@ -40,6 +40,10 @@ Restrict external access of this network
|
||||
Allocate container IP from a range. The range must be a complete subnet and in CIDR notation. The *ip-range* option
|
||||
must be used with a *subnet* option.
|
||||
|
||||
#### **--label**
|
||||
|
||||
Set metadata for a network (e.g., --label mykey=value).
|
||||
|
||||
#### **--macvlan**
|
||||
|
||||
Create a *Macvlan* based connection rather than a classic bridge. You must pass an interface name from the host for the
|
||||
|
@ -14,13 +14,25 @@ Displays a list of existing podman networks. This command is not available for r
|
||||
|
||||
The `quiet` option will restrict the output to only the network names.
|
||||
|
||||
#### **--format**, **-f**
|
||||
#### **--format**
|
||||
|
||||
Pretty-print networks to JSON or using a Go template.
|
||||
|
||||
#### **--filter**
|
||||
#### **--filter**, **-f**
|
||||
|
||||
Provide filter values (e.g. 'name=podman').
|
||||
Filter output based on conditions given.
|
||||
Multiple filters can be given with multiple uses of the --filter flag.
|
||||
Filters with the same key work inclusive with the only exception being
|
||||
`label` which is exclusive. Filters with different keys always work exclusive.
|
||||
|
||||
Valid filters are listed below:
|
||||
|
||||
| **Filter** | **Description** |
|
||||
| ---------- | ------------------------------------------------------------------------------------- |
|
||||
| name | [Name] Network name (accepts regex) |
|
||||
| label | [Key] or [Key=Value] Label assigned to a network |
|
||||
| plugin | [Plugin] CNI plugins included in a network (e.g `bridge`,`portmap`,`firewall`,`tuning`,`dnsname`,`macvlan`) |
|
||||
| driver | [Driver] Only `bridge` is supported |
|
||||
|
||||
## EXAMPLE
|
||||
|
||||
|
@ -169,7 +169,7 @@ func createBridge(name string, options entities.NetworkCreateOptions, runtimeCon
|
||||
}
|
||||
|
||||
// create CNI plugin configuration
|
||||
ncList := NewNcList(name, version.Current())
|
||||
ncList := NewNcList(name, version.Current(), options.Labels)
|
||||
var plugins []CNIPlugins
|
||||
// TODO need to iron out the role of isDefaultGW and IPMasq
|
||||
bridge := NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, ipamConfig)
|
||||
@ -223,7 +223,7 @@ func createMacVLAN(name string, options entities.NetworkCreateOptions, runtimeCo
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
ncList := NewNcList(name, version.Current())
|
||||
ncList := NewNcList(name, version.Current(), options.Labels)
|
||||
macvlan := NewMacVLANPlugin(options.MacVLAN)
|
||||
plugins = append(plugins, macvlan)
|
||||
ncList["plugins"] = plugins
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/containers/common/pkg/config"
|
||||
"github.com/containers/podman/v2/libpod/define"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// GetCNIConfDir get CNI configuration directory
|
||||
@ -86,6 +87,35 @@ func GetCNIPlugins(list *libcni.NetworkConfigList) string {
|
||||
return strings.Join(plugins, ",")
|
||||
}
|
||||
|
||||
// GetNetworkLabels returns a list of labels as a string
|
||||
func GetNetworkLabels(list *libcni.NetworkConfigList) NcLabels {
|
||||
cniJSON := make(map[string]interface{})
|
||||
err := json.Unmarshal(list.Bytes, &cniJSON)
|
||||
if err != nil {
|
||||
logrus.Errorf("failed to unmarshal network config %v %v", cniJSON["name"], err)
|
||||
return nil
|
||||
}
|
||||
if args, ok := cniJSON["args"]; ok {
|
||||
if key, ok := args.(map[string]interface{}); ok {
|
||||
if labels, ok := key[PodmanLabelKey]; ok {
|
||||
if labels, ok := labels.(map[string]interface{}); ok {
|
||||
result := make(NcLabels, len(labels))
|
||||
for k, v := range labels {
|
||||
if v, ok := v.(string); ok {
|
||||
result[k] = v
|
||||
} else {
|
||||
logrus.Errorf("network config %v invalid label value type %T should be string", cniJSON["name"], labels)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
logrus.Errorf("network config %v invalid label type %T should be map[string]string", cniJSON["name"], labels)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNetworksFromFilesystem gets all the networks from the cni configuration
|
||||
// files
|
||||
func GetNetworksFromFilesystem(config *config.Config) ([]*allocator.Net, error) {
|
||||
|
@ -4,6 +4,11 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containernetworking/cni/libcni"
|
||||
"github.com/containers/podman/v2/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -14,12 +19,24 @@ const (
|
||||
// NcList describes a generic map
|
||||
type NcList map[string]interface{}
|
||||
|
||||
// NcArgs describes the cni args field
|
||||
type NcArgs map[string]NcLabels
|
||||
|
||||
// NcLabels describes the label map
|
||||
type NcLabels map[string]string
|
||||
|
||||
// PodmanLabelKey key used to store the podman network label in a cni config
|
||||
const PodmanLabelKey = "podman_labels"
|
||||
|
||||
// NewNcList creates a generic map of values with string
|
||||
// keys and adds in version and network name
|
||||
func NewNcList(name, version string) NcList {
|
||||
func NewNcList(name, version string, labels NcLabels) NcList {
|
||||
n := NcList{}
|
||||
n["cniVersion"] = version
|
||||
n["name"] = name
|
||||
if len(labels) > 0 {
|
||||
n["args"] = NcArgs{PodmanLabelKey: labels}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@ -159,3 +176,64 @@ func NewMacVLANPlugin(device string) MacVLANConfig {
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// IfPassesFilter filters NetworkListReport and returns true if the filter match the given config
|
||||
func IfPassesFilter(netconf *libcni.NetworkConfigList, filters map[string][]string) (bool, error) {
|
||||
result := true
|
||||
for key, filterValues := range filters {
|
||||
result = false
|
||||
switch strings.ToLower(key) {
|
||||
case "name":
|
||||
// matches one name, regex allowed
|
||||
result = util.StringMatchRegexSlice(netconf.Name, filterValues)
|
||||
|
||||
case "plugin":
|
||||
// match one plugin
|
||||
plugins := GetCNIPlugins(netconf)
|
||||
for _, val := range filterValues {
|
||||
if strings.Contains(plugins, val) {
|
||||
result = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
case "label":
|
||||
// matches all labels
|
||||
labels := GetNetworkLabels(netconf)
|
||||
outer:
|
||||
for _, filterValue := range filterValues {
|
||||
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) {
|
||||
result = true
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
result = false
|
||||
}
|
||||
|
||||
case "driver":
|
||||
// matches only for the DefaultNetworkDriver
|
||||
for _, filterValue := range filterValues {
|
||||
plugins := GetCNIPlugins(netconf)
|
||||
if filterValue == DefaultNetworkDriver &&
|
||||
strings.Contains(plugins, DefaultNetworkDriver) {
|
||||
result = true
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add dangling filter
|
||||
// TODO TODO: add id filter if we support ids
|
||||
|
||||
default:
|
||||
return false, errors.Errorf("invalid filter %q", key)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) {
|
||||
utils.NetworkNotFound(w, name, err)
|
||||
return
|
||||
}
|
||||
report, err := getNetworkResourceByName(name, runtime)
|
||||
report, err := getNetworkResourceByName(name, runtime, nil)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
@ -58,7 +58,7 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) {
|
||||
utils.WriteResponse(w, http.StatusOK, report)
|
||||
}
|
||||
|
||||
func getNetworkResourceByName(name string, runtime *libpod.Runtime) (*types.NetworkResource, error) {
|
||||
func getNetworkResourceByName(name string, runtime *libpod.Runtime, filters map[string][]string) (*types.NetworkResource, error) {
|
||||
var (
|
||||
ipamConfigs []dockerNetwork.IPAMConfig
|
||||
)
|
||||
@ -85,6 +85,16 @@ func getNetworkResourceByName(name string, runtime *libpod.Runtime) (*types.Netw
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(filters) > 0 {
|
||||
ok, err := network.IfPassesFilter(conf, filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
// do not return the config if we did not match the filter
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// No Bridge plugin means we bail
|
||||
bridge, err := genericPluginsToBridge(conf.Plugins, network.DefaultNetworkDriver)
|
||||
@ -129,14 +139,14 @@ func getNetworkResourceByName(name string, runtime *libpod.Runtime) (*types.Netw
|
||||
Options: nil,
|
||||
Config: ipamConfigs,
|
||||
},
|
||||
Internal: false,
|
||||
Internal: !bridge.IsGW,
|
||||
Attachable: false,
|
||||
Ingress: false,
|
||||
ConfigFrom: dockerNetwork.ConfigReference{},
|
||||
ConfigOnly: false,
|
||||
Containers: containerEndpoints,
|
||||
Options: nil,
|
||||
Labels: nil,
|
||||
Labels: network.GetNetworkLabels(conf),
|
||||
Peers: nil,
|
||||
Services: nil,
|
||||
}
|
||||
@ -180,42 +190,24 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
filterNames, nameFilterExists := query.Filters["name"]
|
||||
// TODO remove when filters are implemented
|
||||
if (!nameFilterExists && len(query.Filters) > 0) || len(query.Filters) > 1 {
|
||||
utils.InternalServerError(w, errors.New("only the name filter for listing networks is implemented"))
|
||||
return
|
||||
}
|
||||
netNames, err := network.GetNetworkNamesFromFileSystem(config)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// filter by name
|
||||
if nameFilterExists {
|
||||
names := []string{}
|
||||
for _, name := range netNames {
|
||||
for _, filter := range filterNames {
|
||||
if strings.Contains(name, filter) {
|
||||
names = append(names, name)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
netNames = names
|
||||
}
|
||||
|
||||
reports := make([]*types.NetworkResource, 0, len(netNames))
|
||||
var reports []*types.NetworkResource
|
||||
logrus.Errorf("netNames: %q", strings.Join(netNames, ", "))
|
||||
for _, name := range netNames {
|
||||
report, err := getNetworkResourceByName(name, runtime)
|
||||
report, err := getNetworkResourceByName(name, runtime, query.Filters)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
if report != nil {
|
||||
reports = append(reports, report)
|
||||
}
|
||||
}
|
||||
utils.WriteResponse(w, http.StatusOK, reports)
|
||||
}
|
||||
|
||||
@ -245,6 +237,7 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) {
|
||||
ncOptions := entities.NetworkCreateOptions{
|
||||
Driver: network.DefaultNetworkDriver,
|
||||
Internal: networkCreate.Internal,
|
||||
Labels: networkCreate.Labels,
|
||||
}
|
||||
if networkCreate.IPAM != nil && networkCreate.IPAM.Config != nil {
|
||||
if len(networkCreate.IPAM.Config) > 1 {
|
||||
|
@ -48,7 +48,7 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) {
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
query := struct {
|
||||
Filter string `schema:"filter"`
|
||||
Filters map[string][]string `schema:"filters"`
|
||||
}{
|
||||
// override any golang type defaults
|
||||
}
|
||||
@ -59,7 +59,7 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
options := entities.NetworkListOptions{
|
||||
Filter: query.Filter,
|
||||
Filters: query.Filters,
|
||||
}
|
||||
ic := abi.ContainerEngine{Libpod: runtime}
|
||||
reports, err := ic.NetworkList(r.Context(), options)
|
||||
|
@ -65,7 +65,11 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
|
||||
// - in: query
|
||||
// name: filters
|
||||
// type: string
|
||||
// description: JSON encoded value of the filters (a map[string][]string) to process on the networks list. Only the name filter is supported.
|
||||
// description: |
|
||||
// JSON encoded value of the filters (a map[string][]string) to process on the network list. Currently available filters:
|
||||
// - name=[name] Matches network name (accepts regex).
|
||||
// - driver=[driver] Only bridge is supported.
|
||||
// - label=[key] or label=[key=value] Matches networks based on the presence of a label alone or a label and a value.
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
@ -216,9 +220,14 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
|
||||
// description: Display summary of network configurations
|
||||
// parameters:
|
||||
// - in: query
|
||||
// name: filter
|
||||
// name: filters
|
||||
// type: string
|
||||
// description: Provide filter values (e.g. 'name=podman')
|
||||
// description: |
|
||||
// JSON encoded value of the filters (a map[string][]string) to process on the network list. Available filters:
|
||||
// - name=[name] Matches network name (accepts regex).
|
||||
// - driver=[driver] Only bridge is supported.
|
||||
// - label=[key] or label=[key=value] Matches networks based on the presence of a label alone or a label and a value.
|
||||
// - plugin=[plugin] Matches CNI plugins included in a network (e.g `bridge`,`portmap`,`firewall`,`tuning`,`dnsname`,`macvlan`)
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
|
@ -2,6 +2,7 @@ package network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@ -79,8 +80,12 @@ func List(ctx context.Context, options entities.NetworkListOptions) ([]*entities
|
||||
return nil, err
|
||||
}
|
||||
params := url.Values{}
|
||||
if options.Filter != "" {
|
||||
params.Set("filter", options.Filter)
|
||||
if options.Filters != nil {
|
||||
b, err := json.Marshal(options.Filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.Set("filters", string(b))
|
||||
}
|
||||
response, err := conn.DoRequest(nil, http.MethodGet, "/networks/json", params, nil)
|
||||
if err != nil {
|
||||
|
@ -10,12 +10,13 @@ import (
|
||||
type NetworkListOptions struct {
|
||||
Format string
|
||||
Quiet bool
|
||||
Filter string
|
||||
Filters map[string][]string
|
||||
}
|
||||
|
||||
// NetworkListReport describes the results from listing networks
|
||||
type NetworkListReport struct {
|
||||
*libcni.NetworkConfigList
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// NetworkInspectReport describes the results from inspect networks
|
||||
@ -39,6 +40,7 @@ type NetworkCreateOptions struct {
|
||||
Driver string
|
||||
Gateway net.IP
|
||||
Internal bool
|
||||
Labels map[string]string
|
||||
MacVLAN string
|
||||
Range net.IPNet
|
||||
Subnet net.IPNet
|
||||
|
@ -2,10 +2,7 @@ package abi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containernetworking/cni/libcni"
|
||||
"github.com/containers/podman/v2/libpod/define"
|
||||
"github.com/containers/podman/v2/libpod/network"
|
||||
"github.com/containers/podman/v2/pkg/domain/entities"
|
||||
@ -26,18 +23,16 @@ func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.Net
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var tokens []string
|
||||
// tokenize the networkListOptions.Filter in key=value.
|
||||
if len(options.Filter) > 0 {
|
||||
tokens = strings.Split(options.Filter, "=")
|
||||
if len(tokens) != 2 {
|
||||
return nil, fmt.Errorf("invalid filter syntax : %s", options.Filter)
|
||||
}
|
||||
}
|
||||
|
||||
for _, n := range networks {
|
||||
if ifPassesFilterTest(n, tokens) {
|
||||
reports = append(reports, &entities.NetworkListReport{NetworkConfigList: n})
|
||||
ok, err := network.IfPassesFilter(n, options.Filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ok {
|
||||
reports = append(reports, &entities.NetworkListReport{
|
||||
NetworkConfigList: n,
|
||||
Labels: network.GetNetworkLabels(n),
|
||||
})
|
||||
}
|
||||
}
|
||||
return reports, nil
|
||||
@ -117,28 +112,6 @@ func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, optio
|
||||
return network.Create(name, options, runtimeConfig)
|
||||
}
|
||||
|
||||
func ifPassesFilterTest(netconf *libcni.NetworkConfigList, filter []string) bool {
|
||||
result := false
|
||||
if len(filter) == 0 {
|
||||
// No filter, so pass
|
||||
return true
|
||||
}
|
||||
switch strings.ToLower(filter[0]) {
|
||||
case "name":
|
||||
if filter[1] == netconf.Name {
|
||||
result = true
|
||||
}
|
||||
case "plugin":
|
||||
plugins := network.GetCNIPlugins(netconf)
|
||||
if strings.Contains(plugins, filter[1]) {
|
||||
result = true
|
||||
}
|
||||
default:
|
||||
result = false
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// NetworkDisconnect removes a container from a given network
|
||||
func (ic *ContainerEngine) NetworkDisconnect(ctx context.Context, networkname string, options entities.NetworkDisconnectOptions) error {
|
||||
return ic.Libpod.DisconnectContainerFromNetwork(options.Container, networkname, options.Force)
|
||||
|
@ -9,8 +9,8 @@ t GET networks/non-existing-network 404 \
|
||||
t POST libpod/networks/create?name=network1 '' 200 \
|
||||
.Filename~.*/network1\\.conflist
|
||||
|
||||
# --data '{"Subnet":{"IP":"10.10.254.0","Mask":[255,255,255,0]}}'
|
||||
t POST libpod/networks/create?name=network2 '"Subnet":{"IP":"10.10.254.0","Mask":[255,255,255,0]}' 200 \
|
||||
# --data '{"Subnet":{"IP":"10.10.254.0","Mask":[255,255,255,0]},"Labels":{"abc":"val"}}'
|
||||
t POST libpod/networks/create?name=network2 '"Subnet":{"IP":"10.10.254.0","Mask":[255,255,255,0]},"Labels":{"abc":"val"}' 200 \
|
||||
.Filename~.*/network2\\.conflist
|
||||
|
||||
# test for empty mask
|
||||
@ -22,7 +22,8 @@ t POST libpod/networks/create '"Subnet":{"IP":"10.10.1.0","Mask":[0,255,255,0]}'
|
||||
|
||||
# network list
|
||||
t GET libpod/networks/json 200
|
||||
t GET libpod/networks/json?filter=name=network1 200 \
|
||||
# filters={"name":["network1"]}
|
||||
t GET libpod/networks/json?filters=%7B%22name%22%3A%5B%22network1%22%5D%7D 200 \
|
||||
length=1 \
|
||||
.[0].Name=network1
|
||||
t GET networks 200
|
||||
@ -34,12 +35,12 @@ length=2
|
||||
#filters={"name":["network"]}
|
||||
t GET networks?filters=%7B%22name%22%3A%5B%22network%22%5D%7D 200 \
|
||||
length=2
|
||||
# invalid filter filters={"label":"abc"}
|
||||
t GET networks?filters=%7B%22label%22%3A%5B%22abc%22%5D%7D 500 \
|
||||
.cause="only the name filter for listing networks is implemented"
|
||||
# invalid filter filters={"label":"abc","name":["network"]}
|
||||
t GET networks?filters=%7B%22label%22%3A%22abc%22%2C%22name%22%3A%5B%22network%22%5D%7D 500 \
|
||||
.cause="only the name filter for listing networks is implemented"
|
||||
# filters={"label":["abc"]}
|
||||
t GET networks?filters=%7B%22label%22%3A%5B%22abc%22%5D%7D 200 \
|
||||
length=1
|
||||
# invalid filter filters={"id":["abc"]}
|
||||
t GET networks?filters=%7B%22id%22%3A%5B%22abc%22%5D%7D 500 \
|
||||
.cause='invalid filter "id"'
|
||||
|
||||
# clean the network
|
||||
t DELETE libpod/networks/network1 200 \
|
||||
|
@ -66,6 +66,65 @@ var _ = Describe("Podman network", func() {
|
||||
Expect(session.LineInOutputContains(name)).To(BeTrue())
|
||||
})
|
||||
|
||||
It("podman network list --filter plugin and name", func() {
|
||||
name, path := generateNetworkConfig(podmanTest)
|
||||
defer removeConf(path)
|
||||
|
||||
session := podmanTest.Podman([]string{"network", "ls", "--filter", "plugin=bridge", "--filter", "name=" + name})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(session.OutputToString()).To(ContainSubstring(name))
|
||||
})
|
||||
|
||||
It("podman network list --filter two names", func() {
|
||||
name1, path1 := generateNetworkConfig(podmanTest)
|
||||
defer removeConf(path1)
|
||||
|
||||
name2, path2 := generateNetworkConfig(podmanTest)
|
||||
defer removeConf(path2)
|
||||
|
||||
session := podmanTest.Podman([]string{"network", "ls", "--filter", "name=" + name1, "--filter", "name=" + name2})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(session.OutputToString()).To(ContainSubstring(name1))
|
||||
Expect(session.OutputToString()).To(ContainSubstring(name2))
|
||||
})
|
||||
|
||||
It("podman network list --filter labels", func() {
|
||||
net1 := "labelnet" + stringid.GenerateNonCryptoID()
|
||||
label1 := "testlabel1=abc"
|
||||
label2 := "abcdef"
|
||||
session := podmanTest.Podman([]string{"network", "create", "--label", label1, net1})
|
||||
session.WaitWithDefaultTimeout()
|
||||
defer podmanTest.removeCNINetwork(net1)
|
||||
Expect(session.ExitCode()).To(BeZero())
|
||||
|
||||
net2 := "labelnet" + stringid.GenerateNonCryptoID()
|
||||
session = podmanTest.Podman([]string{"network", "create", "--label", label1, "--label", label2, net2})
|
||||
session.WaitWithDefaultTimeout()
|
||||
defer podmanTest.removeCNINetwork(net2)
|
||||
Expect(session.ExitCode()).To(BeZero())
|
||||
|
||||
session = podmanTest.Podman([]string{"network", "ls", "--filter", "label=" + label1})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(session.OutputToString()).To(ContainSubstring(net1))
|
||||
Expect(session.OutputToString()).To(ContainSubstring(net2))
|
||||
|
||||
session = podmanTest.Podman([]string{"network", "ls", "--filter", "label=" + label1, "--filter", "label=" + label2})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(Equal(0))
|
||||
Expect(session.OutputToString()).ToNot(ContainSubstring(net1))
|
||||
Expect(session.OutputToString()).To(ContainSubstring(net2))
|
||||
})
|
||||
|
||||
It("podman network list --filter invalid value", func() {
|
||||
session := podmanTest.Podman([]string{"network", "ls", "--filter", "namr=ab"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).To(ExitWithError())
|
||||
Expect(session.ErrorToString()).To(ContainSubstring(`invalid filter "namr"`))
|
||||
})
|
||||
|
||||
It("podman network list --filter failure", func() {
|
||||
name, path := generateNetworkConfig(podmanTest)
|
||||
defer removeConf(path)
|
||||
|
Reference in New Issue
Block a user