mirror of
https://github.com/containers/podman.git
synced 2025-06-20 17:13: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/common/pkg/completion"
|
||||||
"github.com/containers/podman/v2/cmd/podman/common"
|
"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/cmd/podman/registry"
|
||||||
"github.com/containers/podman/v2/libpod/define"
|
"github.com/containers/podman/v2/libpod/define"
|
||||||
"github.com/containers/podman/v2/pkg/domain/entities"
|
"github.com/containers/podman/v2/pkg/domain/entities"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,6 +29,7 @@ var (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
networkCreateOptions entities.NetworkCreateOptions
|
networkCreateOptions entities.NetworkCreateOptions
|
||||||
|
labels []string
|
||||||
)
|
)
|
||||||
|
|
||||||
func networkCreateFlags(cmd *cobra.Command) {
|
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")
|
flags.StringVar(&networkCreateOptions.MacVLAN, macvlanFlagName, "", "create a Macvlan connection based on this device")
|
||||||
_ = cmd.RegisterFlagCompletionFunc(macvlanFlagName, completion.AutocompleteNone)
|
_ = 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
|
// TODO not supported yet
|
||||||
// flags.StringVar(&networkCreateOptions.IPamDriver, "ipam-driver", "", "IP Address Management Driver")
|
// 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]
|
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)
|
response, err := registry.ContainerEngine().NetworkCreate(registry.Context(), name, networkCreateOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/containers/podman/v2/cmd/podman/validate"
|
"github.com/containers/podman/v2/cmd/podman/validate"
|
||||||
"github.com/containers/podman/v2/libpod/network"
|
"github.com/containers/podman/v2/libpod/network"
|
||||||
"github.com/containers/podman/v2/pkg/domain/entities"
|
"github.com/containers/podman/v2/pkg/domain/entities"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
@ -35,17 +36,18 @@ var (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
networkListOptions entities.NetworkListOptions
|
networkListOptions entities.NetworkListOptions
|
||||||
|
filters []string
|
||||||
)
|
)
|
||||||
|
|
||||||
func networkListFlags(flags *pflag.FlagSet) {
|
func networkListFlags(flags *pflag.FlagSet) {
|
||||||
formatFlagName := "format"
|
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)
|
_ = networklistCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat)
|
||||||
|
|
||||||
flags.BoolVarP(&networkListOptions.Quiet, "quiet", "q", false, "display only names")
|
flags.BoolVarP(&networkListOptions.Quiet, "quiet", "q", false, "display only names")
|
||||||
|
|
||||||
filterFlagName := "filter"
|
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)
|
_ = networklistCommand.RegisterFlagCompletionFunc(filterFlagName, common.AutocompleteNetworkFilters)
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -61,14 +63,14 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func networkList(cmd *cobra.Command, args []string) error {
|
func networkList(cmd *cobra.Command, args []string) error {
|
||||||
// validate the filter pattern.
|
networkListOptions.Filters = make(map[string][]string)
|
||||||
if len(networkListOptions.Filter) > 0 {
|
for _, f := range filters {
|
||||||
tokens := strings.Split(networkListOptions.Filter, "=")
|
split := strings.SplitN(f, "=", 2)
|
||||||
if len(tokens) != 2 {
|
if len(split) == 1 {
|
||||||
return fmt.Errorf("invalid filter syntax : %s", networkListOptions.Filter)
|
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)
|
responses, err := registry.ContainerEngine().NetworkList(registry.Context(), networkListOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -93,6 +95,7 @@ func networkList(cmd *cobra.Command, args []string) error {
|
|||||||
"CNIVersion": "version",
|
"CNIVersion": "version",
|
||||||
"Version": "version",
|
"Version": "version",
|
||||||
"Plugins": "plugins",
|
"Plugins": "plugins",
|
||||||
|
"Labels": "labels",
|
||||||
})
|
})
|
||||||
renderHeaders := true
|
renderHeaders := true
|
||||||
row := "{{.Name}}\t{{.Version}}\t{{.Plugins}}\n"
|
row := "{{.Name}}\t{{.Version}}\t{{.Plugins}}\n"
|
||||||
@ -144,3 +147,11 @@ func (n ListPrintReports) Version() string {
|
|||||||
func (n ListPrintReports) Plugins() string {
|
func (n ListPrintReports) Plugins() string {
|
||||||
return network.GetCNIPlugins(n.NetworkConfigList)
|
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
|
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.
|
must be used with a *subnet* option.
|
||||||
|
|
||||||
|
#### **--label**
|
||||||
|
|
||||||
|
Set metadata for a network (e.g., --label mykey=value).
|
||||||
|
|
||||||
#### **--macvlan**
|
#### **--macvlan**
|
||||||
|
|
||||||
Create a *Macvlan* based connection rather than a classic bridge. You must pass an interface name from the host for the
|
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.
|
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.
|
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
|
## EXAMPLE
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ func createBridge(name string, options entities.NetworkCreateOptions, runtimeCon
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create CNI plugin configuration
|
// create CNI plugin configuration
|
||||||
ncList := NewNcList(name, version.Current())
|
ncList := NewNcList(name, version.Current(), options.Labels)
|
||||||
var plugins []CNIPlugins
|
var plugins []CNIPlugins
|
||||||
// TODO need to iron out the role of isDefaultGW and IPMasq
|
// TODO need to iron out the role of isDefaultGW and IPMasq
|
||||||
bridge := NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, ipamConfig)
|
bridge := NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, ipamConfig)
|
||||||
@ -223,7 +223,7 @@ func createMacVLAN(name string, options entities.NetworkCreateOptions, runtimeCo
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ncList := NewNcList(name, version.Current())
|
ncList := NewNcList(name, version.Current(), options.Labels)
|
||||||
macvlan := NewMacVLANPlugin(options.MacVLAN)
|
macvlan := NewMacVLANPlugin(options.MacVLAN)
|
||||||
plugins = append(plugins, macvlan)
|
plugins = append(plugins, macvlan)
|
||||||
ncList["plugins"] = plugins
|
ncList["plugins"] = plugins
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/containers/common/pkg/config"
|
"github.com/containers/common/pkg/config"
|
||||||
"github.com/containers/podman/v2/libpod/define"
|
"github.com/containers/podman/v2/libpod/define"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetCNIConfDir get CNI configuration directory
|
// GetCNIConfDir get CNI configuration directory
|
||||||
@ -86,6 +87,35 @@ func GetCNIPlugins(list *libcni.NetworkConfigList) string {
|
|||||||
return strings.Join(plugins, ",")
|
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
|
// GetNetworksFromFilesystem gets all the networks from the cni configuration
|
||||||
// files
|
// files
|
||||||
func GetNetworksFromFilesystem(config *config.Config) ([]*allocator.Net, error) {
|
func GetNetworksFromFilesystem(config *config.Config) ([]*allocator.Net, error) {
|
||||||
|
@ -4,6 +4,11 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containernetworking/cni/libcni"
|
||||||
|
"github.com/containers/podman/v2/pkg/util"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -14,12 +19,24 @@ const (
|
|||||||
// NcList describes a generic map
|
// NcList describes a generic map
|
||||||
type NcList map[string]interface{}
|
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
|
// NewNcList creates a generic map of values with string
|
||||||
// keys and adds in version and network name
|
// 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 := NcList{}
|
||||||
n["cniVersion"] = version
|
n["cniVersion"] = version
|
||||||
n["name"] = name
|
n["name"] = name
|
||||||
|
if len(labels) > 0 {
|
||||||
|
n["args"] = NcArgs{PodmanLabelKey: labels}
|
||||||
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,3 +176,64 @@ func NewMacVLANPlugin(device string) MacVLANConfig {
|
|||||||
}
|
}
|
||||||
return m
|
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)
|
utils.NetworkNotFound(w, name, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
report, err := getNetworkResourceByName(name, runtime)
|
report, err := getNetworkResourceByName(name, runtime, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.InternalServerError(w, err)
|
utils.InternalServerError(w, err)
|
||||||
return
|
return
|
||||||
@ -58,7 +58,7 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) {
|
|||||||
utils.WriteResponse(w, http.StatusOK, report)
|
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 (
|
var (
|
||||||
ipamConfigs []dockerNetwork.IPAMConfig
|
ipamConfigs []dockerNetwork.IPAMConfig
|
||||||
)
|
)
|
||||||
@ -85,6 +85,16 @@ func getNetworkResourceByName(name string, runtime *libpod.Runtime) (*types.Netw
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// No Bridge plugin means we bail
|
||||||
bridge, err := genericPluginsToBridge(conf.Plugins, network.DefaultNetworkDriver)
|
bridge, err := genericPluginsToBridge(conf.Plugins, network.DefaultNetworkDriver)
|
||||||
@ -129,14 +139,14 @@ func getNetworkResourceByName(name string, runtime *libpod.Runtime) (*types.Netw
|
|||||||
Options: nil,
|
Options: nil,
|
||||||
Config: ipamConfigs,
|
Config: ipamConfigs,
|
||||||
},
|
},
|
||||||
Internal: false,
|
Internal: !bridge.IsGW,
|
||||||
Attachable: false,
|
Attachable: false,
|
||||||
Ingress: false,
|
Ingress: false,
|
||||||
ConfigFrom: dockerNetwork.ConfigReference{},
|
ConfigFrom: dockerNetwork.ConfigReference{},
|
||||||
ConfigOnly: false,
|
ConfigOnly: false,
|
||||||
Containers: containerEndpoints,
|
Containers: containerEndpoints,
|
||||||
Options: nil,
|
Options: nil,
|
||||||
Labels: nil,
|
Labels: network.GetNetworkLabels(conf),
|
||||||
Peers: nil,
|
Peers: nil,
|
||||||
Services: nil,
|
Services: nil,
|
||||||
}
|
}
|
||||||
@ -180,41 +190,23 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
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)
|
netNames, err := network.GetNetworkNamesFromFileSystem(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.InternalServerError(w, err)
|
utils.InternalServerError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter by name
|
var reports []*types.NetworkResource
|
||||||
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))
|
|
||||||
logrus.Errorf("netNames: %q", strings.Join(netNames, ", "))
|
logrus.Errorf("netNames: %q", strings.Join(netNames, ", "))
|
||||||
for _, name := range netNames {
|
for _, name := range netNames {
|
||||||
report, err := getNetworkResourceByName(name, runtime)
|
report, err := getNetworkResourceByName(name, runtime, query.Filters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.InternalServerError(w, err)
|
utils.InternalServerError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
reports = append(reports, report)
|
if report != nil {
|
||||||
|
reports = append(reports, report)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
utils.WriteResponse(w, http.StatusOK, reports)
|
utils.WriteResponse(w, http.StatusOK, reports)
|
||||||
}
|
}
|
||||||
@ -245,6 +237,7 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) {
|
|||||||
ncOptions := entities.NetworkCreateOptions{
|
ncOptions := entities.NetworkCreateOptions{
|
||||||
Driver: network.DefaultNetworkDriver,
|
Driver: network.DefaultNetworkDriver,
|
||||||
Internal: networkCreate.Internal,
|
Internal: networkCreate.Internal,
|
||||||
|
Labels: networkCreate.Labels,
|
||||||
}
|
}
|
||||||
if networkCreate.IPAM != nil && networkCreate.IPAM.Config != nil {
|
if networkCreate.IPAM != nil && networkCreate.IPAM.Config != nil {
|
||||||
if len(networkCreate.IPAM.Config) > 1 {
|
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)
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||||
query := struct {
|
query := struct {
|
||||||
Filter string `schema:"filter"`
|
Filters map[string][]string `schema:"filters"`
|
||||||
}{
|
}{
|
||||||
// override any golang type defaults
|
// override any golang type defaults
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
options := entities.NetworkListOptions{
|
options := entities.NetworkListOptions{
|
||||||
Filter: query.Filter,
|
Filters: query.Filters,
|
||||||
}
|
}
|
||||||
ic := abi.ContainerEngine{Libpod: runtime}
|
ic := abi.ContainerEngine{Libpod: runtime}
|
||||||
reports, err := ic.NetworkList(r.Context(), options)
|
reports, err := ic.NetworkList(r.Context(), options)
|
||||||
|
@ -65,7 +65,11 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
|
|||||||
// - in: query
|
// - in: query
|
||||||
// name: filters
|
// name: filters
|
||||||
// type: string
|
// 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:
|
// produces:
|
||||||
// - application/json
|
// - application/json
|
||||||
// responses:
|
// responses:
|
||||||
@ -216,9 +220,14 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
|
|||||||
// description: Display summary of network configurations
|
// description: Display summary of network configurations
|
||||||
// parameters:
|
// parameters:
|
||||||
// - in: query
|
// - in: query
|
||||||
// name: filter
|
// name: filters
|
||||||
// type: string
|
// 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:
|
// produces:
|
||||||
// - application/json
|
// - application/json
|
||||||
// responses:
|
// responses:
|
||||||
|
@ -2,6 +2,7 @@ package network
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -79,8 +80,12 @@ func List(ctx context.Context, options entities.NetworkListOptions) ([]*entities
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
if options.Filter != "" {
|
if options.Filters != nil {
|
||||||
params.Set("filter", options.Filter)
|
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)
|
response, err := conn.DoRequest(nil, http.MethodGet, "/networks/json", params, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -8,14 +8,15 @@ import (
|
|||||||
|
|
||||||
// NetworkListOptions describes options for listing networks in cli
|
// NetworkListOptions describes options for listing networks in cli
|
||||||
type NetworkListOptions struct {
|
type NetworkListOptions struct {
|
||||||
Format string
|
Format string
|
||||||
Quiet bool
|
Quiet bool
|
||||||
Filter string
|
Filters map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkListReport describes the results from listing networks
|
// NetworkListReport describes the results from listing networks
|
||||||
type NetworkListReport struct {
|
type NetworkListReport struct {
|
||||||
*libcni.NetworkConfigList
|
*libcni.NetworkConfigList
|
||||||
|
Labels map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkInspectReport describes the results from inspect networks
|
// NetworkInspectReport describes the results from inspect networks
|
||||||
@ -39,6 +40,7 @@ type NetworkCreateOptions struct {
|
|||||||
Driver string
|
Driver string
|
||||||
Gateway net.IP
|
Gateway net.IP
|
||||||
Internal bool
|
Internal bool
|
||||||
|
Labels map[string]string
|
||||||
MacVLAN string
|
MacVLAN string
|
||||||
Range net.IPNet
|
Range net.IPNet
|
||||||
Subnet net.IPNet
|
Subnet net.IPNet
|
||||||
|
@ -2,10 +2,7 @@ package abi
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/containernetworking/cni/libcni"
|
|
||||||
"github.com/containers/podman/v2/libpod/define"
|
"github.com/containers/podman/v2/libpod/define"
|
||||||
"github.com/containers/podman/v2/libpod/network"
|
"github.com/containers/podman/v2/libpod/network"
|
||||||
"github.com/containers/podman/v2/pkg/domain/entities"
|
"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
|
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 {
|
for _, n := range networks {
|
||||||
if ifPassesFilterTest(n, tokens) {
|
ok, err := network.IfPassesFilter(n, options.Filters)
|
||||||
reports = append(reports, &entities.NetworkListReport{NetworkConfigList: n})
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
reports = append(reports, &entities.NetworkListReport{
|
||||||
|
NetworkConfigList: n,
|
||||||
|
Labels: network.GetNetworkLabels(n),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return reports, nil
|
return reports, nil
|
||||||
@ -117,28 +112,6 @@ func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, optio
|
|||||||
return network.Create(name, options, runtimeConfig)
|
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
|
// NetworkDisconnect removes a container from a given network
|
||||||
func (ic *ContainerEngine) NetworkDisconnect(ctx context.Context, networkname string, options entities.NetworkDisconnectOptions) error {
|
func (ic *ContainerEngine) NetworkDisconnect(ctx context.Context, networkname string, options entities.NetworkDisconnectOptions) error {
|
||||||
return ic.Libpod.DisconnectContainerFromNetwork(options.Container, networkname, options.Force)
|
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 \
|
t POST libpod/networks/create?name=network1 '' 200 \
|
||||||
.Filename~.*/network1\\.conflist
|
.Filename~.*/network1\\.conflist
|
||||||
|
|
||||||
# --data '{"Subnet":{"IP":"10.10.254.0","Mask":[255,255,255,0]}}'
|
# --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]}' 200 \
|
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
|
.Filename~.*/network2\\.conflist
|
||||||
|
|
||||||
# test for empty mask
|
# 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
|
# network list
|
||||||
t GET libpod/networks/json 200
|
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 \
|
length=1 \
|
||||||
.[0].Name=network1
|
.[0].Name=network1
|
||||||
t GET networks 200
|
t GET networks 200
|
||||||
@ -34,12 +35,12 @@ length=2
|
|||||||
#filters={"name":["network"]}
|
#filters={"name":["network"]}
|
||||||
t GET networks?filters=%7B%22name%22%3A%5B%22network%22%5D%7D 200 \
|
t GET networks?filters=%7B%22name%22%3A%5B%22network%22%5D%7D 200 \
|
||||||
length=2
|
length=2
|
||||||
# invalid filter filters={"label":"abc"}
|
# filters={"label":["abc"]}
|
||||||
t GET networks?filters=%7B%22label%22%3A%5B%22abc%22%5D%7D 500 \
|
t GET networks?filters=%7B%22label%22%3A%5B%22abc%22%5D%7D 200 \
|
||||||
.cause="only the name filter for listing networks is implemented"
|
length=1
|
||||||
# invalid filter filters={"label":"abc","name":["network"]}
|
# invalid filter filters={"id":["abc"]}
|
||||||
t GET networks?filters=%7B%22label%22%3A%22abc%22%2C%22name%22%3A%5B%22network%22%5D%7D 500 \
|
t GET networks?filters=%7B%22id%22%3A%5B%22abc%22%5D%7D 500 \
|
||||||
.cause="only the name filter for listing networks is implemented"
|
.cause='invalid filter "id"'
|
||||||
|
|
||||||
# clean the network
|
# clean the network
|
||||||
t DELETE libpod/networks/network1 200 \
|
t DELETE libpod/networks/network1 200 \
|
||||||
|
@ -66,6 +66,65 @@ var _ = Describe("Podman network", func() {
|
|||||||
Expect(session.LineInOutputContains(name)).To(BeTrue())
|
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() {
|
It("podman network list --filter failure", func() {
|
||||||
name, path := generateNetworkConfig(podmanTest)
|
name, path := generateNetworkConfig(podmanTest)
|
||||||
defer removeConf(path)
|
defer removeConf(path)
|
||||||
|
Reference in New Issue
Block a user