mirror of
https://github.com/containers/podman.git
synced 2025-06-23 10:38:20 +08:00
Merge pull request #8505 from Luap99/network-labels
podman network label support
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrNoSuchNetworkInterface indicates that no network interface exists
|
// ErrNoSuchNetworkInterface indicates that no network interface exists
|
||||||
@ -89,6 +90,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,42 +190,24 @@ 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
|
||||||
}
|
}
|
||||||
|
if report != nil {
|
||||||
reports = append(reports, report)
|
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 {
|
||||||
|
@ -10,12 +10,13 @@ import (
|
|||||||
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