mirror of
https://github.com/containers/podman.git
synced 2025-05-20 08:36:23 +08:00

The api doc used wrong response examples for both the compat and libpod network prune endpoints. Change the doc so that it matches the actual return values. Also fix the endpoints to return an empty array instead of null when no networks are removed. [NO TESTS NEEDED] Fixes: #10564 Signed-off-by: Paul Holzinger <pholzing@redhat.com>
427 lines
12 KiB
Go
427 lines
12 KiB
Go
package compat
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/containernetworking/cni/libcni"
|
|
"github.com/containers/podman/v3/libpod"
|
|
"github.com/containers/podman/v3/libpod/define"
|
|
"github.com/containers/podman/v3/libpod/network"
|
|
"github.com/containers/podman/v3/pkg/api/handlers/utils"
|
|
"github.com/containers/podman/v3/pkg/domain/entities"
|
|
"github.com/containers/podman/v3/pkg/domain/infra/abi"
|
|
networkid "github.com/containers/podman/v3/pkg/network"
|
|
"github.com/containers/podman/v3/pkg/util"
|
|
"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
|
|
}
|
|
}
|
|
|
|
labels := network.GetNetworkLabels(conf)
|
|
if labels == nil {
|
|
labels = map[string]string{}
|
|
}
|
|
|
|
report := types.NetworkResource{
|
|
Name: conf.Name,
|
|
ID: networkid.GetNetworkID(conf.Name),
|
|
Created: time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)), // nolint: unconvert
|
|
Scope: "local",
|
|
Driver: network.DefaultNetworkDriver,
|
|
EnableIPv6: false,
|
|
IPAM: dockerNetwork.IPAM{
|
|
Driver: "default",
|
|
Options: map[string]string{},
|
|
Config: ipamConfigs,
|
|
},
|
|
Internal: !bridge.IsGW,
|
|
Attachable: false,
|
|
Ingress: false,
|
|
ConfigFrom: dockerNetwork.ConfigReference{},
|
|
ConfigOnly: false,
|
|
Containers: containerEndpoints,
|
|
Options: map[string]string{},
|
|
Labels: labels,
|
|
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)
|
|
filterMap, err := util.PrepareFilters(r)
|
|
if err != nil {
|
|
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, 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
|
|
}
|
|
|
|
reports := []*types.NetworkResource{}
|
|
logrus.Debugf("netNames: %q", strings.Join(netNames, ", "))
|
|
for _, name := range netNames {
|
|
report, err := getNetworkResourceByNameOrID(name, runtime, *filterMap)
|
|
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 && len(networkCreate.IPAM.Config) > 0 {
|
|
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
|
|
}
|
|
|
|
net, err := getNetworkResourceByNameOrID(name, runtime, nil)
|
|
if err != nil {
|
|
utils.InternalServerError(w, err)
|
|
return
|
|
}
|
|
body := struct {
|
|
ID string `json:"Id"`
|
|
Warning []string
|
|
}{
|
|
ID: net.ID,
|
|
}
|
|
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, nil)
|
|
}
|
|
|
|
// 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")
|
|
}
|
|
|
|
// Prune removes unused networks
|
|
func Prune(w http.ResponseWriter, r *http.Request) {
|
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
|
filterMap, err := util.PrepareFilters(r)
|
|
if err != nil {
|
|
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
|
|
return
|
|
}
|
|
|
|
ic := abi.ContainerEngine{Libpod: runtime}
|
|
pruneOptions := entities.NetworkPruneOptions{
|
|
Filters: *filterMap,
|
|
}
|
|
pruneReports, err := ic.NetworkPrune(r.Context(), pruneOptions)
|
|
if err != nil {
|
|
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
type response struct {
|
|
NetworksDeleted []string
|
|
}
|
|
prunedNetworks := []string{}
|
|
for _, pr := range pruneReports {
|
|
if pr.Error != nil {
|
|
logrus.Error(pr.Error)
|
|
continue
|
|
}
|
|
prunedNetworks = append(prunedNetworks, pr.Name)
|
|
}
|
|
utils.WriteResponse(w, http.StatusOK, response{NetworksDeleted: prunedNetworks})
|
|
}
|