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

Use the stylecheck linter and fix the reported problems. [NO TESTS NEEDED] Signed-off-by: Paul Holzinger <paul.holzinger@web.de>
416 lines
12 KiB
Go
416 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/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: "local",
|
|
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
|
|
}
|
|
|
|
reports := []*types.NetworkResource{}
|
|
logrus.Debugf("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 && 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) {
|
|
// TODO Filters are not implemented
|
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
|
ic := abi.ContainerEngine{Libpod: runtime}
|
|
pruneOptions := entities.NetworkPruneOptions{}
|
|
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
|
|
}
|
|
var prunedNetworks []string //nolint
|
|
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})
|
|
}
|