mirror of
https://github.com/containers/podman.git
synced 2025-07-15 03:02:52 +08:00
network compatibility endpoints for API
add endpoints for networking compatibility with the API. Signed-off-by: Brent Baude <bbaude@redhat.com>
This commit is contained in:
301
pkg/api/handlers/compat/networks.go
Normal file
301
pkg/api/handlers/compat/networks.go
Normal file
@ -0,0 +1,301 @@
|
||||
package compat
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containernetworking/cni/libcni"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/pkg/api/handlers/utils"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/libpod/pkg/domain/infra/abi"
|
||||
"github.com/containers/libpod/pkg/network"
|
||||
"github.com/docker/docker/api/types"
|
||||
dockerNetwork "github.com/docker/docker/api/types/network"
|
||||
"github.com/gorilla/schema"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type CompatInspectNetwork struct {
|
||||
types.NetworkResource
|
||||
}
|
||||
|
||||
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 {
|
||||
// TODO our network package does not distinguish between not finding a
|
||||
// specific network vs not being able to read it
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
report, err := getNetworkResourceByName(name, runtime)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
utils.WriteResponse(w, http.StatusOK, report)
|
||||
}
|
||||
|
||||
func getNetworkResourceByName(name string, runtime *libpod.Runtime) (*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.GetCNIConfigPathByName(config, name)
|
||||
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
|
||||
}
|
||||
|
||||
// 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[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: name,
|
||||
ID: "",
|
||||
Created: time.Unix(stat.Ctim.Sec, stat.Ctim.Nsec),
|
||||
Scope: "",
|
||||
Driver: network.DefaultNetworkDriver,
|
||||
EnableIPv6: false,
|
||||
IPAM: dockerNetwork.IPAM{
|
||||
Driver: "default",
|
||||
Options: nil,
|
||||
Config: ipamConfigs,
|
||||
},
|
||||
Internal: false,
|
||||
Attachable: false,
|
||||
Ingress: false,
|
||||
ConfigFrom: dockerNetwork.ConfigReference{},
|
||||
ConfigOnly: false,
|
||||
Containers: containerEndpoints,
|
||||
Options: nil,
|
||||
Labels: nil,
|
||||
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) {
|
||||
var (
|
||||
reports []*types.NetworkResource
|
||||
)
|
||||
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
|
||||
}
|
||||
// TODO remove when filters are implemented
|
||||
if len(query.Filters) > 0 {
|
||||
utils.InternalServerError(w, errors.New("filters for listing networks is not implemented"))
|
||||
return
|
||||
}
|
||||
netNames, err := network.GetNetworkNamesFromFileSystem(config)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
for _, name := range netNames {
|
||||
report, err := getNetworkResourceByName(name, runtime)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
}
|
||||
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
|
||||
}
|
||||
// At present I think we should just suport 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,
|
||||
}
|
||||
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}
|
||||
_, err := ce.NetworkCreate(r.Context(), name, ncOptions)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
}
|
||||
report := types.NetworkCreate{
|
||||
CheckDuplicate: networkCreate.CheckDuplicate,
|
||||
Driver: networkCreate.Driver,
|
||||
Scope: networkCreate.Scope,
|
||||
EnableIPv6: networkCreate.EnableIPv6,
|
||||
IPAM: networkCreate.IPAM,
|
||||
Internal: networkCreate.Internal,
|
||||
Attachable: networkCreate.Attachable,
|
||||
Ingress: networkCreate.Ingress,
|
||||
ConfigOnly: networkCreate.ConfigOnly,
|
||||
ConfigFrom: networkCreate.ConfigFrom,
|
||||
Options: networkCreate.Options,
|
||||
Labels: networkCreate.Labels,
|
||||
}
|
||||
utils.WriteResponse(w, http.StatusOK, report)
|
||||
}
|
||||
|
||||
func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
config, err := runtime.GetConfig()
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
name := utils.GetName(r)
|
||||
exists, err := network.Exists(config, name)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
if !exists {
|
||||
utils.Error(w, "network not found", http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
if err := network.RemoveNetwork(config, name); err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
}
|
||||
utils.WriteResponse(w, http.StatusNoContent, "")
|
||||
}
|
@ -3,6 +3,7 @@ package compat
|
||||
import (
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
// Create container
|
||||
@ -35,3 +36,30 @@ type swagChangesResponse struct {
|
||||
Changes []archive.Change
|
||||
}
|
||||
}
|
||||
|
||||
// Network inspect
|
||||
// swagger:response CompatNetworkInspect
|
||||
type swagCompatNetworkInspect struct {
|
||||
// in:body
|
||||
Body types.NetworkResource
|
||||
}
|
||||
|
||||
// Network list
|
||||
// swagger:response CompatNetworkList
|
||||
type swagCompatNetworkList struct {
|
||||
// in:body
|
||||
Body []types.NetworkResource
|
||||
}
|
||||
|
||||
// Network create
|
||||
// swagger:model NetworkCreateRequest
|
||||
type NetworkCreateRequest struct {
|
||||
types.NetworkCreateRequest
|
||||
}
|
||||
|
||||
// Network create
|
||||
// swagger:response CompatNetworkCreate
|
||||
type swagCompatNetworkCreateResponse struct {
|
||||
// in:body
|
||||
Body struct{ types.NetworkCreate }
|
||||
}
|
||||
|
@ -3,11 +3,96 @@ package server
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/containers/libpod/pkg/api/handlers/compat"
|
||||
"github.com/containers/libpod/pkg/api/handlers/libpod"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
|
||||
// swagger:operation DELETE /networks/{name} compat compatRemoveNetwork
|
||||
// ---
|
||||
// tags:
|
||||
// - networks (compat)
|
||||
// summary: Remove a network
|
||||
// description: Remove a network
|
||||
// parameters:
|
||||
// - in: path
|
||||
// name: name
|
||||
// type: string
|
||||
// required: true
|
||||
// description: the name of the network
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
// 204:
|
||||
// description: no error
|
||||
// 404:
|
||||
// $ref: "#/responses/NoSuchNetwork"
|
||||
// 500:
|
||||
// $ref: "#/responses/InternalError"
|
||||
r.HandleFunc(VersionedPath("/networks/{name}"), s.APIHandler(compat.RemoveNetwork)).Methods(http.MethodDelete)
|
||||
r.HandleFunc("/networks/{name}", s.APIHandler(compat.RemoveNetwork)).Methods(http.MethodDelete)
|
||||
// swagger:operation GET /networks/{name}/json compat compatInspectNetwork
|
||||
// ---
|
||||
// tags:
|
||||
// - networks (compat)
|
||||
// summary: Inspect a network
|
||||
// description: Display low level configuration network
|
||||
// parameters:
|
||||
// - in: path
|
||||
// name: name
|
||||
// type: string
|
||||
// required: true
|
||||
// description: the name of the network
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
// 200:
|
||||
// $ref: "#/responses/CompatNetworkInspect"
|
||||
// 404:
|
||||
// $ref: "#/responses/NoSuchNetwork"
|
||||
// 500:
|
||||
// $ref: "#/responses/InternalError"
|
||||
r.HandleFunc(VersionedPath("/networks/{name}/json"), s.APIHandler(compat.InspectNetwork)).Methods(http.MethodGet)
|
||||
r.HandleFunc("/networks/{name}/json", s.APIHandler(compat.InspectNetwork)).Methods(http.MethodGet)
|
||||
// swagger:operation GET /networks/json compat compatListNetwork
|
||||
// ---
|
||||
// tags:
|
||||
// - networks (compat)
|
||||
// summary: List networks
|
||||
// description: Display summary of network configurations
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
// 200:
|
||||
// $ref: "#/responses/CompatNetworkList"
|
||||
// 500:
|
||||
// $ref: "#/responses/InternalError"
|
||||
r.HandleFunc(VersionedPath("/networks/json"), s.APIHandler(compat.ListNetworks)).Methods(http.MethodGet)
|
||||
r.HandleFunc("/networks", s.APIHandler(compat.ListNetworks)).Methods(http.MethodGet)
|
||||
// swagger:operation POST /networks/create compat compatCreateNetwork
|
||||
// ---
|
||||
// tags:
|
||||
// - networks (compat)
|
||||
// summary: Create network
|
||||
// description: Create a network configuration
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - in: body
|
||||
// name: create
|
||||
// description: attributes for creating a container
|
||||
// schema:
|
||||
// $ref: "#/definitions/NetworkCreateRequest"
|
||||
// responses:
|
||||
// 200:
|
||||
// $ref: "#/responses/CompatNetworkCreate"
|
||||
// 400:
|
||||
// $ref: "#/responses/BadParamError"
|
||||
// 500:
|
||||
// $ref: "#/responses/InternalError"
|
||||
r.HandleFunc(VersionedPath("/networks/create"), s.APIHandler(compat.CreateNetwork)).Methods(http.MethodPost)
|
||||
r.HandleFunc("/networks/create", s.APIHandler(compat.CreateNetwork)).Methods(http.MethodPost)
|
||||
// swagger:operation DELETE /libpod/networks/{name} libpod libpodRemoveNetwork
|
||||
// ---
|
||||
// tags:
|
||||
@ -33,6 +118,11 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
|
||||
// $ref: "#/responses/NoSuchNetwork"
|
||||
// 500:
|
||||
// $ref: "#/responses/InternalError"
|
||||
|
||||
/*
|
||||
Libpod
|
||||
*/
|
||||
|
||||
r.HandleFunc(VersionedPath("/libpod/networks/{name}"), s.APIHandler(libpod.RemoveNetwork)).Methods(http.MethodDelete)
|
||||
// swagger:operation GET /libpod/networks/{name}/json libpod libpodInspectNetwork
|
||||
// ---
|
||||
|
@ -21,5 +21,7 @@ tags:
|
||||
description: Actions related to exec for the compatibility endpoints
|
||||
- name: images (compat)
|
||||
description: Actions related to images for the compatibility endpoints
|
||||
- name: networks (compat)
|
||||
description: Actions related to compatibility networks
|
||||
- name: system (compat)
|
||||
description: Actions related to Podman and compatibility engines
|
||||
|
@ -13,8 +13,11 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// DefaultNetworkDriver is the default network type used
|
||||
var DefaultNetworkDriver string = "bridge"
|
||||
|
||||
// SupportedNetworkDrivers describes the list of supported drivers
|
||||
var SupportedNetworkDrivers = []string{"bridge"}
|
||||
var SupportedNetworkDrivers = []string{DefaultNetworkDriver}
|
||||
|
||||
// IsSupportedDriver checks if the user provided driver is supported
|
||||
func IsSupportedDriver(driver string) error {
|
||||
@ -191,3 +194,16 @@ func InspectNetwork(config *config.Config, name string) (map[string]interface{},
|
||||
err = json.Unmarshal(b, &rawList)
|
||||
return rawList, err
|
||||
}
|
||||
|
||||
// Exists says whether a given network exists or not; it meant
|
||||
// specifically for restful reponses so 404s can be used
|
||||
func Exists(config *config.Config, name string) (bool, error) {
|
||||
_, err := ReadRawCNIConfByName(config, name)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == ErrNetworkNotFound {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user