mirror of
https://github.com/containers/podman.git
synced 2025-06-22 18:08:11 +08:00
Add support for network ids
The network ID is not stored. It is just the sha256 hash from the network name. There is a risk of a potential hash collision. However it's very unlikely and even if we hit this it will complain that more than network with this ID exists. The main benefit is that the compat api can have proper network ID support. Also this adds the support for `podman network ls --format "{{.ID}}"` and `--filter id=<ID>`. It also ensures that we can do network rm <ID> and network inspect <ID>. Since we use a hash this commit is backwards compatible even for already existing networks. Signed-off-by: Paul Holzinger <paul.holzinger@web.de>
This commit is contained in:
@ -37,6 +37,7 @@ var (
|
||||
var (
|
||||
networkListOptions entities.NetworkListOptions
|
||||
filters []string
|
||||
noTrunc bool
|
||||
)
|
||||
|
||||
func networkListFlags(flags *pflag.FlagSet) {
|
||||
@ -45,6 +46,7 @@ func networkListFlags(flags *pflag.FlagSet) {
|
||||
_ = networklistCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat)
|
||||
|
||||
flags.BoolVarP(&networkListOptions.Quiet, "quiet", "q", false, "display only names")
|
||||
flags.BoolVar(&noTrunc, "no-trunc", false, "Do not truncate the network ID")
|
||||
|
||||
filterFlagName := "filter"
|
||||
flags.StringArrayVarP(&filters, filterFlagName, "f", nil, "Provide filter values (e.g. 'name=podman')")
|
||||
@ -96,6 +98,7 @@ func networkList(cmd *cobra.Command, args []string) error {
|
||||
"Version": "version",
|
||||
"Plugins": "plugins",
|
||||
"Labels": "labels",
|
||||
"ID": "network id",
|
||||
})
|
||||
renderHeaders := true
|
||||
row := "{{.Name}}\t{{.Version}}\t{{.Plugins}}\n"
|
||||
@ -155,3 +158,11 @@ func (n ListPrintReports) Labels() string {
|
||||
}
|
||||
return strings.Join(list, ",")
|
||||
}
|
||||
|
||||
func (n ListPrintReports) ID() string {
|
||||
length := 12
|
||||
if noTrunc {
|
||||
length = 64
|
||||
}
|
||||
return network.GetNetworkID(n.Name)[:length]
|
||||
}
|
||||
|
@ -10,14 +10,6 @@ podman\-network\-ls - Display a summary of CNI networks
|
||||
Displays a list of existing podman networks. This command is not available for rootless users.
|
||||
|
||||
## OPTIONS
|
||||
#### **--quiet**, **-q**
|
||||
|
||||
The `quiet` option will restrict the output to only the network names.
|
||||
|
||||
#### **--format**
|
||||
|
||||
Pretty-print networks to JSON or using a Go template.
|
||||
|
||||
#### **--filter**, **-f**
|
||||
|
||||
Filter output based on conditions given.
|
||||
@ -30,10 +22,33 @@ Valid filters are listed below:
|
||||
| **Filter** | **Description** |
|
||||
| ---------- | ------------------------------------------------------------------------------------- |
|
||||
| name | [Name] Network name (accepts regex) |
|
||||
| id | [ID] Full or partial network ID |
|
||||
| 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 |
|
||||
|
||||
#### **--format**
|
||||
|
||||
Change the default output format. This can be of a supported type like 'json'
|
||||
or a Go template.
|
||||
Valid placeholders for the Go template are listed below:
|
||||
|
||||
| **Placeholder** | **Description** |
|
||||
| --------------- | --------------------------------|
|
||||
| .ID | Network ID |
|
||||
| .Name | Network name |
|
||||
| .Plugins | Network Plugins |
|
||||
| .Labels | Network labels |
|
||||
| .Version | CNI Version of the config file |
|
||||
|
||||
#### **--no-trunc**
|
||||
|
||||
Do not truncate the network ID. The network ID is not displayed by default and must be specified with **--format**.
|
||||
|
||||
#### **--quiet**, **-q**
|
||||
|
||||
The `quiet` option will restrict the output to only the network names.
|
||||
|
||||
## EXAMPLE
|
||||
|
||||
Display networks
|
||||
|
@ -50,13 +50,15 @@ func LoadCNIConfsFromDir(dir string) ([]*libcni.NetworkConfigList, error) {
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// GetCNIConfigPathByName finds a CNI network by name and
|
||||
// GetCNIConfigPathByNameOrID finds a CNI network by name and
|
||||
// returns its configuration file path
|
||||
func GetCNIConfigPathByName(config *config.Config, name string) (string, error) {
|
||||
func GetCNIConfigPathByNameOrID(config *config.Config, name string) (string, error) {
|
||||
files, err := libcni.ConfFiles(GetCNIConfDir(config), []string{".conflist"})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
idMatch := 0
|
||||
file := ""
|
||||
for _, confFile := range files {
|
||||
conf, err := libcni.ConfListFromFile(confFile)
|
||||
if err != nil {
|
||||
@ -65,6 +67,16 @@ func GetCNIConfigPathByName(config *config.Config, name string) (string, error)
|
||||
if conf.Name == name {
|
||||
return confFile, nil
|
||||
}
|
||||
if strings.HasPrefix(GetNetworkID(conf.Name), name) {
|
||||
idMatch++
|
||||
file = confFile
|
||||
}
|
||||
}
|
||||
if idMatch == 1 {
|
||||
return file, nil
|
||||
}
|
||||
if idMatch > 1 {
|
||||
return "", errors.Errorf("more than one result for network ID %s", name)
|
||||
}
|
||||
return "", errors.Wrap(define.ErrNoSuchNetwork, fmt.Sprintf("unable to find network configuration for %s", name))
|
||||
}
|
||||
@ -72,7 +84,7 @@ func GetCNIConfigPathByName(config *config.Config, name string) (string, error)
|
||||
// ReadRawCNIConfByName reads the raw CNI configuration for a CNI
|
||||
// network by name
|
||||
func ReadRawCNIConfByName(config *config.Config, name string) ([]byte, error) {
|
||||
confFile, err := GetCNIConfigPathByName(config, name)
|
||||
confFile, err := GetCNIConfigPathByNameOrID(config, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -230,8 +230,16 @@ func IfPassesFilter(netconf *libcni.NetworkConfigList, filters map[string][]stri
|
||||
}
|
||||
}
|
||||
|
||||
case "id":
|
||||
// matches part of one id
|
||||
for _, filterValue := range filterValues {
|
||||
if strings.Contains(GetNetworkID(netconf.Name), filterValue) {
|
||||
result = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add dangling filter
|
||||
// TODO TODO: add id filter if we support ids
|
||||
|
||||
default:
|
||||
return false, errors.Errorf("invalid filter %q", key)
|
||||
|
@ -1,6 +1,8 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"os"
|
||||
@ -175,7 +177,7 @@ func RemoveNetwork(config *config.Config, name string) error {
|
||||
return err
|
||||
}
|
||||
defer l.releaseCNILock()
|
||||
cniPath, err := GetCNIConfigPathByName(config, name)
|
||||
cniPath, err := GetCNIConfigPathByNameOrID(config, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -229,3 +231,10 @@ func Exists(config *config.Config, name string) (bool, error) {
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// GetNetworkID return the network ID for a given name.
|
||||
// It is just the sha256 hash but this should be good enough.
|
||||
func GetNetworkID(name string) string {
|
||||
hash := sha256.Sum256([]byte(name))
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) {
|
||||
utils.NetworkNotFound(w, name, err)
|
||||
return
|
||||
}
|
||||
report, err := getNetworkResourceByName(name, runtime, nil)
|
||||
report, err := getNetworkResourceByNameOrID(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, filters map[string][]string) (*types.NetworkResource, error) {
|
||||
func getNetworkResourceByNameOrID(nameOrID string, runtime *libpod.Runtime, filters map[string][]string) (*types.NetworkResource, error) {
|
||||
var (
|
||||
ipamConfigs []dockerNetwork.IPAMConfig
|
||||
)
|
||||
@ -68,7 +68,7 @@ func getNetworkResourceByName(name string, runtime *libpod.Runtime, filters map[
|
||||
}
|
||||
containerEndpoints := map[string]types.EndpointResource{}
|
||||
// Get the network path so we can get created time
|
||||
networkConfigPath, err := network.GetCNIConfigPathByName(config, name)
|
||||
networkConfigPath, err := network.GetCNIConfigPathByNameOrID(config, nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -116,7 +116,7 @@ func getNetworkResourceByName(name string, runtime *libpod.Runtime, filters map[
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if netData, ok := data.NetworkSettings.Networks[name]; ok {
|
||||
if netData, ok := data.NetworkSettings.Networks[conf.Name]; ok {
|
||||
containerEndpoint := types.EndpointResource{
|
||||
Name: netData.NetworkID,
|
||||
EndpointID: netData.EndpointID,
|
||||
@ -128,8 +128,8 @@ func getNetworkResourceByName(name string, runtime *libpod.Runtime, filters map[
|
||||
}
|
||||
}
|
||||
report := types.NetworkResource{
|
||||
Name: name,
|
||||
ID: name,
|
||||
Name: conf.Name,
|
||||
ID: network.GetNetworkID(conf.Name),
|
||||
Created: time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)), // nolint: unconvert
|
||||
Scope: "",
|
||||
Driver: network.DefaultNetworkDriver,
|
||||
@ -199,7 +199,7 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) {
|
||||
var reports []*types.NetworkResource
|
||||
logrus.Errorf("netNames: %q", strings.Join(netNames, ", "))
|
||||
for _, name := range netNames {
|
||||
report, err := getNetworkResourceByName(name, runtime, query.Filters)
|
||||
report, err := getNetworkResourceByNameOrID(name, runtime, query.Filters)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
|
@ -68,6 +68,7 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
|
||||
// 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).
|
||||
// - id=[id] Matches for full or partial ID.
|
||||
// - 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:
|
||||
@ -225,6 +226,7 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
|
||||
// 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).
|
||||
// - id=[id] Matches for full or partial ID.
|
||||
// - 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`)
|
||||
|
@ -38,9 +38,19 @@ length=2
|
||||
# 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"'
|
||||
# id filter filters={"id":["a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1"]}
|
||||
t GET networks?filters=%7B%22id%22%3A%5B%22a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1%22%5D%7D 200 \
|
||||
length=1 \
|
||||
.[0].Name=network1 \
|
||||
.[0].Id=a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1
|
||||
# invalid filter filters={"dangling":["1"]}
|
||||
t GET networks?filters=%7B%22dangling%22%3A%5B%221%22%5D%7D 500 \
|
||||
.cause='invalid filter "dangling"'
|
||||
|
||||
# network inspect docker
|
||||
t GET networks/a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1 200 \
|
||||
.Name=network1 \
|
||||
.Id=a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1
|
||||
|
||||
# clean the network
|
||||
t DELETE libpod/networks/network1 200 \
|
||||
|
@ -135,6 +135,40 @@ var _ = Describe("Podman network", func() {
|
||||
Expect(session.LineInOutputContains(name)).To(BeFalse())
|
||||
})
|
||||
|
||||
It("podman network ID test", func() {
|
||||
net := "networkIDTest"
|
||||
// the network id should be the sha256 hash of the network name
|
||||
netID := "6073aefe03cdf8f29be5b23ea9795c431868a3a22066a6290b187691614fee84"
|
||||
session := podmanTest.Podman([]string{"network", "create", net})
|
||||
session.WaitWithDefaultTimeout()
|
||||
defer podmanTest.removeCNINetwork(net)
|
||||
Expect(session.ExitCode()).To(BeZero())
|
||||
|
||||
session = podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}} {{.ID}}", "--filter", "id=" + netID})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(BeZero())
|
||||
Expect(session.OutputToString()).To(ContainSubstring(net + " " + netID[:12]))
|
||||
|
||||
session = podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}} {{.ID}}", "--filter", "id=" + netID[10:50], "--no-trunc"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(BeZero())
|
||||
Expect(session.OutputToString()).To(ContainSubstring(net + " " + netID))
|
||||
|
||||
session = podmanTest.Podman([]string{"network", "inspect", netID[:40]})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(BeZero())
|
||||
Expect(session.OutputToString()).To(ContainSubstring(net))
|
||||
|
||||
session = podmanTest.Podman([]string{"network", "inspect", netID[1:]})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).ToNot(BeZero())
|
||||
Expect(session.ErrorToString()).To(ContainSubstring("no such network"))
|
||||
|
||||
session = podmanTest.Podman([]string{"network", "rm", netID})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session.ExitCode()).To(BeZero())
|
||||
})
|
||||
|
||||
rm_func := func(rm string) {
|
||||
It(fmt.Sprintf("podman network %s no args", rm), func() {
|
||||
session := podmanTest.Podman([]string{"network", rm})
|
||||
|
Reference in New Issue
Block a user