diff --git a/libnetwork/cni/network.go b/libnetwork/cni/network.go index e9121252a..83d6cbbac 100644 --- a/libnetwork/cni/network.go +++ b/libnetwork/cni/network.go @@ -295,6 +295,12 @@ func (n *cniNetwork) DefaultInterfaceName() string { return cniDeviceName } +// NetworkInfo return the network information about binary path, +// package version and program version. +func (n *cniNetwork) NetworkInfo() (map[string]interface{}, error) { + return nil, nil +} + func (n *cniNetwork) Network(nameOrID string) (*types.Network, error) { network, err := n.getNetwork(nameOrID) if err != nil { diff --git a/libnetwork/netavark/network.go b/libnetwork/netavark/network.go index 77dda2483..f94929eb8 100644 --- a/libnetwork/netavark/network.go +++ b/libnetwork/netavark/network.go @@ -15,6 +15,7 @@ import ( "github.com/containers/common/libnetwork/internal/util" "github.com/containers/common/libnetwork/types" "github.com/containers/common/pkg/config" + cutil "github.com/containers/common/pkg/util" "github.com/containers/storage/pkg/lockfile" "github.com/containers/storage/pkg/unshare" "github.com/sirupsen/logrus" @@ -336,6 +337,30 @@ func (n *netavarkNetwork) DefaultInterfaceName() string { return defaultBridgeName } +// NetworkInfo return the network information about binary path, +// package version and program version. +func (n *netavarkNetwork) NetworkInfo() (map[string]interface{}, error) { + info := make(map[string]interface{}) + + netavarkPath := n.netavarkBinary + netavarkPackageVersion := cutil.PackageVersion(netavarkPath) + netavarkProgramVersion, err := cutil.ProgramVersion(netavarkPath) + + info["netavark-path"] = netavarkPath + info["netavark-package"] = netavarkPackageVersion + info["netavark-version"] = netavarkProgramVersion + + aardvarkPath := n.aardvarkBinary + aardvarkPackageVersion := cutil.PackageVersion(aardvarkPath) + aardvarkProgramVersion, err := cutil.ProgramVersion(aardvarkPath) + + info["aardvark-path"] = aardvarkPath + info["aardvark-package"] = aardvarkPackageVersion + info["aardvark-version"] = aardvarkProgramVersion + + return info, err +} + func (n *netavarkNetwork) Network(nameOrID string) (*types.Network, error) { network, err := n.getNetwork(nameOrID) if err != nil { diff --git a/libnetwork/types/network.go b/libnetwork/types/network.go index b8804bf6b..3258e4741 100644 --- a/libnetwork/types/network.go +++ b/libnetwork/types/network.go @@ -34,6 +34,10 @@ type ContainerNetwork interface { // DefaultNetworkName will return the default network name // for this interface. DefaultNetworkName() string + + // NetworkInfo return the network information about binary path, + // package version and program version. + NetworkInfo() (map[string]interface{}, error) } // Network describes the Network attributes. diff --git a/pkg/util/util.go b/pkg/util/util.go index 98890a686..677439136 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -1,6 +1,74 @@ package util -import "regexp" +import ( + "bytes" + "fmt" + "os/exec" + "regexp" + "strings" +) + +const ( + unknownPackage = "Unknown" +) + +func queryPackageVersion(cmdArg ...string) string { + output := unknownPackage + if 1 < len(cmdArg) { + cmd := exec.Command(cmdArg[0], cmdArg[1:]...) + if outp, err := cmd.Output(); err == nil { + output = string(outp) + if cmdArg[0] == "/usr/bin/dpkg" { + r := strings.Split(output, ": ") + queryFormat := `${Package}_${Version}_${Architecture}` + cmd = exec.Command("/usr/bin/dpkg-query", "-f", queryFormat, "-W", r[0]) + if outp, err := cmd.Output(); err == nil { + output = string(outp) + } + } + } + if cmdArg[0] == "/sbin/apk" { + prefix := cmdArg[len(cmdArg)-1] + " is owned by " + output = strings.Replace(output, prefix, "", 1) + } + } + return strings.Trim(output, "\n") +} + +func PackageVersion(program string) string { // program is full path + packagers := [][]string{ + {"/usr/bin/rpm", "-q", "-f"}, + {"/usr/bin/dpkg", "-S"}, // Debian, Ubuntu + {"/usr/bin/pacman", "-Qo"}, // Arch + {"/usr/bin/qfile", "-qv"}, // Gentoo (quick) + {"/usr/bin/equery", "b"}, // Gentoo (slow) + {"/sbin/apk", "info", "-W"}, // Alpine + {"/usr/local/sbin/pkg", "which", "-q"}, // FreeBSD + } + + for _, cmd := range packagers { + cmd = append(cmd, program) + if out := queryPackageVersion(cmd...); out != unknownPackage { + return out + } + } + return unknownPackage +} + +func ProgramVersion(mountProgram string) (string, error) { + cmd := exec.Command(mountProgram, "--version") + var stdout bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err := cmd.Run() + if err != nil { + return "", fmt.Errorf("`%v --version` failed: %v %v (%v)", mountProgram, stderr.String(), stdout.String(), err) + } + + return strings.TrimSuffix(stdout.String(), "\n"), nil +} // StringInSlice determines if a string is in a string slice, returns bool func StringInSlice(s string, sl []string) bool {