mirror of
				https://github.com/containers/podman.git
				synced 2025-11-01 02:42:11 +08:00 
			
		
		
		
	 3b6d7a3669
			
		
	
	3b6d7a3669
	
	
	
		
			
			Add some more tests, document cases where remote will not work Add FIXMEs for tests that should work on podman-remote but currently do not. Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
		
			
				
	
	
		
			393 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			393 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 := 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:         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:   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) {
 | |
| 	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
 | |
| 	}
 | |
| 
 | |
| 	filterNames, nameFilterExists := query.Filters["name"]
 | |
| 	// TODO remove when filters are implemented
 | |
| 	if (!nameFilterExists && len(query.Filters) > 0) || len(query.Filters) > 1 {
 | |
| 		utils.InternalServerError(w, errors.New("only the name filter for listing networks is implemented"))
 | |
| 		return
 | |
| 	}
 | |
| 	netNames, err := network.GetNetworkNamesFromFileSystem(config)
 | |
| 	if err != nil {
 | |
| 		utils.InternalServerError(w, err)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// filter by name
 | |
| 	if nameFilterExists {
 | |
| 		names := []string{}
 | |
| 		for _, name := range netNames {
 | |
| 			for _, filter := range filterNames {
 | |
| 				if strings.Contains(name, filter) {
 | |
| 					names = append(names, name)
 | |
| 					break
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		netNames = names
 | |
| 	}
 | |
| 
 | |
| 	reports := make([]*types.NetworkResource, 0, len(netNames))
 | |
| 	logrus.Errorf("netNames: %q", strings.Join(netNames, ", "))
 | |
| 	for _, name := range netNames {
 | |
| 		report, err := getNetworkResourceByName(name, runtime)
 | |
| 		if err != nil {
 | |
| 			utils.InternalServerError(w, err)
 | |
| 			return
 | |
| 		}
 | |
| 		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,
 | |
| 	}
 | |
| 	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")
 | |
| }
 |