github.com/looshlee/cilium@v1.6.12/plugins/cilium-docker/driver/driver.go (about)

     1  // Copyright 2016-2017 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package driver
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  	"net"
    21  	"net/http"
    22  	"reflect"
    23  	"time"
    24  
    25  	"github.com/cilium/cilium/api/v1/models"
    26  	"github.com/cilium/cilium/pkg/client"
    27  	"github.com/cilium/cilium/pkg/datapath/link"
    28  	"github.com/cilium/cilium/pkg/datapath/linux/route"
    29  	"github.com/cilium/cilium/pkg/endpoint/connector"
    30  	endpointIDPkg "github.com/cilium/cilium/pkg/endpoint/id"
    31  	"github.com/cilium/cilium/pkg/lock"
    32  	"github.com/cilium/cilium/pkg/logging"
    33  	"github.com/cilium/cilium/pkg/logging/logfields"
    34  	"github.com/cilium/cilium/pkg/option"
    35  
    36  	"github.com/docker/libnetwork/drivers/remote/api"
    37  	lnTypes "github.com/docker/libnetwork/types"
    38  	"github.com/gorilla/mux"
    39  	"github.com/sirupsen/logrus"
    40  	"github.com/vishvananda/netlink"
    41  )
    42  
    43  var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "cilium-docker-driver")
    44  
    45  const (
    46  	// ContainerInterfacePrefix is the container's internal interface name prefix.
    47  	ContainerInterfacePrefix = "cilium"
    48  )
    49  
    50  // Driver interface that listens for docker requests.
    51  type Driver interface {
    52  	Listen(string) error
    53  }
    54  
    55  type driver struct {
    56  	mutex       lock.RWMutex
    57  	client      *client.Client
    58  	conf        models.DaemonConfigurationStatus
    59  	routes      []api.StaticRoute
    60  	gatewayIPv6 string
    61  	gatewayIPv4 string
    62  }
    63  
    64  func endpointID(id string) string {
    65  	return endpointIDPkg.NewID(endpointIDPkg.DockerEndpointPrefix, id)
    66  }
    67  
    68  func newLibnetworkRoute(route route.Route) api.StaticRoute {
    69  	rt := api.StaticRoute{
    70  		Destination: route.Prefix.String(),
    71  		RouteType:   lnTypes.CONNECTED,
    72  	}
    73  
    74  	if route.Nexthop != nil {
    75  		rt.NextHop = route.Nexthop.String()
    76  		rt.RouteType = lnTypes.NEXTHOP
    77  	}
    78  
    79  	return rt
    80  }
    81  
    82  // NewDriver creates and returns a new Driver for the given API URL.
    83  // If url is nil then use SockPath provided by CILIUM_SOCK
    84  // or the cilium default SockPath
    85  func NewDriver(url string) (Driver, error) {
    86  
    87  	if url == "" {
    88  		url = client.DefaultSockPath()
    89  	}
    90  
    91  	scopedLog := log.WithField("url", url)
    92  	c, err := client.NewClient(url)
    93  	if err != nil {
    94  		scopedLog.WithError(err).Fatal("Error while starting cilium-client")
    95  	}
    96  
    97  	d := &driver{client: c}
    98  
    99  	for tries := 0; tries < 24; tries++ {
   100  		if res, err := c.ConfigGet(); err != nil {
   101  			if tries == 23 {
   102  				scopedLog.WithError(err).Fatal("Unable to connect to cilium daemon")
   103  			} else {
   104  				scopedLog.Info("Waiting for cilium daemon to start up...")
   105  			}
   106  			time.Sleep(time.Duration(tries) * time.Second)
   107  		} else {
   108  			if res.Status.Addressing == nil || (res.Status.Addressing.IPV4 == nil && res.Status.Addressing.IPV6 == nil) {
   109  				scopedLog.Fatal("Invalid addressing information from daemon")
   110  			}
   111  
   112  			d.conf = *res.Status
   113  			break
   114  		}
   115  	}
   116  
   117  	if err := connector.SufficientAddressing(d.conf.Addressing); err != nil {
   118  		scopedLog.WithError(err).Fatal("Insufficient addressing")
   119  	}
   120  
   121  	d.updateRoutes(nil)
   122  
   123  	log.Infof("Cilium Docker plugin ready")
   124  
   125  	return d, nil
   126  }
   127  
   128  func (driver *driver) updateRoutes(addressing *models.NodeAddressing) {
   129  	driver.mutex.Lock()
   130  	defer driver.mutex.Unlock()
   131  
   132  	if addressing != nil {
   133  		driver.conf.Addressing = addressing
   134  	}
   135  
   136  	driver.routes = []api.StaticRoute{}
   137  
   138  	if driver.conf.Addressing.IPV6 != nil && driver.conf.Addressing.IPV6.Enabled {
   139  		if routes, err := connector.IPv6Routes(driver.conf.Addressing, int(driver.conf.RouteMTU)); err != nil {
   140  			log.Fatalf("Unable to generate IPv6 routes: %s", err)
   141  		} else {
   142  			for _, r := range routes {
   143  				driver.routes = append(driver.routes, newLibnetworkRoute(r))
   144  			}
   145  		}
   146  
   147  		driver.gatewayIPv6 = connector.IPv6Gateway(driver.conf.Addressing)
   148  	}
   149  
   150  	if driver.conf.Addressing.IPV4 != nil && driver.conf.Addressing.IPV4.Enabled {
   151  		if routes, err := connector.IPv4Routes(driver.conf.Addressing, int(driver.conf.RouteMTU)); err != nil {
   152  			log.Fatalf("Unable to generate IPv4 routes: %s", err)
   153  		} else {
   154  			for _, r := range routes {
   155  				driver.routes = append(driver.routes, newLibnetworkRoute(r))
   156  			}
   157  		}
   158  
   159  		driver.gatewayIPv4 = connector.IPv4Gateway(driver.conf.Addressing)
   160  	}
   161  }
   162  
   163  // Listen listens for docker requests on a particular set of endpoints on the given
   164  // socket path.
   165  func (driver *driver) Listen(socket string) error {
   166  	router := mux.NewRouter()
   167  	router.NotFoundHandler = http.HandlerFunc(notFound)
   168  
   169  	handleMethod := func(method string, h http.HandlerFunc) {
   170  		router.Methods("POST").Path(fmt.Sprintf("/%s", method)).HandlerFunc(h)
   171  	}
   172  
   173  	handleMethod("Plugin.Activate", driver.handshake)
   174  	handleMethod("NetworkDriver.GetCapabilities", driver.capabilities)
   175  	handleMethod("NetworkDriver.CreateNetwork", driver.createNetwork)
   176  	handleMethod("NetworkDriver.DeleteNetwork", driver.deleteNetwork)
   177  	handleMethod("NetworkDriver.CreateEndpoint", driver.createEndpoint)
   178  	handleMethod("NetworkDriver.DeleteEndpoint", driver.deleteEndpoint)
   179  	handleMethod("NetworkDriver.EndpointOperInfo", driver.infoEndpoint)
   180  	handleMethod("NetworkDriver.Join", driver.joinEndpoint)
   181  	handleMethod("NetworkDriver.Leave", driver.leaveEndpoint)
   182  	handleMethod("IpamDriver.GetCapabilities", driver.ipamCapabilities)
   183  	handleMethod("IpamDriver.GetDefaultAddressSpaces", driver.getDefaultAddressSpaces)
   184  	handleMethod("IpamDriver.RequestPool", driver.requestPool)
   185  	handleMethod("IpamDriver.ReleasePool", driver.releasePool)
   186  	handleMethod("IpamDriver.RequestAddress", driver.requestAddress)
   187  	handleMethod("IpamDriver.ReleaseAddress", driver.releaseAddress)
   188  
   189  	listener, err := net.Listen("unix", socket)
   190  	if err != nil {
   191  		return err
   192  	}
   193  	return http.Serve(listener, router)
   194  }
   195  
   196  func notFound(w http.ResponseWriter, r *http.Request) {
   197  	log.WithField(logfields.Object, logfields.Repr(r)).Warn("plugin Not found")
   198  	http.NotFound(w, r)
   199  }
   200  
   201  func sendError(w http.ResponseWriter, msg string, code int) {
   202  	log.WithFields(logrus.Fields{
   203  		"code": code,
   204  		"msg":  msg,
   205  	}).Error("Sending error")
   206  	http.Error(w, msg, code)
   207  }
   208  
   209  func objectResponse(w http.ResponseWriter, obj interface{}) {
   210  	if err := json.NewEncoder(w).Encode(obj); err != nil {
   211  		sendError(w, "Could not JSON encode response", http.StatusInternalServerError)
   212  		return
   213  	}
   214  }
   215  
   216  func emptyResponse(w http.ResponseWriter) {
   217  	json.NewEncoder(w).Encode(map[string]string{})
   218  }
   219  
   220  type handshakeResp struct {
   221  	Implements []string
   222  }
   223  
   224  func (driver *driver) handshake(w http.ResponseWriter, r *http.Request) {
   225  	err := json.NewEncoder(w).Encode(&handshakeResp{
   226  		[]string{"NetworkDriver", "IpamDriver"},
   227  	})
   228  	if err != nil {
   229  		log.WithError(err).Fatal("handshake encode")
   230  		sendError(w, "encode error", http.StatusInternalServerError)
   231  		return
   232  	}
   233  	log.Debug("Handshake completed")
   234  }
   235  
   236  func (driver *driver) capabilities(w http.ResponseWriter, r *http.Request) {
   237  	err := json.NewEncoder(w).Encode(&api.GetCapabilityResponse{
   238  		Scope: "local",
   239  	})
   240  	if err != nil {
   241  		log.WithError(err).Fatal("capabilities encode")
   242  		sendError(w, "encode error", http.StatusInternalServerError)
   243  		return
   244  	}
   245  	log.Debug("NetworkDriver capabilities exchange complete")
   246  }
   247  
   248  func (driver *driver) createNetwork(w http.ResponseWriter, r *http.Request) {
   249  	var create api.CreateNetworkRequest
   250  	err := json.NewDecoder(r.Body).Decode(&create)
   251  	if err != nil {
   252  		sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
   253  		return
   254  	}
   255  	log.WithField(logfields.Request, logfields.Repr(&create)).Debug("Network Create Called")
   256  	emptyResponse(w)
   257  }
   258  
   259  func (driver *driver) deleteNetwork(w http.ResponseWriter, r *http.Request) {
   260  	var delete api.DeleteNetworkRequest
   261  	if err := json.NewDecoder(r.Body).Decode(&delete); err != nil {
   262  		sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
   263  		return
   264  	}
   265  	log.WithField(logfields.Request, logfields.Repr(&delete)).Debug("Delete network request")
   266  	emptyResponse(w)
   267  }
   268  
   269  // CreateEndpointRequest is the as api.CreateEndpointRequest but with
   270  // json.RawMessage type for Options map
   271  type CreateEndpointRequest struct {
   272  	NetworkID  string
   273  	EndpointID string
   274  	Interface  api.EndpointInterface
   275  	Options    map[string]json.RawMessage
   276  }
   277  
   278  func (driver *driver) createEndpoint(w http.ResponseWriter, r *http.Request) {
   279  	var create CreateEndpointRequest
   280  	var err error
   281  	if err := json.NewDecoder(r.Body).Decode(&create); err != nil {
   282  		sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
   283  		return
   284  	}
   285  	log.WithField(logfields.Request, logfields.Repr(&create)).Debug("Create endpoint request")
   286  
   287  	if create.Interface.Address == "" && create.Interface.AddressIPv6 == "" {
   288  		sendError(w, "No IPv4 or IPv6 address provided (required)", http.StatusBadRequest)
   289  		return
   290  	}
   291  
   292  	endpoint := &models.EndpointChangeRequest{
   293  		SyncBuildEndpoint: true,
   294  		State:             models.EndpointStateWaitingForIdentity,
   295  		DockerEndpointID:  create.EndpointID,
   296  		DockerNetworkID:   create.NetworkID,
   297  		Addressing: &models.AddressPair{
   298  			IPV6: create.Interface.AddressIPv6,
   299  			IPV4: create.Interface.Address,
   300  		},
   301  	}
   302  
   303  	removeLinkOnErr := func(link netlink.Link) {
   304  		if err != nil && link != nil && !reflect.ValueOf(link).IsNil() {
   305  			if err := netlink.LinkDel(link); err != nil {
   306  				log.WithError(err).WithFields(logrus.Fields{
   307  					logfields.DatapathMode: driver.conf.DatapathMode,
   308  					logfields.Device:       link.Attrs().Name,
   309  				}).Warn("failed to clean up")
   310  			}
   311  		}
   312  	}
   313  
   314  	switch driver.conf.DatapathMode {
   315  	case option.DatapathModeVeth:
   316  		var veth *netlink.Veth
   317  		veth, _, _, err = connector.SetupVeth(create.EndpointID, int(driver.conf.DeviceMTU), endpoint)
   318  		defer removeLinkOnErr(veth)
   319  	case option.DatapathModeIpvlan:
   320  		var ipvlan *netlink.IPVlan
   321  		ipvlan, _, _, err = connector.CreateIpvlanSlave(
   322  			create.EndpointID, int(driver.conf.DeviceMTU),
   323  			int(driver.conf.IpvlanConfiguration.MasterDeviceIndex),
   324  			driver.conf.IpvlanConfiguration.OperationMode, endpoint,
   325  		)
   326  		defer removeLinkOnErr(ipvlan)
   327  	}
   328  	if err != nil {
   329  		sendError(w,
   330  			fmt.Sprintf("Error while setting up %s mode: %s", driver.conf.DatapathMode, err),
   331  			http.StatusBadRequest)
   332  		return
   333  	}
   334  
   335  	// FIXME: Translate port mappings to RuleL4 policy elements
   336  
   337  	if err = driver.client.EndpointCreate(endpoint); err != nil {
   338  		sendError(w, fmt.Sprintf("Error creating endpoint %s", err), http.StatusBadRequest)
   339  		return
   340  	}
   341  
   342  	log.WithField(logfields.EndpointID, create.EndpointID).Debug("Created new endpoint")
   343  
   344  	respIface := &api.EndpointInterface{
   345  		// Fixme: the lxcmac is an empty string at this point and we only know the
   346  		// mac address at the end of joinEndpoint
   347  		// There's no problem in the setup but docker inspect will show an empty mac address
   348  		MacAddress: "",
   349  	}
   350  
   351  	resp := &api.CreateEndpointResponse{
   352  		Interface: respIface,
   353  	}
   354  	log.WithField(logfields.Response, logfields.Repr(resp)).Debug("Create endpoint response")
   355  	objectResponse(w, resp)
   356  }
   357  
   358  func (driver *driver) deleteEndpoint(w http.ResponseWriter, r *http.Request) {
   359  	var del api.DeleteEndpointRequest
   360  	var ifName string
   361  	if err := json.NewDecoder(r.Body).Decode(&del); err != nil {
   362  		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
   363  		return
   364  	}
   365  	log.WithField(logfields.Request, logfields.Repr(&del)).Debug("Delete endpoint request")
   366  
   367  	switch driver.conf.DatapathMode {
   368  	case option.DatapathModeVeth:
   369  		ifName = connector.Endpoint2IfName(del.EndpointID)
   370  	case option.DatapathModeIpvlan:
   371  		ifName = connector.Endpoint2TempIfName(del.EndpointID)
   372  	}
   373  
   374  	if err := link.DeleteByName(ifName); err != nil {
   375  		log.WithError(err).Warn("Error while deleting link")
   376  	}
   377  
   378  	emptyResponse(w)
   379  }
   380  
   381  func (driver *driver) infoEndpoint(w http.ResponseWriter, r *http.Request) {
   382  	var info api.EndpointInfoRequest
   383  	if err := json.NewDecoder(r.Body).Decode(&info); err != nil {
   384  		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
   385  		return
   386  	}
   387  	log.WithField(logfields.Request, logfields.Repr(&info)).Debug("Endpoint info request")
   388  	objectResponse(w, &api.EndpointInfoResponse{Value: map[string]interface{}{}})
   389  	log.WithField(logfields.Response, info.EndpointID).Debug("Endpoint info")
   390  }
   391  
   392  func (driver *driver) joinEndpoint(w http.ResponseWriter, r *http.Request) {
   393  	var (
   394  		j   api.JoinRequest
   395  		err error
   396  	)
   397  
   398  	driver.mutex.RLock()
   399  	defer driver.mutex.RUnlock()
   400  
   401  	if err := json.NewDecoder(r.Body).Decode(&j); err != nil {
   402  		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
   403  		return
   404  	}
   405  	log.WithField(logfields.Request, logfields.Repr(&j)).Debug("Join request")
   406  
   407  	old, err := driver.client.EndpointGet(endpointID(j.EndpointID))
   408  	if err != nil {
   409  		sendError(w, fmt.Sprintf("Error retrieving endpoint %s", err), http.StatusBadRequest)
   410  		return
   411  	}
   412  
   413  	log.WithField(logfields.Object, old).Debug("Existing endpoint")
   414  
   415  	res := &api.JoinResponse{
   416  		InterfaceName: &api.InterfaceName{
   417  			SrcName:   connector.Endpoint2TempIfName(j.EndpointID),
   418  			DstPrefix: ContainerInterfacePrefix,
   419  		},
   420  		StaticRoutes:          driver.routes,
   421  		DisableGatewayService: true,
   422  		GatewayIPv6:           driver.gatewayIPv6,
   423  		//GatewayIPv4:           driver.gatewayIPv4,
   424  	}
   425  
   426  	// FIXME? Having the following code results on a runtime error: docker: Error
   427  	// response from daemon: oci runtime error: process_linux.go:334: running prestart
   428  	// hook 0 caused "exit status 1: time=\"2016-10-26T06:33:17-07:00\" level=fatal
   429  	// msg=\"failed to set gateway while updating gateway: file exists\" \n"
   430  	//
   431  	// If empty, it works as expected without docker runtime errors
   432  	// res.Gateway = connector.IPv4Gateway(addr)
   433  
   434  	log.WithField(logfields.Response, logfields.Repr(res)).Debug("Join response")
   435  	objectResponse(w, res)
   436  }
   437  
   438  func (driver *driver) leaveEndpoint(w http.ResponseWriter, r *http.Request) {
   439  	var l api.LeaveRequest
   440  	if err := json.NewDecoder(r.Body).Decode(&l); err != nil {
   441  		sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
   442  		return
   443  	}
   444  	log.WithField(logfields.Request, logfields.Repr(&l)).Debug("Leave request")
   445  
   446  	if err := driver.client.EndpointDelete(endpointID(l.EndpointID)); err != nil {
   447  		log.WithError(err).Warn("Leaving the endpoint failed")
   448  	}
   449  
   450  	emptyResponse(w)
   451  }