mirror of
https://github.com/containers/podman.git
synced 2025-09-25 07:44:24 +08:00

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>
386 lines
11 KiB
Go
386 lines
11 KiB
Go
package compat
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/containernetworking/cni/libcni"
|
|
"github.com/containers/podman/v2/libpod"
|
|
"github.com/containers/podman/v2/libpod/define"
|
|
"github.com/containers/podman/v2/libpod/network"
|
|
"github.com/containers/podman/v2/pkg/api/handlers/utils"
|
|
"github.com/containers/podman/v2/pkg/domain/entities"
|
|
"github.com/containers/podman/v2/pkg/domain/infra/abi"
|
|
"github.com/docker/docker/api/types"
|
|
dockerNetwork "github.com/docker/docker/api/types/network"
|
|
"github.com/gorilla/schema"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
func InspectNetwork(w http.ResponseWriter, r *http.Request) {
|
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
|
|
|
// FYI scope and version are currently unused but are described by the API
|
|
// Leaving this for if/when we have to enable these
|
|
// query := struct {
|
|
// scope string
|
|
// verbose bool
|
|
// }{
|
|
// // override any golang type defaults
|
|
// }
|
|
// decoder := r.Context().Value("decoder").(*schema.Decoder)
|
|
// if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
|
// utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
|
|
// return
|
|
// }
|
|
config, err := runtime.GetConfig()
|
|
if err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
name := utils.GetName(r)
|
|
_, err = network.InspectNetwork(config, name)
|
|
if err != nil {
|
|
utils.NetworkNotFound(w, name, err)
|
|
return
|
|
}
|
|
report, err := getNetworkResourceByNameOrID(name, runtime, nil)
|
|
if err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
utils.WriteResponse(w, http.StatusOK, report)
|
|
}
|
|
|
|
func getNetworkResourceByNameOrID(nameOrID string, runtime *libpod.Runtime, filters map[string][]string) (*types.NetworkResource, error) {
|
|
var (
|
|
ipamConfigs []dockerNetwork.IPAMConfig
|
|
)
|
|
config, err := runtime.GetConfig()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
containerEndpoints := map[string]types.EndpointResource{}
|
|
// Get the network path so we can get created time
|
|
networkConfigPath, err := network.GetCNIConfigPathByNameOrID(config, nameOrID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
f, err := os.Stat(networkConfigPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
stat := f.Sys().(*syscall.Stat_t)
|
|
cons, err := runtime.GetAllContainers()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conf, err := libcni.ConfListFromFile(networkConfigPath)
|
|
if err != nil {
|
|
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
|
|
bridge, err := genericPluginsToBridge(conf.Plugins, network.DefaultNetworkDriver)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, outer := range bridge.IPAM.Ranges {
|
|
for _, n := range outer {
|
|
ipamConfig := dockerNetwork.IPAMConfig{
|
|
Subnet: n.Subnet,
|
|
Gateway: n.Gateway,
|
|
}
|
|
ipamConfigs = append(ipamConfigs, ipamConfig)
|
|
}
|
|
}
|
|
|
|
for _, con := range cons {
|
|
data, err := con.Inspect(false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if netData, ok := data.NetworkSettings.Networks[conf.Name]; ok {
|
|
containerEndpoint := types.EndpointResource{
|
|
Name: netData.NetworkID,
|
|
EndpointID: netData.EndpointID,
|
|
MacAddress: netData.MacAddress,
|
|
IPv4Address: netData.IPAddress,
|
|
IPv6Address: netData.GlobalIPv6Address,
|
|
}
|
|
containerEndpoints[con.ID()] = containerEndpoint
|
|
}
|
|
}
|
|
report := types.NetworkResource{
|
|
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,
|
|
EnableIPv6: false,
|
|
IPAM: dockerNetwork.IPAM{
|
|
Driver: "default",
|
|
Options: nil,
|
|
Config: ipamConfigs,
|
|
},
|
|
Internal: !bridge.IsGW,
|
|
Attachable: false,
|
|
Ingress: false,
|
|
ConfigFrom: dockerNetwork.ConfigReference{},
|
|
ConfigOnly: false,
|
|
Containers: containerEndpoints,
|
|
Options: nil,
|
|
Labels: network.GetNetworkLabels(conf),
|
|
Peers: nil,
|
|
Services: nil,
|
|
}
|
|
return &report, nil
|
|
}
|
|
|
|
func genericPluginsToBridge(plugins []*libcni.NetworkConfig, pluginType string) (network.HostLocalBridge, error) {
|
|
var bridge network.HostLocalBridge
|
|
generic, err := findPluginByName(plugins, pluginType)
|
|
if err != nil {
|
|
return bridge, err
|
|
}
|
|
err = json.Unmarshal(generic, &bridge)
|
|
return bridge, err
|
|
}
|
|
|
|
func findPluginByName(plugins []*libcni.NetworkConfig, pluginType string) ([]byte, error) {
|
|
for _, p := range plugins {
|
|
if pluginType == p.Network.Type {
|
|
return p.Bytes, nil
|
|
}
|
|
}
|
|
return nil, errors.New("unable to find bridge plugin")
|
|
}
|
|
|
|
func ListNetworks(w http.ResponseWriter, r *http.Request) {
|
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
|
query := struct {
|
|
Filters map[string][]string `schema:"filters"`
|
|
}{
|
|
// override any golang type defaults
|
|
}
|
|
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
|
utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
|
|
return
|
|
}
|
|
config, err := runtime.GetConfig()
|
|
if err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
|
|
netNames, err := network.GetNetworkNamesFromFileSystem(config)
|
|
if err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
|
|
var reports []*types.NetworkResource
|
|
logrus.Errorf("netNames: %q", strings.Join(netNames, ", "))
|
|
for _, name := range netNames {
|
|
report, err := getNetworkResourceByNameOrID(name, runtime, query.Filters)
|
|
if err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
if report != nil {
|
|
reports = append(reports, report)
|
|
}
|
|
}
|
|
utils.WriteResponse(w, http.StatusOK, reports)
|
|
}
|
|
|
|
func CreateNetwork(w http.ResponseWriter, r *http.Request) {
|
|
var (
|
|
name string
|
|
networkCreate types.NetworkCreateRequest
|
|
)
|
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
|
if err := json.NewDecoder(r.Body).Decode(&networkCreate); err != nil {
|
|
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
|
|
return
|
|
}
|
|
|
|
if len(networkCreate.Name) > 0 {
|
|
name = networkCreate.Name
|
|
}
|
|
if len(networkCreate.Driver) < 1 {
|
|
networkCreate.Driver = network.DefaultNetworkDriver
|
|
}
|
|
// At present I think we should just support the bridge driver
|
|
// and allow demand to make us consider more
|
|
if networkCreate.Driver != network.DefaultNetworkDriver {
|
|
utils.InternalServerError(w, errors.New("network create only supports the bridge driver"))
|
|
return
|
|
}
|
|
ncOptions := entities.NetworkCreateOptions{
|
|
Driver: network.DefaultNetworkDriver,
|
|
Internal: networkCreate.Internal,
|
|
Labels: networkCreate.Labels,
|
|
}
|
|
if networkCreate.IPAM != nil && networkCreate.IPAM.Config != nil {
|
|
if len(networkCreate.IPAM.Config) > 1 {
|
|
utils.InternalServerError(w, errors.New("compat network create can only support one IPAM config"))
|
|
return
|
|
}
|
|
|
|
if len(networkCreate.IPAM.Config[0].Subnet) > 0 {
|
|
_, subnet, err := net.ParseCIDR(networkCreate.IPAM.Config[0].Subnet)
|
|
if err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
ncOptions.Subnet = *subnet
|
|
}
|
|
if len(networkCreate.IPAM.Config[0].Gateway) > 0 {
|
|
ncOptions.Gateway = net.ParseIP(networkCreate.IPAM.Config[0].Gateway)
|
|
}
|
|
if len(networkCreate.IPAM.Config[0].IPRange) > 0 {
|
|
_, IPRange, err := net.ParseCIDR(networkCreate.IPAM.Config[0].IPRange)
|
|
if err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
ncOptions.Range = *IPRange
|
|
}
|
|
}
|
|
ce := abi.ContainerEngine{Libpod: runtime}
|
|
if _, err := ce.NetworkCreate(r.Context(), name, ncOptions); err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
|
|
body := struct {
|
|
Id string
|
|
Warning []string
|
|
}{
|
|
Id: name,
|
|
}
|
|
utils.WriteResponse(w, http.StatusCreated, body)
|
|
}
|
|
|
|
func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
|
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
|
ic := abi.ContainerEngine{Libpod: runtime}
|
|
|
|
query := struct {
|
|
Force bool `schema:"force"`
|
|
}{
|
|
// This is where you can override the golang default value for one of fields
|
|
}
|
|
|
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
|
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
|
utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
|
|
return
|
|
}
|
|
|
|
options := entities.NetworkRmOptions{
|
|
Force: query.Force,
|
|
}
|
|
|
|
name := utils.GetName(r)
|
|
reports, err := ic.NetworkRm(r.Context(), []string{name}, options)
|
|
if err != nil {
|
|
utils.Error(w, "remove Network failed", http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
if len(reports) == 0 {
|
|
utils.Error(w, "remove Network failed", http.StatusInternalServerError, errors.Errorf("internal error"))
|
|
return
|
|
}
|
|
report := reports[0]
|
|
if report.Err != nil {
|
|
if errors.Cause(report.Err) == define.ErrNoSuchNetwork {
|
|
utils.Error(w, "network not found", http.StatusNotFound, define.ErrNoSuchNetwork)
|
|
return
|
|
}
|
|
utils.InternalServerError(w, report.Err)
|
|
return
|
|
}
|
|
|
|
utils.WriteResponse(w, http.StatusNoContent, "")
|
|
}
|
|
|
|
// Connect adds a container to a network
|
|
func Connect(w http.ResponseWriter, r *http.Request) {
|
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
|
|
|
var (
|
|
aliases []string
|
|
netConnect types.NetworkConnect
|
|
)
|
|
if err := json.NewDecoder(r.Body).Decode(&netConnect); err != nil {
|
|
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
|
|
return
|
|
}
|
|
name := utils.GetName(r)
|
|
if netConnect.EndpointConfig != nil {
|
|
if netConnect.EndpointConfig.Aliases != nil {
|
|
aliases = netConnect.EndpointConfig.Aliases
|
|
}
|
|
}
|
|
err := runtime.ConnectContainerToNetwork(netConnect.Container, name, aliases)
|
|
if err != nil {
|
|
if errors.Cause(err) == define.ErrNoSuchCtr {
|
|
utils.ContainerNotFound(w, netConnect.Container, err)
|
|
return
|
|
}
|
|
if errors.Cause(err) == define.ErrNoSuchNetwork {
|
|
utils.Error(w, "network not found", http.StatusNotFound, err)
|
|
return
|
|
}
|
|
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
utils.WriteResponse(w, http.StatusOK, "OK")
|
|
}
|
|
|
|
// Disconnect removes a container from a network
|
|
func Disconnect(w http.ResponseWriter, r *http.Request) {
|
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
|
|
|
var netDisconnect types.NetworkDisconnect
|
|
if err := json.NewDecoder(r.Body).Decode(&netDisconnect); err != nil {
|
|
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
|
|
return
|
|
}
|
|
|
|
name := utils.GetName(r)
|
|
err := runtime.DisconnectContainerFromNetwork(netDisconnect.Container, name, netDisconnect.Force)
|
|
if err != nil {
|
|
if errors.Cause(err) == define.ErrNoSuchCtr {
|
|
utils.Error(w, "container not found", http.StatusNotFound, err)
|
|
return
|
|
}
|
|
if errors.Cause(err) == define.ErrNoSuchNetwork {
|
|
utils.Error(w, "network not found", http.StatusNotFound, err)
|
|
return
|
|
}
|
|
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
utils.WriteResponse(w, http.StatusOK, "OK")
|
|
}
|