Skip to content

Commit

Permalink
podman network label support
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
Paul Holzinger committed Nov 28, 2020
1 parent ad24392 commit 8494bcb
Show file tree
Hide file tree
Showing 15 changed files with 285 additions and 96 deletions.
12 changes: 12 additions & 0 deletions cmd/podman/networks/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -27,6 +29,7 @@ var (

var (
networkCreateOptions entities.NetworkCreateOptions
labels []string
)

func networkCreateFlags(cmd *cobra.Command) {
Expand All @@ -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")

Expand Down Expand Up @@ -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
Expand Down
27 changes: 19 additions & 8 deletions cmd/podman/networks/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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)

}
Expand All @@ -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
Expand All @@ -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"
Expand Down Expand Up @@ -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, ",")
}
4 changes: 4 additions & 0 deletions docs/source/markdown/podman-network-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 15 additions & 3 deletions docs/source/markdown/podman-network-ls.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions libpod/network/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
30 changes: 30 additions & 0 deletions libpod/network/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
80 changes: 79 additions & 1 deletion libpod/network/netconflist.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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
}

Expand Down Expand Up @@ -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
}
Loading

0 comments on commit 8494bcb

Please sign in to comment.