| // +build windows |
| |
| // Shim for the Host Network Service (HNS) to manage networking for |
| // Windows Server containers and Hyper-V containers. This module |
| // is a basic libnetwork driver that passes all the calls to HNS |
| // It implements the 4 networking modes supported by HNS L2Bridge, |
| // L2Tunnel, NAT and Transparent(DHCP) |
| // |
| // The network are stored in memory and docker daemon ensures discovering |
| // and loading these networks on startup |
| |
| package windows |
| |
| import ( |
| "encoding/json" |
| "fmt" |
| "net" |
| "strconv" |
| "strings" |
| "sync" |
| |
| "github.com/Microsoft/hcsshim" |
| "github.com/docker/docker/pkg/system" |
| "github.com/docker/libnetwork/datastore" |
| "github.com/docker/libnetwork/discoverapi" |
| "github.com/docker/libnetwork/driverapi" |
| "github.com/docker/libnetwork/netlabel" |
| "github.com/docker/libnetwork/portmapper" |
| "github.com/docker/libnetwork/types" |
| "github.com/sirupsen/logrus" |
| ) |
| |
| // networkConfiguration for network specific configuration |
| type networkConfiguration struct { |
| ID string |
| Type string |
| Name string |
| HnsID string |
| RDID string |
| VLAN uint |
| VSID uint |
| DNSServers string |
| MacPools []hcsshim.MacPool |
| DNSSuffix string |
| SourceMac string |
| NetworkAdapterName string |
| dbIndex uint64 |
| dbExists bool |
| DisableGatewayDNS bool |
| EnableOutboundNat bool |
| OutboundNatExceptions []string |
| } |
| |
| // endpointConfiguration represents the user specified configuration for the sandbox endpoint |
| type endpointOption struct { |
| MacAddress net.HardwareAddr |
| QosPolicies []types.QosPolicy |
| DNSServers []string |
| DisableDNS bool |
| DisableICC bool |
| } |
| |
| // EndpointConnectivity stores the port bindings and exposed ports that the user has specified in epOptions. |
| type EndpointConnectivity struct { |
| PortBindings []types.PortBinding |
| ExposedPorts []types.TransportPort |
| } |
| |
| type hnsEndpoint struct { |
| id string |
| nid string |
| profileID string |
| Type string |
| //Note: Currently, the sandboxID is the same as the containerID since windows does |
| //not expose the sandboxID. |
| //In the future, windows will support a proper sandboxID that is different |
| //than the containerID. |
| //Therefore, we are using sandboxID now, so that we won't have to change this code |
| //when windows properly supports a sandboxID. |
| sandboxID string |
| macAddress net.HardwareAddr |
| epOption *endpointOption // User specified parameters |
| epConnectivity *EndpointConnectivity // User specified parameters |
| portMapping []types.PortBinding // Operation port bindings |
| addr *net.IPNet |
| gateway net.IP |
| dbIndex uint64 |
| dbExists bool |
| } |
| |
| type hnsNetwork struct { |
| id string |
| created bool |
| config *networkConfiguration |
| endpoints map[string]*hnsEndpoint // key: endpoint id |
| driver *driver // The network's driver |
| portMapper *portmapper.PortMapper |
| sync.Mutex |
| } |
| |
| type driver struct { |
| name string |
| networks map[string]*hnsNetwork |
| store datastore.DataStore |
| sync.Mutex |
| } |
| |
| const ( |
| errNotFound = "HNS failed with error : The object identifier does not represent a valid object. " |
| ) |
| |
| // IsBuiltinLocalDriver validates if network-type is a builtin local-scoped driver |
| func IsBuiltinLocalDriver(networkType string) bool { |
| if "l2bridge" == networkType || "l2tunnel" == networkType || "nat" == networkType || "ics" == networkType || "transparent" == networkType { |
| return true |
| } |
| |
| return false |
| } |
| |
| // New constructs a new bridge driver |
| func newDriver(networkType string) *driver { |
| return &driver{name: networkType, networks: map[string]*hnsNetwork{}} |
| } |
| |
| // GetInit returns an initializer for the given network type |
| func GetInit(networkType string) func(dc driverapi.DriverCallback, config map[string]interface{}) error { |
| return func(dc driverapi.DriverCallback, config map[string]interface{}) error { |
| if !IsBuiltinLocalDriver(networkType) { |
| return types.BadRequestErrorf("Network type not supported: %s", networkType) |
| } |
| |
| d := newDriver(networkType) |
| |
| err := d.initStore(config) |
| if err != nil { |
| return err |
| } |
| |
| return dc.RegisterDriver(networkType, d, driverapi.Capability{ |
| DataScope: datastore.LocalScope, |
| ConnectivityScope: datastore.LocalScope, |
| }) |
| } |
| } |
| |
| func (d *driver) getNetwork(id string) (*hnsNetwork, error) { |
| d.Lock() |
| defer d.Unlock() |
| |
| if nw, ok := d.networks[id]; ok { |
| return nw, nil |
| } |
| |
| return nil, types.NotFoundErrorf("network not found: %s", id) |
| } |
| |
| func (n *hnsNetwork) getEndpoint(eid string) (*hnsEndpoint, error) { |
| n.Lock() |
| defer n.Unlock() |
| |
| if ep, ok := n.endpoints[eid]; ok { |
| return ep, nil |
| } |
| |
| return nil, types.NotFoundErrorf("Endpoint not found: %s", eid) |
| } |
| |
| func (d *driver) parseNetworkOptions(id string, genericOptions map[string]string) (*networkConfiguration, error) { |
| config := &networkConfiguration{Type: d.name} |
| |
| for label, value := range genericOptions { |
| switch label { |
| case NetworkName: |
| config.Name = value |
| case HNSID: |
| config.HnsID = value |
| case RoutingDomain: |
| config.RDID = value |
| case Interface: |
| config.NetworkAdapterName = value |
| case DNSSuffix: |
| config.DNSSuffix = value |
| case DNSServers: |
| config.DNSServers = value |
| case DisableGatewayDNS: |
| b, err := strconv.ParseBool(value) |
| if err != nil { |
| return nil, err |
| } |
| config.DisableGatewayDNS = b |
| case MacPool: |
| config.MacPools = make([]hcsshim.MacPool, 0) |
| s := strings.Split(value, ",") |
| if len(s)%2 != 0 { |
| return nil, types.BadRequestErrorf("Invalid mac pool. You must specify both a start range and an end range") |
| } |
| for i := 0; i < len(s)-1; i += 2 { |
| config.MacPools = append(config.MacPools, hcsshim.MacPool{ |
| StartMacAddress: s[i], |
| EndMacAddress: s[i+1], |
| }) |
| } |
| case VLAN: |
| vlan, err := strconv.ParseUint(value, 10, 32) |
| if err != nil { |
| return nil, err |
| } |
| config.VLAN = uint(vlan) |
| case VSID: |
| vsid, err := strconv.ParseUint(value, 10, 32) |
| if err != nil { |
| return nil, err |
| } |
| config.VSID = uint(vsid) |
| case EnableOutboundNat: |
| if system.GetOSVersion().Build <= 16236 { |
| return nil, fmt.Errorf("Invalid network option. OutboundNat is not supported on this OS version") |
| } |
| b, err := strconv.ParseBool(value) |
| if err != nil { |
| return nil, err |
| } |
| config.EnableOutboundNat = b |
| case OutboundNatExceptions: |
| s := strings.Split(value, ",") |
| config.OutboundNatExceptions = s |
| } |
| } |
| |
| config.ID = id |
| config.Type = d.name |
| return config, nil |
| } |
| |
| func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error { |
| if len(ipamV6Data) > 0 { |
| return types.ForbiddenErrorf("windowsshim driver doesn't support v6 subnets") |
| } |
| |
| if len(ipamV4Data) == 0 { |
| return types.BadRequestErrorf("network %s requires ipv4 configuration", id) |
| } |
| |
| return nil |
| } |
| |
| func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) { |
| } |
| |
| func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) { |
| return "", nil |
| } |
| |
| func (d *driver) createNetwork(config *networkConfiguration) error { |
| network := &hnsNetwork{ |
| id: config.ID, |
| endpoints: make(map[string]*hnsEndpoint), |
| config: config, |
| driver: d, |
| portMapper: portmapper.New(""), |
| } |
| |
| d.Lock() |
| d.networks[config.ID] = network |
| d.Unlock() |
| |
| return nil |
| } |
| |
| // Create a new network |
| func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error { |
| if _, err := d.getNetwork(id); err == nil { |
| return types.ForbiddenErrorf("network %s exists", id) |
| } |
| |
| genData, ok := option[netlabel.GenericData].(map[string]string) |
| if !ok { |
| return fmt.Errorf("Unknown generic data option") |
| } |
| |
| // Parse and validate the config. It should not conflict with existing networks' config |
| config, err := d.parseNetworkOptions(id, genData) |
| if err != nil { |
| return err |
| } |
| |
| err = config.processIPAM(id, ipV4Data, ipV6Data) |
| if err != nil { |
| return err |
| } |
| |
| err = d.createNetwork(config) |
| |
| if err != nil { |
| return err |
| } |
| |
| // A non blank hnsid indicates that the network was discovered |
| // from HNS. No need to call HNS if this network was discovered |
| // from HNS |
| if config.HnsID == "" { |
| subnets := []hcsshim.Subnet{} |
| |
| for _, ipData := range ipV4Data { |
| subnet := hcsshim.Subnet{ |
| AddressPrefix: ipData.Pool.String(), |
| } |
| |
| if ipData.Gateway != nil { |
| subnet.GatewayAddress = ipData.Gateway.IP.String() |
| } |
| |
| subnets = append(subnets, subnet) |
| } |
| |
| network := &hcsshim.HNSNetwork{ |
| Name: config.Name, |
| Type: d.name, |
| Subnets: subnets, |
| DNSServerList: config.DNSServers, |
| DNSSuffix: config.DNSSuffix, |
| MacPools: config.MacPools, |
| SourceMac: config.SourceMac, |
| NetworkAdapterName: config.NetworkAdapterName, |
| } |
| |
| if config.VLAN != 0 { |
| vlanPolicy, err := json.Marshal(hcsshim.VlanPolicy{ |
| Type: "VLAN", |
| VLAN: config.VLAN, |
| }) |
| |
| if err != nil { |
| return err |
| } |
| network.Policies = append(network.Policies, vlanPolicy) |
| } |
| |
| if config.VSID != 0 { |
| vsidPolicy, err := json.Marshal(hcsshim.VsidPolicy{ |
| Type: "VSID", |
| VSID: config.VSID, |
| }) |
| |
| if err != nil { |
| return err |
| } |
| network.Policies = append(network.Policies, vsidPolicy) |
| } |
| |
| if network.Name == "" { |
| network.Name = id |
| } |
| |
| configurationb, err := json.Marshal(network) |
| if err != nil { |
| return err |
| } |
| |
| configuration := string(configurationb) |
| logrus.Debugf("HNSNetwork Request =%v Address Space=%v", configuration, subnets) |
| |
| hnsresponse, err := hcsshim.HNSNetworkRequest("POST", "", configuration) |
| if err != nil { |
| return err |
| } |
| |
| config.HnsID = hnsresponse.Id |
| genData[HNSID] = config.HnsID |
| |
| } else { |
| // Delete any stale HNS endpoints for this network. |
| if endpoints, err := hcsshim.HNSListEndpointRequest(); err == nil { |
| for _, ep := range endpoints { |
| if ep.VirtualNetwork == config.HnsID { |
| logrus.Infof("Removing stale HNS endpoint %s", ep.Id) |
| _, err = hcsshim.HNSEndpointRequest("DELETE", ep.Id, "") |
| if err != nil { |
| logrus.Warnf("Error removing HNS endpoint %s", ep.Id) |
| } |
| } |
| } |
| } else { |
| logrus.Warnf("Error listing HNS endpoints for network %s", config.HnsID) |
| } |
| } |
| |
| n, err := d.getNetwork(id) |
| if err != nil { |
| return err |
| } |
| n.created = true |
| return d.storeUpdate(config) |
| } |
| |
| func (d *driver) DeleteNetwork(nid string) error { |
| n, err := d.getNetwork(nid) |
| if err != nil { |
| return types.InternalMaskableErrorf("%s", err) |
| } |
| |
| n.Lock() |
| config := n.config |
| n.Unlock() |
| |
| if n.created { |
| _, err = hcsshim.HNSNetworkRequest("DELETE", config.HnsID, "") |
| if err != nil && err.Error() != errNotFound { |
| return types.ForbiddenErrorf(err.Error()) |
| } |
| } |
| |
| d.Lock() |
| delete(d.networks, nid) |
| d.Unlock() |
| |
| // delele endpoints belong to this network |
| for _, ep := range n.endpoints { |
| if err := d.storeDelete(ep); err != nil { |
| logrus.Warnf("Failed to remove bridge endpoint %.7s from store: %v", ep.id, err) |
| } |
| } |
| |
| return d.storeDelete(config) |
| } |
| |
| func convertQosPolicies(qosPolicies []types.QosPolicy) ([]json.RawMessage, error) { |
| var qps []json.RawMessage |
| |
| // Enumerate through the qos policies specified by the user and convert |
| // them into the internal structure matching the JSON blob that can be |
| // understood by the HCS. |
| for _, elem := range qosPolicies { |
| encodedPolicy, err := json.Marshal(hcsshim.QosPolicy{ |
| Type: "QOS", |
| MaximumOutgoingBandwidthInBytes: elem.MaxEgressBandwidth, |
| }) |
| |
| if err != nil { |
| return nil, err |
| } |
| qps = append(qps, encodedPolicy) |
| } |
| return qps, nil |
| } |
| |
| // ConvertPortBindings converts PortBindings to JSON for HNS request |
| func ConvertPortBindings(portBindings []types.PortBinding) ([]json.RawMessage, error) { |
| var pbs []json.RawMessage |
| |
| // Enumerate through the port bindings specified by the user and convert |
| // them into the internal structure matching the JSON blob that can be |
| // understood by the HCS. |
| for _, elem := range portBindings { |
| proto := strings.ToUpper(elem.Proto.String()) |
| if proto != "TCP" && proto != "UDP" { |
| return nil, fmt.Errorf("invalid protocol %s", elem.Proto.String()) |
| } |
| |
| if elem.HostPort != elem.HostPortEnd { |
| return nil, fmt.Errorf("Windows does not support more than one host port in NAT settings") |
| } |
| |
| if len(elem.HostIP) != 0 { |
| return nil, fmt.Errorf("Windows does not support host IP addresses in NAT settings") |
| } |
| |
| encodedPolicy, err := json.Marshal(hcsshim.NatPolicy{ |
| Type: "NAT", |
| ExternalPort: elem.HostPort, |
| InternalPort: elem.Port, |
| Protocol: elem.Proto.String(), |
| }) |
| |
| if err != nil { |
| return nil, err |
| } |
| pbs = append(pbs, encodedPolicy) |
| } |
| return pbs, nil |
| } |
| |
| // ParsePortBindingPolicies parses HNS endpoint response message to PortBindings |
| func ParsePortBindingPolicies(policies []json.RawMessage) ([]types.PortBinding, error) { |
| var bindings []types.PortBinding |
| hcsPolicy := &hcsshim.NatPolicy{} |
| |
| for _, elem := range policies { |
| |
| if err := json.Unmarshal([]byte(elem), &hcsPolicy); err != nil || hcsPolicy.Type != "NAT" { |
| continue |
| } |
| |
| binding := types.PortBinding{ |
| HostPort: hcsPolicy.ExternalPort, |
| HostPortEnd: hcsPolicy.ExternalPort, |
| Port: hcsPolicy.InternalPort, |
| Proto: types.ParseProtocol(hcsPolicy.Protocol), |
| HostIP: net.IPv4(0, 0, 0, 0), |
| } |
| |
| bindings = append(bindings, binding) |
| } |
| |
| return bindings, nil |
| } |
| |
| func parseEndpointOptions(epOptions map[string]interface{}) (*endpointOption, error) { |
| if epOptions == nil { |
| return nil, nil |
| } |
| |
| ec := &endpointOption{} |
| |
| if opt, ok := epOptions[netlabel.MacAddress]; ok { |
| if mac, ok := opt.(net.HardwareAddr); ok { |
| ec.MacAddress = mac |
| } else { |
| return nil, fmt.Errorf("Invalid endpoint configuration") |
| } |
| } |
| |
| if opt, ok := epOptions[QosPolicies]; ok { |
| if policies, ok := opt.([]types.QosPolicy); ok { |
| ec.QosPolicies = policies |
| } else { |
| return nil, fmt.Errorf("Invalid endpoint configuration") |
| } |
| } |
| |
| if opt, ok := epOptions[netlabel.DNSServers]; ok { |
| if dns, ok := opt.([]string); ok { |
| ec.DNSServers = dns |
| } else { |
| return nil, fmt.Errorf("Invalid endpoint configuration") |
| } |
| } |
| |
| if opt, ok := epOptions[DisableICC]; ok { |
| if disableICC, ok := opt.(bool); ok { |
| ec.DisableICC = disableICC |
| } else { |
| return nil, fmt.Errorf("Invalid endpoint configuration") |
| } |
| } |
| |
| if opt, ok := epOptions[DisableDNS]; ok { |
| if disableDNS, ok := opt.(bool); ok { |
| ec.DisableDNS = disableDNS |
| } else { |
| return nil, fmt.Errorf("Invalid endpoint configuration") |
| } |
| } |
| |
| return ec, nil |
| } |
| |
| // ParseEndpointConnectivity parses options passed to CreateEndpoint, specifically port bindings, and store in a endpointConnectivity object. |
| func ParseEndpointConnectivity(epOptions map[string]interface{}) (*EndpointConnectivity, error) { |
| if epOptions == nil { |
| return nil, nil |
| } |
| |
| ec := &EndpointConnectivity{} |
| |
| if opt, ok := epOptions[netlabel.PortMap]; ok { |
| if bs, ok := opt.([]types.PortBinding); ok { |
| ec.PortBindings = bs |
| } else { |
| return nil, fmt.Errorf("Invalid endpoint configuration") |
| } |
| } |
| |
| if opt, ok := epOptions[netlabel.ExposedPorts]; ok { |
| if ports, ok := opt.([]types.TransportPort); ok { |
| ec.ExposedPorts = ports |
| } else { |
| return nil, fmt.Errorf("Invalid endpoint configuration") |
| } |
| } |
| return ec, nil |
| } |
| |
| func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { |
| n, err := d.getNetwork(nid) |
| if err != nil { |
| return err |
| } |
| |
| // Check if endpoint id is good and retrieve corresponding endpoint |
| ep, err := n.getEndpoint(eid) |
| if err == nil && ep != nil { |
| return driverapi.ErrEndpointExists(eid) |
| } |
| |
| endpointStruct := &hcsshim.HNSEndpoint{ |
| VirtualNetwork: n.config.HnsID, |
| } |
| |
| epOption, err := parseEndpointOptions(epOptions) |
| if err != nil { |
| return err |
| } |
| epConnectivity, err := ParseEndpointConnectivity(epOptions) |
| if err != nil { |
| return err |
| } |
| |
| macAddress := ifInfo.MacAddress() |
| // Use the macaddress if it was provided |
| if macAddress != nil { |
| endpointStruct.MacAddress = strings.Replace(macAddress.String(), ":", "-", -1) |
| } |
| |
| portMapping := epConnectivity.PortBindings |
| |
| if n.config.Type == "l2bridge" || n.config.Type == "l2tunnel" { |
| ip := net.IPv4(0, 0, 0, 0) |
| if ifInfo.Address() != nil { |
| ip = ifInfo.Address().IP |
| } |
| |
| portMapping, err = AllocatePorts(n.portMapper, portMapping, ip) |
| if err != nil { |
| return err |
| } |
| |
| defer func() { |
| if err != nil { |
| ReleasePorts(n.portMapper, portMapping) |
| } |
| }() |
| } |
| |
| endpointStruct.Policies, err = ConvertPortBindings(portMapping) |
| if err != nil { |
| return err |
| } |
| |
| qosPolicies, err := convertQosPolicies(epOption.QosPolicies) |
| if err != nil { |
| return err |
| } |
| endpointStruct.Policies = append(endpointStruct.Policies, qosPolicies...) |
| |
| if ifInfo.Address() != nil { |
| endpointStruct.IPAddress = ifInfo.Address().IP |
| } |
| |
| endpointStruct.DNSServerList = strings.Join(epOption.DNSServers, ",") |
| |
| // overwrite the ep DisableDNS option if DisableGatewayDNS was set to true during the network creation option |
| if n.config.DisableGatewayDNS { |
| logrus.Debugf("n.config.DisableGatewayDNS[%v] overwrites epOption.DisableDNS[%v]", n.config.DisableGatewayDNS, epOption.DisableDNS) |
| epOption.DisableDNS = n.config.DisableGatewayDNS |
| } |
| |
| if n.driver.name == "nat" && !epOption.DisableDNS { |
| logrus.Debugf("endpointStruct.EnableInternalDNS =[%v]", endpointStruct.EnableInternalDNS) |
| endpointStruct.EnableInternalDNS = true |
| } |
| |
| endpointStruct.DisableICC = epOption.DisableICC |
| |
| // Inherit OutboundNat policy from the network |
| if n.config.EnableOutboundNat { |
| outboundNatPolicy, err := json.Marshal(hcsshim.OutboundNatPolicy{ |
| Policy: hcsshim.Policy{Type: hcsshim.OutboundNat}, |
| Exceptions: n.config.OutboundNatExceptions, |
| }) |
| |
| if err != nil { |
| return err |
| } |
| endpointStruct.Policies = append(endpointStruct.Policies, outboundNatPolicy) |
| } |
| |
| configurationb, err := json.Marshal(endpointStruct) |
| if err != nil { |
| return err |
| } |
| |
| hnsresponse, err := hcsshim.HNSEndpointRequest("POST", "", string(configurationb)) |
| if err != nil { |
| return err |
| } |
| |
| mac, err := net.ParseMAC(hnsresponse.MacAddress) |
| if err != nil { |
| return err |
| } |
| |
| // TODO For now the ip mask is not in the info generated by HNS |
| endpoint := &hnsEndpoint{ |
| id: eid, |
| nid: n.id, |
| Type: d.name, |
| addr: &net.IPNet{IP: hnsresponse.IPAddress, Mask: hnsresponse.IPAddress.DefaultMask()}, |
| macAddress: mac, |
| } |
| |
| if hnsresponse.GatewayAddress != "" { |
| endpoint.gateway = net.ParseIP(hnsresponse.GatewayAddress) |
| } |
| |
| endpoint.profileID = hnsresponse.Id |
| endpoint.epConnectivity = epConnectivity |
| endpoint.epOption = epOption |
| endpoint.portMapping, err = ParsePortBindingPolicies(hnsresponse.Policies) |
| |
| if err != nil { |
| hcsshim.HNSEndpointRequest("DELETE", hnsresponse.Id, "") |
| return err |
| } |
| |
| n.Lock() |
| n.endpoints[eid] = endpoint |
| n.Unlock() |
| |
| if ifInfo.Address() == nil { |
| ifInfo.SetIPAddress(endpoint.addr) |
| } |
| |
| if macAddress == nil { |
| ifInfo.SetMacAddress(endpoint.macAddress) |
| } |
| |
| if err = d.storeUpdate(endpoint); err != nil { |
| logrus.Errorf("Failed to save endpoint %.7s to store: %v", endpoint.id, err) |
| } |
| |
| return nil |
| } |
| |
| func (d *driver) DeleteEndpoint(nid, eid string) error { |
| n, err := d.getNetwork(nid) |
| if err != nil { |
| return types.InternalMaskableErrorf("%s", err) |
| } |
| |
| ep, err := n.getEndpoint(eid) |
| if err != nil { |
| return err |
| } |
| |
| if n.config.Type == "l2bridge" || n.config.Type == "l2tunnel" { |
| ReleasePorts(n.portMapper, ep.portMapping) |
| } |
| |
| n.Lock() |
| delete(n.endpoints, eid) |
| n.Unlock() |
| |
| _, err = hcsshim.HNSEndpointRequest("DELETE", ep.profileID, "") |
| if err != nil && err.Error() != errNotFound { |
| return err |
| } |
| |
| if err := d.storeDelete(ep); err != nil { |
| logrus.Warnf("Failed to remove bridge endpoint %.7s from store: %v", ep.id, err) |
| } |
| return nil |
| } |
| |
| func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { |
| network, err := d.getNetwork(nid) |
| if err != nil { |
| return nil, err |
| } |
| |
| ep, err := network.getEndpoint(eid) |
| if err != nil { |
| return nil, err |
| } |
| |
| data := make(map[string]interface{}, 1) |
| if network.driver.name == "nat" { |
| data["AllowUnqualifiedDNSQuery"] = true |
| } |
| |
| data["hnsid"] = ep.profileID |
| if ep.epConnectivity.ExposedPorts != nil { |
| // Return a copy of the config data |
| epc := make([]types.TransportPort, 0, len(ep.epConnectivity.ExposedPorts)) |
| for _, tp := range ep.epConnectivity.ExposedPorts { |
| epc = append(epc, tp.GetCopy()) |
| } |
| data[netlabel.ExposedPorts] = epc |
| } |
| |
| if ep.portMapping != nil { |
| // Return a copy of the operational data |
| pmc := make([]types.PortBinding, 0, len(ep.portMapping)) |
| for _, pm := range ep.portMapping { |
| pmc = append(pmc, pm.GetCopy()) |
| } |
| data[netlabel.PortMap] = pmc |
| } |
| |
| if len(ep.macAddress) != 0 { |
| data[netlabel.MacAddress] = ep.macAddress |
| } |
| return data, nil |
| } |
| |
| // Join method is invoked when a Sandbox is attached to an endpoint. |
| func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { |
| network, err := d.getNetwork(nid) |
| if err != nil { |
| return err |
| } |
| |
| // Ensure that the endpoint exists |
| endpoint, err := network.getEndpoint(eid) |
| if err != nil { |
| return err |
| } |
| |
| err = jinfo.SetGateway(endpoint.gateway) |
| if err != nil { |
| return err |
| } |
| |
| endpoint.sandboxID = sboxKey |
| |
| err = hcsshim.HotAttachEndpoint(endpoint.sandboxID, endpoint.profileID) |
| if err != nil { |
| // If container doesn't exists in hcs, do not throw error for hot add/remove |
| if err != hcsshim.ErrComputeSystemDoesNotExist { |
| return err |
| } |
| } |
| |
| jinfo.DisableGatewayService() |
| return nil |
| } |
| |
| // Leave method is invoked when a Sandbox detaches from an endpoint. |
| func (d *driver) Leave(nid, eid string) error { |
| network, err := d.getNetwork(nid) |
| if err != nil { |
| return types.InternalMaskableErrorf("%s", err) |
| } |
| |
| // Ensure that the endpoint exists |
| endpoint, err := network.getEndpoint(eid) |
| if err != nil { |
| return err |
| } |
| |
| err = hcsshim.HotDetachEndpoint(endpoint.sandboxID, endpoint.profileID) |
| if err != nil { |
| // If container doesn't exists in hcs, do not throw error for hot add/remove |
| if err != hcsshim.ErrComputeSystemDoesNotExist { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { |
| return nil |
| } |
| |
| func (d *driver) RevokeExternalConnectivity(nid, eid string) error { |
| return nil |
| } |
| |
| func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) { |
| return nil, types.NotImplementedErrorf("not implemented") |
| } |
| |
| func (d *driver) NetworkFree(id string) error { |
| return types.NotImplementedErrorf("not implemented") |
| } |
| |
| func (d *driver) Type() string { |
| return d.name |
| } |
| |
| func (d *driver) IsBuiltIn() bool { |
| return true |
| } |
| |
| // DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster |
| func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { |
| return nil |
| } |
| |
| // DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster |
| func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { |
| return nil |
| } |