mirror of https://github.com/containers/podman.git
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:
parent
a6ee8bf2af
commit
c3af2faab2
|
@ -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 (
|
import (
|
||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
"github.com/containers/storage/pkg/archive"
|
"github.com/containers/storage/pkg/archive"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create container
|
// Create container
|
||||||
|
@ -35,3 +36,30 @@ type swagChangesResponse struct {
|
||||||
Changes []archive.Change
|
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 (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/pkg/api/handlers/compat"
|
||||||
"github.com/containers/libpod/pkg/api/handlers/libpod"
|
"github.com/containers/libpod/pkg/api/handlers/libpod"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
|
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
|
// swagger:operation DELETE /libpod/networks/{name} libpod libpodRemoveNetwork
|
||||||
// ---
|
// ---
|
||||||
// tags:
|
// tags:
|
||||||
|
@ -33,6 +118,11 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
|
||||||
// $ref: "#/responses/NoSuchNetwork"
|
// $ref: "#/responses/NoSuchNetwork"
|
||||||
// 500:
|
// 500:
|
||||||
// $ref: "#/responses/InternalError"
|
// $ref: "#/responses/InternalError"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Libpod
|
||||||
|
*/
|
||||||
|
|
||||||
r.HandleFunc(VersionedPath("/libpod/networks/{name}"), s.APIHandler(libpod.RemoveNetwork)).Methods(http.MethodDelete)
|
r.HandleFunc(VersionedPath("/libpod/networks/{name}"), s.APIHandler(libpod.RemoveNetwork)).Methods(http.MethodDelete)
|
||||||
// swagger:operation GET /libpod/networks/{name}/json libpod libpodInspectNetwork
|
// swagger:operation GET /libpod/networks/{name}/json libpod libpodInspectNetwork
|
||||||
// ---
|
// ---
|
||||||
|
|
|
@ -21,5 +21,7 @@ tags:
|
||||||
description: Actions related to exec for the compatibility endpoints
|
description: Actions related to exec for the compatibility endpoints
|
||||||
- name: images (compat)
|
- name: images (compat)
|
||||||
description: Actions related to images for the compatibility endpoints
|
description: Actions related to images for the compatibility endpoints
|
||||||
|
- name: networks (compat)
|
||||||
|
description: Actions related to compatibility networks
|
||||||
- name: system (compat)
|
- name: system (compat)
|
||||||
description: Actions related to Podman and compatibility engines
|
description: Actions related to Podman and compatibility engines
|
||||||
|
|
|
@ -13,8 +13,11 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultNetworkDriver is the default network type used
|
||||||
|
var DefaultNetworkDriver string = "bridge"
|
||||||
|
|
||||||
// SupportedNetworkDrivers describes the list of supported drivers
|
// SupportedNetworkDrivers describes the list of supported drivers
|
||||||
var SupportedNetworkDrivers = []string{"bridge"}
|
var SupportedNetworkDrivers = []string{DefaultNetworkDriver}
|
||||||
|
|
||||||
// IsSupportedDriver checks if the user provided driver is supported
|
// IsSupportedDriver checks if the user provided driver is supported
|
||||||
func IsSupportedDriver(driver string) error {
|
func IsSupportedDriver(driver string) error {
|
||||||
|
@ -191,3 +194,16 @@ func InspectNetwork(config *config.Config, name string) (map[string]interface{},
|
||||||
err = json.Unmarshal(b, &rawList)
|
err = json.Unmarshal(b, &rawList)
|
||||||
return rawList, err
|
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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue