github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/api/handlers/compat/networks.go (about)

     1  package compat
     2  
     3  import (
     4  	"encoding/json"
     5  	"net"
     6  	"net/http"
     7  	"os"
     8  	"strings"
     9  	"syscall"
    10  	"time"
    11  
    12  	"github.com/containernetworking/cni/libcni"
    13  	"github.com/containers/podman/v2/libpod"
    14  	"github.com/containers/podman/v2/libpod/define"
    15  	"github.com/containers/podman/v2/libpod/network"
    16  	"github.com/containers/podman/v2/pkg/api/handlers/utils"
    17  	"github.com/containers/podman/v2/pkg/domain/entities"
    18  	"github.com/containers/podman/v2/pkg/domain/infra/abi"
    19  	"github.com/docker/docker/api/types"
    20  	dockerNetwork "github.com/docker/docker/api/types/network"
    21  	"github.com/gorilla/schema"
    22  	"github.com/pkg/errors"
    23  	"github.com/sirupsen/logrus"
    24  )
    25  
    26  func InspectNetwork(w http.ResponseWriter, r *http.Request) {
    27  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
    28  
    29  	// FYI scope and version are currently unused but are described by the API
    30  	// Leaving this for if/when we have to enable these
    31  	// query := struct {
    32  	//	scope   string
    33  	//	verbose bool
    34  	// }{
    35  	//	// override any golang type defaults
    36  	// }
    37  	// decoder := r.Context().Value("decoder").(*schema.Decoder)
    38  	// if err := decoder.Decode(&query, r.URL.Query()); err != nil {
    39  	//	utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
    40  	//	return
    41  	// }
    42  	config, err := runtime.GetConfig()
    43  	if err != nil {
    44  		utils.InternalServerError(w, err)
    45  		return
    46  	}
    47  	name := utils.GetName(r)
    48  	_, err = network.InspectNetwork(config, name)
    49  	if err != nil {
    50  		utils.NetworkNotFound(w, name, err)
    51  		return
    52  	}
    53  	report, err := getNetworkResourceByName(name, runtime)
    54  	if err != nil {
    55  		utils.InternalServerError(w, err)
    56  		return
    57  	}
    58  	utils.WriteResponse(w, http.StatusOK, report)
    59  }
    60  
    61  func getNetworkResourceByName(name string, runtime *libpod.Runtime) (*types.NetworkResource, error) {
    62  	var (
    63  		ipamConfigs []dockerNetwork.IPAMConfig
    64  	)
    65  	config, err := runtime.GetConfig()
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	containerEndpoints := map[string]types.EndpointResource{}
    70  	// Get the network path so we can get created time
    71  	networkConfigPath, err := network.GetCNIConfigPathByName(config, name)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	f, err := os.Stat(networkConfigPath)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	stat := f.Sys().(*syscall.Stat_t)
    80  	cons, err := runtime.GetAllContainers()
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	conf, err := libcni.ConfListFromFile(networkConfigPath)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	// No Bridge plugin means we bail
    90  	bridge, err := genericPluginsToBridge(conf.Plugins, network.DefaultNetworkDriver)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	for _, outer := range bridge.IPAM.Ranges {
    95  		for _, n := range outer {
    96  			ipamConfig := dockerNetwork.IPAMConfig{
    97  				Subnet:  n.Subnet,
    98  				Gateway: n.Gateway,
    99  			}
   100  			ipamConfigs = append(ipamConfigs, ipamConfig)
   101  		}
   102  	}
   103  
   104  	for _, con := range cons {
   105  		data, err := con.Inspect(false)
   106  		if err != nil {
   107  			return nil, err
   108  		}
   109  		if netData, ok := data.NetworkSettings.Networks[name]; ok {
   110  			containerEndpoint := types.EndpointResource{
   111  				Name:        netData.NetworkID,
   112  				EndpointID:  netData.EndpointID,
   113  				MacAddress:  netData.MacAddress,
   114  				IPv4Address: netData.IPAddress,
   115  				IPv6Address: netData.GlobalIPv6Address,
   116  			}
   117  			containerEndpoints[con.ID()] = containerEndpoint
   118  		}
   119  	}
   120  	report := types.NetworkResource{
   121  		Name:       name,
   122  		ID:         name,
   123  		Created:    time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)), // nolint: unconvert
   124  		Scope:      "",
   125  		Driver:     network.DefaultNetworkDriver,
   126  		EnableIPv6: false,
   127  		IPAM: dockerNetwork.IPAM{
   128  			Driver:  "default",
   129  			Options: nil,
   130  			Config:  ipamConfigs,
   131  		},
   132  		Internal:   false,
   133  		Attachable: false,
   134  		Ingress:    false,
   135  		ConfigFrom: dockerNetwork.ConfigReference{},
   136  		ConfigOnly: false,
   137  		Containers: containerEndpoints,
   138  		Options:    nil,
   139  		Labels:     nil,
   140  		Peers:      nil,
   141  		Services:   nil,
   142  	}
   143  	return &report, nil
   144  }
   145  
   146  func genericPluginsToBridge(plugins []*libcni.NetworkConfig, pluginType string) (network.HostLocalBridge, error) {
   147  	var bridge network.HostLocalBridge
   148  	generic, err := findPluginByName(plugins, pluginType)
   149  	if err != nil {
   150  		return bridge, err
   151  	}
   152  	err = json.Unmarshal(generic, &bridge)
   153  	return bridge, err
   154  }
   155  
   156  func findPluginByName(plugins []*libcni.NetworkConfig, pluginType string) ([]byte, error) {
   157  	for _, p := range plugins {
   158  		if pluginType == p.Network.Type {
   159  			return p.Bytes, nil
   160  		}
   161  	}
   162  	return nil, errors.New("unable to find bridge plugin")
   163  }
   164  
   165  func ListNetworks(w http.ResponseWriter, r *http.Request) {
   166  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   167  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   168  	query := struct {
   169  		Filters map[string][]string `schema:"filters"`
   170  	}{
   171  		// override any golang type defaults
   172  	}
   173  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   174  		utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
   175  		return
   176  	}
   177  	config, err := runtime.GetConfig()
   178  	if err != nil {
   179  		utils.InternalServerError(w, err)
   180  		return
   181  	}
   182  
   183  	filterNames, nameFilterExists := query.Filters["name"]
   184  	// TODO remove when filters are implemented
   185  	if (!nameFilterExists && len(query.Filters) > 0) || len(query.Filters) > 1 {
   186  		utils.InternalServerError(w, errors.New("only the name filter for listing networks is implemented"))
   187  		return
   188  	}
   189  	netNames, err := network.GetNetworkNamesFromFileSystem(config)
   190  	if err != nil {
   191  		utils.InternalServerError(w, err)
   192  		return
   193  	}
   194  
   195  	// filter by name
   196  	if nameFilterExists {
   197  		names := []string{}
   198  		for _, name := range netNames {
   199  			for _, filter := range filterNames {
   200  				if strings.Contains(name, filter) {
   201  					names = append(names, name)
   202  					break
   203  				}
   204  			}
   205  		}
   206  		netNames = names
   207  	}
   208  
   209  	reports := make([]*types.NetworkResource, 0, len(netNames))
   210  	logrus.Errorf("netNames: %q", strings.Join(netNames, ", "))
   211  	for _, name := range netNames {
   212  		report, err := getNetworkResourceByName(name, runtime)
   213  		if err != nil {
   214  			utils.InternalServerError(w, err)
   215  			return
   216  		}
   217  		reports = append(reports, report)
   218  	}
   219  	utils.WriteResponse(w, http.StatusOK, reports)
   220  }
   221  
   222  func CreateNetwork(w http.ResponseWriter, r *http.Request) {
   223  	var (
   224  		name          string
   225  		networkCreate types.NetworkCreateRequest
   226  	)
   227  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   228  	if err := json.NewDecoder(r.Body).Decode(&networkCreate); err != nil {
   229  		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
   230  		return
   231  	}
   232  
   233  	if len(networkCreate.Name) > 0 {
   234  		name = networkCreate.Name
   235  	}
   236  	if len(networkCreate.Driver) < 1 {
   237  		networkCreate.Driver = network.DefaultNetworkDriver
   238  	}
   239  	// At present I think we should just support the bridge driver
   240  	// and allow demand to make us consider more
   241  	if networkCreate.Driver != network.DefaultNetworkDriver {
   242  		utils.InternalServerError(w, errors.New("network create only supports the bridge driver"))
   243  		return
   244  	}
   245  	ncOptions := entities.NetworkCreateOptions{
   246  		Driver:   network.DefaultNetworkDriver,
   247  		Internal: networkCreate.Internal,
   248  	}
   249  	if networkCreate.IPAM != nil && networkCreate.IPAM.Config != nil {
   250  		if len(networkCreate.IPAM.Config) > 1 {
   251  			utils.InternalServerError(w, errors.New("compat network create can only support one IPAM config"))
   252  			return
   253  		}
   254  
   255  		if len(networkCreate.IPAM.Config[0].Subnet) > 0 {
   256  			_, subnet, err := net.ParseCIDR(networkCreate.IPAM.Config[0].Subnet)
   257  			if err != nil {
   258  				utils.InternalServerError(w, err)
   259  				return
   260  			}
   261  			ncOptions.Subnet = *subnet
   262  		}
   263  		if len(networkCreate.IPAM.Config[0].Gateway) > 0 {
   264  			ncOptions.Gateway = net.ParseIP(networkCreate.IPAM.Config[0].Gateway)
   265  		}
   266  		if len(networkCreate.IPAM.Config[0].IPRange) > 0 {
   267  			_, IPRange, err := net.ParseCIDR(networkCreate.IPAM.Config[0].IPRange)
   268  			if err != nil {
   269  				utils.InternalServerError(w, err)
   270  				return
   271  			}
   272  			ncOptions.Range = *IPRange
   273  		}
   274  	}
   275  	ce := abi.ContainerEngine{Libpod: runtime}
   276  	if _, err := ce.NetworkCreate(r.Context(), name, ncOptions); err != nil {
   277  		utils.InternalServerError(w, err)
   278  		return
   279  	}
   280  
   281  	body := struct {
   282  		Id      string
   283  		Warning []string
   284  	}{
   285  		Id: name,
   286  	}
   287  	utils.WriteResponse(w, http.StatusCreated, body)
   288  }
   289  
   290  func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
   291  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   292  	ic := abi.ContainerEngine{Libpod: runtime}
   293  
   294  	query := struct {
   295  		Force bool `schema:"force"`
   296  	}{
   297  		// This is where you can override the golang default value for one of fields
   298  	}
   299  
   300  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   301  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   302  		utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
   303  		return
   304  	}
   305  
   306  	options := entities.NetworkRmOptions{
   307  		Force: query.Force,
   308  	}
   309  
   310  	name := utils.GetName(r)
   311  	reports, err := ic.NetworkRm(r.Context(), []string{name}, options)
   312  	if err != nil {
   313  		utils.Error(w, "remove Network failed", http.StatusInternalServerError, err)
   314  		return
   315  	}
   316  	if len(reports) == 0 {
   317  		utils.Error(w, "remove Network failed", http.StatusInternalServerError, errors.Errorf("internal error"))
   318  		return
   319  	}
   320  	report := reports[0]
   321  	if report.Err != nil {
   322  		if errors.Cause(report.Err) == define.ErrNoSuchNetwork {
   323  			utils.Error(w, "network not found", http.StatusNotFound, define.ErrNoSuchNetwork)
   324  			return
   325  		}
   326  		utils.InternalServerError(w, report.Err)
   327  		return
   328  	}
   329  
   330  	utils.WriteResponse(w, http.StatusNoContent, "")
   331  }
   332  
   333  // Connect adds a container to a network
   334  func Connect(w http.ResponseWriter, r *http.Request) {
   335  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   336  
   337  	var (
   338  		aliases    []string
   339  		netConnect types.NetworkConnect
   340  	)
   341  	if err := json.NewDecoder(r.Body).Decode(&netConnect); err != nil {
   342  		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
   343  		return
   344  	}
   345  	name := utils.GetName(r)
   346  	if netConnect.EndpointConfig != nil {
   347  		if netConnect.EndpointConfig.Aliases != nil {
   348  			aliases = netConnect.EndpointConfig.Aliases
   349  		}
   350  	}
   351  	err := runtime.ConnectContainerToNetwork(netConnect.Container, name, aliases)
   352  	if err != nil {
   353  		if errors.Cause(err) == define.ErrNoSuchCtr {
   354  			utils.ContainerNotFound(w, netConnect.Container, err)
   355  			return
   356  		}
   357  		if errors.Cause(err) == define.ErrNoSuchNetwork {
   358  			utils.Error(w, "network not found", http.StatusNotFound, err)
   359  			return
   360  		}
   361  		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
   362  		return
   363  	}
   364  	utils.WriteResponse(w, http.StatusOK, "OK")
   365  }
   366  
   367  // Disconnect removes a container from a network
   368  func Disconnect(w http.ResponseWriter, r *http.Request) {
   369  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   370  
   371  	var netDisconnect types.NetworkDisconnect
   372  	if err := json.NewDecoder(r.Body).Decode(&netDisconnect); err != nil {
   373  		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
   374  		return
   375  	}
   376  
   377  	name := utils.GetName(r)
   378  	err := runtime.DisconnectContainerFromNetwork(netDisconnect.Container, name, netDisconnect.Force)
   379  	if err != nil {
   380  		if errors.Cause(err) == define.ErrNoSuchCtr {
   381  			utils.Error(w, "container not found", http.StatusNotFound, err)
   382  			return
   383  		}
   384  		if errors.Cause(err) == define.ErrNoSuchNetwork {
   385  			utils.Error(w, "network not found", http.StatusNotFound, err)
   386  			return
   387  		}
   388  		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
   389  		return
   390  	}
   391  	utils.WriteResponse(w, http.StatusOK, "OK")
   392  }