azure-container-networking/cns/dockerclient/dockerclient.go

200 строки
5.4 KiB
Go

// Copyright 2017 Microsoft. All rights reserved.
// MIT License
package dockerclient
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/Azure/azure-container-networking/cns/logger"
"github.com/Azure/azure-container-networking/cns/wireserver"
"github.com/Azure/azure-container-networking/common"
"github.com/Azure/azure-container-networking/platform"
"github.com/pkg/errors"
)
const (
defaultDockerConnectionURL = "http://127.0.0.1:2375"
defaultIpamPlugin = "azure-vnet"
networkMode = "com.microsoft.azure.network.mode"
bridgeMode = "bridge"
)
type interfaceGetter interface {
GetInterfaces(ctx context.Context) (*wireserver.GetInterfacesResult, error)
}
// Client specifies a client to connect to docker.
type Client struct {
connectionURL string
wscli interfaceGetter
}
// NewDefaultClient create a new docker client.
func NewDefaultClient(wscli interfaceGetter) (*Client, error) {
return &Client{
connectionURL: defaultDockerConnectionURL,
wscli: wscli,
}, nil
}
// NetworkExists tries to retrieve a network from docker (if it exists).
func (c *Client) NetworkExists(networkName string) error {
logger.Printf("[Azure CNS] NetworkExists")
res, err := http.Get(
c.connectionURL + inspectNetworkPath + networkName)
if err != nil {
logger.Errorf("[Azure CNS] Error received from http Post for docker network inspect %v %v", networkName, err.Error())
return err
}
defer res.Body.Close()
// network exists
if res.StatusCode == 200 {
logger.Debugf("[Azure CNS] Network with name %v already exists. Docker return code: %v", networkName, res.StatusCode)
return nil
}
// network not found
if res.StatusCode == 404 {
logger.Debugf("[Azure CNS] Network with name %v does not exist. Docker return code: %v", networkName, res.StatusCode)
return fmt.Errorf("Network not found")
}
return fmt.Errorf("Unknown return code from docker inspect %d", res.StatusCode)
}
// CreateNetwork creates a network using docker network create.
func (c *Client) CreateNetwork(networkName string, nicInfo *wireserver.InterfaceInfo, options map[string]interface{}) error {
logger.Printf("[Azure CNS] CreateNetwork")
enableSnat := true
config := &Config{
Subnet: nicInfo.Subnet,
}
configs := make([]Config, 1)
configs[0] = *config
ipamConfig := &IPAM{
Driver: defaultIpamPlugin,
Config: configs,
}
netConfig := &NetworkConfiguration{
Name: networkName,
Driver: defaultNetworkPlugin,
IPAM: *ipamConfig,
Internal: true,
}
if options != nil {
if _, ok := options[OptDisableSnat]; ok {
enableSnat = false
}
}
if enableSnat {
netConfig.Options = make(map[string]interface{})
netConfig.Options[networkMode] = bridgeMode
}
logger.Printf("[Azure CNS] Going to create network with config: %+v", netConfig)
netConfigJSON := new(bytes.Buffer)
err := json.NewEncoder(netConfigJSON).Encode(netConfig)
if err != nil {
return err
}
res, err := http.Post(
c.connectionURL+createNetworkPath,
common.JsonContent,
netConfigJSON)
if err != nil {
logger.Printf("[Azure CNS] Error received from http Post for docker network create %v", networkName)
return err
}
defer res.Body.Close()
if res.StatusCode != 201 {
var createNetworkResponse DockerErrorResponse
err = json.NewDecoder(res.Body).Decode(&createNetworkResponse)
var ermsg string
ermsg = ""
if err != nil {
ermsg = err.Error()
}
return fmt.Errorf("[Azure CNS] Create docker network failed with error code %v - %v - %v",
res.StatusCode, createNetworkResponse.message, ermsg)
}
if enableSnat {
err = platform.SetOutboundSNAT(nicInfo.Subnet)
if err != nil {
logger.Printf("[Azure CNS] Error setting up SNAT outbound rule %v", err)
}
}
return nil
}
// DeleteNetwork creates a network using docker network create.
func (c *Client) DeleteNetwork(networkName string) error {
p := platform.NewExecClient()
logger.Printf("[Azure CNS] DeleteNetwork")
url := c.connectionURL + inspectNetworkPath + networkName
req, err := http.NewRequest("DELETE", url, nil)
if err != nil {
logger.Printf("[Azure CNS] Error received while creating http DELETE request for network delete %v %v", networkName, err.Error())
return err
}
req.Header.Set(common.ContentType, common.JsonContent)
client := &http.Client{}
res, err := client.Do(req)
if err != nil {
logger.Printf("[Azure CNS] HTTP Post returned error %v", err.Error())
return err
}
defer res.Body.Close()
// network successfully deleted.
if res.StatusCode == 204 {
res, err := c.wscli.GetInterfaces(context.TODO()) // TODO(rbtr): thread context through this client
if err != nil {
return errors.Wrap(err, "failed to get interfaces from IMDS")
}
primaryNic, err := wireserver.GetPrimaryInterfaceFromResult(res)
if err != nil {
return errors.Wrap(err, "failed to get primary interface from IMDS response")
}
cmd := fmt.Sprintf("iptables -t nat -D POSTROUTING -m iprange ! --dst-range 168.63.129.16 -m addrtype ! --dst-type local ! -d %v -j MASQUERADE",
primaryNic.Subnet)
_, err = p.ExecuteCommand(cmd)
if err != nil {
logger.Printf("[Azure CNS] Error Removing Outbound SNAT rule %v", err)
}
return nil
}
// network not found.
if res.StatusCode == 404 {
return fmt.Errorf("[Azure CNS] Network not found %v", networkName)
}
return fmt.Errorf("[Azure CNS] Unknown return code from docker delete network %v: ret = %d",
networkName, res.StatusCode)
}