diff --git a/Gopkg.lock b/Gopkg.lock index 6c98681..8acd539 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -66,6 +66,12 @@ packages = ["."] revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998" +[[projects]] + name = "github.com/golang/protobuf" + packages = ["proto"] + revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" + version = "v1.1.0" + [[projects]] name = "github.com/onsi/ginkgo" packages = [".","config","extensions/table","internal/codelocation","internal/containernode","internal/failer","internal/leafnodes","internal/remote","internal/spec","internal/spec_iterator","internal/specrunner","internal/suite","internal/testingtproxy","internal/writer","reporters","reporters/stenographer","reporters/stenographer/support/go-colorable","reporters/stenographer/support/go-isatty","types"] @@ -73,7 +79,7 @@ [[projects]] name = "github.com/onsi/gomega" - packages = [".","format","internal/assertion","internal/asyncassertion","internal/oraclematcher","internal/testingtsupport","matchers","matchers/support/goraph/bipartitegraph","matchers/support/goraph/edge","matchers/support/goraph/node","matchers/support/goraph/util","types"] + packages = [".","format","ghttp","internal/assertion","internal/asyncassertion","internal/oraclematcher","internal/testingtsupport","matchers","matchers/support/goraph/bipartitegraph","matchers/support/goraph/edge","matchers/support/goraph/node","matchers/support/goraph/util","types"] revision = "dcabb60a477c2b6f456df65037cb6708210fbb02" [[projects]] @@ -136,6 +142,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "2d6b89a6a393406e7aaa0091c8c8a8b4b420db3c201f6ba9ab2dde3458d9fc23" + inputs-digest = "d0da86fa845b1434cf4ebc19721b1cedf2eee41964e5e0bcf002c5483a83bc46" solver-name = "gps-cdcl" solver-version = 1 diff --git a/adapters/primary/docker_libnetwork_plugin/plugin.go b/adapters/primary/docker_libnetwork_plugin/plugin.go index 59eb9fa..379fec6 100644 --- a/adapters/primary/docker_libnetwork_plugin/plugin.go +++ b/adapters/primary/docker_libnetwork_plugin/plugin.go @@ -32,7 +32,6 @@ import ( "github.com/Juniper/contrail-windows-docker-driver/common" "github.com/Juniper/contrail-windows-docker-driver/core/driver_core" winio "github.com/Microsoft/go-winio" - "github.com/Microsoft/hcsshim" dockerTypes "github.com/docker/docker/api/types" dockerClient "github.com/docker/docker/client" "github.com/docker/go-connections/sockets" @@ -41,8 +40,6 @@ import ( log "github.com/sirupsen/logrus" ) -const hnsEndpointWaitingTime = 5 - type DockerPluginServer struct { // TODO: for now, Core field is public, because we need to access its fields, like controller. // This should be made private when making the Controller port smaller. @@ -224,12 +221,12 @@ func (d *DockerPluginServer) CreateNetwork(req *network.CreateNetworkRequest) er if len(req.IPv4Data) == 0 { return errors.New("Docker subnet IPv4 data missing") } - ipPool := req.IPv4Data[0].Pool + subnetCIDR := req.IPv4Data[0].Pool tenantName := tenant.(string) networkName := netName.(string) - return d.Core.CreateNetwork(tenantName, networkName, ipPool) + return d.Core.CreateNetwork(tenantName, networkName, subnetCIDR) } func (d *DockerPluginServer) AllocateNetwork(req *network.AllocateNetworkRequest) ( @@ -312,102 +309,16 @@ func (d *DockerPluginServer) CreateEndpoint(req *network.CreateEndpointRequest) return nil, err } - contrailNetwork, err := d.Core.Controller.GetNetwork(meta.tenant, meta.network) - if err != nil { - return nil, err - } - log.Infoln("Retrieved Contrail network:", contrailNetwork.GetUuid()) - - // TODO JW-187. - // We need to retreive Container ID here and use it instead of EndpointID as - // argument to GetOrCreateInstance(). - // EndpointID is equiv to interface, but in Contrail, we have a "VirtualMachine" in - // data model. - // A single VM can be connected to two or more overlay networks, but when we use - // EndpointID, this won't work. - // We need something like: - // containerID := req.Options["vmname"] - containerID := req.EndpointID - - contrailIpam, err := d.Core.Controller.GetIpamSubnet(contrailNetwork, meta.subnetCIDR) - if err != nil { - return nil, err - } - contrailSubnetCIDR := d.Core.GetContrailSubnetCIDR(contrailIpam) - - contrailVif, err := d.Core.Controller.GetOrCreateInterface(contrailNetwork, meta.tenant, - containerID) - if err != nil { - return nil, err - } - - contrailVM, err := d.Core.Controller.GetOrCreateInstance(contrailVif, containerID) - if err != nil { - return nil, err - } - - contrailIP, err := d.Core.Controller.GetOrCreateInstanceIp(contrailNetwork, contrailVif, contrailIpam.SubnetUuid) + container, err := d.Core.CreateEndpoint(meta.tenant, meta.network, meta.subnetCIDR, req.EndpointID) if err != nil { return nil, err } - instanceIP := contrailIP.GetInstanceIpAddress() - log.Infoln("Retrieved instance IP:", instanceIP) - contrailGateway := contrailIpam.DefaultGateway - log.Infoln("Retrieved GW address:", contrailGateway) - if contrailGateway == "" { - return nil, errors.New("Default GW is empty") - } - - contrailMac, err := d.Core.Controller.GetInterfaceMac(contrailVif) - log.Infoln("Retrieved MAC:", contrailMac) - if err != nil { - return nil, err - } - // contrail MACs are like 11:22:aa:bb:cc:dd - // HNS needs MACs like 11-22-AA-BB-CC-DD - formattedMac := strings.Replace(strings.ToUpper(contrailMac), ":", "-", -1) - - hnsNet, err := d.Core.LocalContrailNetworksRepo.GetNetwork(meta.tenant, meta.network, contrailSubnetCIDR) - if err != nil { - return nil, err - } - - hnsEndpointConfig := &hcsshim.HNSEndpoint{ - VirtualNetworkName: hnsNet.Name, - Name: req.EndpointID, - IPAddress: net.ParseIP(instanceIP), - MacAddress: formattedMac, - GatewayAddress: contrailGateway, - } - - hnsEndpointID, err := d.Core.LocalContrailEndpointsRepo.CreateEndpoint(hnsEndpointConfig) - if err != nil { - return nil, err - } - - // TODO: test this when Agent is ready - ifName := d.generateFriendlyName(hnsEndpointID) - - go func() { - // Temporary workaround for HNS issue. - // Due to the bug in Microsoft HNS, Docker Driver has to wait for some time until endpoint - // is ready to handle ARP requests. Unfortunately it seems that HNS doesn't have api - // to verify if endpoint setup is done. - - time.Sleep(hnsEndpointWaitingTime * time.Second) - err := d.Core.Agent.AddPort(contrailVM.GetUuid(), contrailVif.GetUuid(), ifName, contrailMac, containerID, - contrailIP.GetInstanceIpAddress(), contrailNetwork.GetUuid()) - if err != nil { - log.Error(err.Error()) - } - }() - - epAddressCIDR := fmt.Sprintf("%s/%v", instanceIP, contrailIpam.Subnet.IpPrefixLen) + ipCIDR := fmt.Sprintf("%s/%v", container.IP, container.PrefixLen) r := &network.CreateEndpointResponse{ Interface: &network.EndpointInterface{ - Address: epAddressCIDR, - MacAddress: contrailMac, + Address: ipCIDR, + MacAddress: container.Mac, }, } return r, nil @@ -442,6 +353,7 @@ func (d *DockerPluginServer) DeleteEndpoint(req *network.DeleteEndpointRequest) // Temporary workaround for HNS issue, see 'CreateEndpoint' method. // This sleep is added to ensure that DeletePort request is called after AddPort. // Value of waiting time has to be equal or greater than the one in 'CreateEndpoint'. + const hnsEndpointWaitingTime = 5 time.Sleep(hnsEndpointWaitingTime * time.Second) err := d.Core.Agent.DeletePort(contrailVif.GetUuid()) if err != nil { @@ -701,19 +613,3 @@ func (d *DockerPluginServer) hnsNetworksMeta() ([]NetworkMeta, error) { } return meta, nil } - -func (d *DockerPluginServer) generateFriendlyName(hnsEndpointID string) string { - // Here's how the Forwarding Extension (kernel) can identify interfaces based on their - // friendly names. - // Windows Containers have NIC names like "NIC ID abcdef", where abcdef are the first 6 chars - // of their HNS endpoint ID. - // Hyper-V Containers have NIC names consisting of two uuids, probably representing utitlity - // VM's interface and endpoint's interface: - // "227301f6-bee9-4ae2-8a93-5e900cde3f47--910c5490-bff8-45e3-a2a0-0114ed9903e0" - // The second UUID (after the "--") is the HNS endpoints ID. - - // For now, we will always send the name in the Windows Containers format, because it probably - // has enough information to recognize it in kernel (6 first chars of UUID should be enough): - containerNicID := strings.Split(hnsEndpointID, "-")[0] - return fmt.Sprintf("Container NIC %s", containerNicID) -} diff --git a/adapters/primary/docker_libnetwork_plugin/plugin_test.go b/adapters/primary/docker_libnetwork_plugin/plugin_test.go index e62b5b9..abb4edd 100644 --- a/adapters/primary/docker_libnetwork_plugin/plugin_test.go +++ b/adapters/primary/docker_libnetwork_plugin/plugin_test.go @@ -824,7 +824,9 @@ func newIntegrationModulesUnderTest() (vr ports.VRouter, d *docker_libnetwork_pl serverUrl, _ := url.Parse("http://127.0.0.1:9091") a := agent.NewAgentRestAPI(http.DefaultClient, serverUrl) - driverCore, err := driver_core.NewContrailDriverCore(vr, c, a, netRepo, epRepo) + hnsWorkaroundSleepTime := time.Second * 0 + driverCore, err := driver_core.NewContrailDriverCore(vr, c, a, netRepo, epRepo, + hnsWorkaroundSleepTime) Expect(err).ToNot(HaveOccurred()) d = docker_libnetwork_plugin.NewDockerPluginServer(driverCore) diff --git a/adapters/secondary/controller_REST/api/api_client.go b/adapters/secondary/controller_REST/api/api_client.go index 1d88ed3..c8c6faf 100644 --- a/adapters/secondary/controller_REST/api/api_client.go +++ b/adapters/secondary/controller_REST/api/api_client.go @@ -23,6 +23,10 @@ import ( func NewFakeApiClient() *mocks.ApiClient { mockedApiClient := new(mocks.ApiClient) mockedApiClient.Init() + // Add interceptors to mimick the behaviour of actual API server. + mockedApiClient.AddInterceptor("virtual-network", &vnInterceptor{}) + mockedApiClient.AddInterceptor("virtual-machine-interface", &vmiInterceptor{}) + mockedApiClient.AddInterceptor("instance-ip", &iipInterceptor{}) return mockedApiClient } diff --git a/adapters/secondary/controller_REST/controller.go b/adapters/secondary/controller_REST/controller.go index cfa9238..04c9421 100644 --- a/adapters/secondary/controller_REST/controller.go +++ b/adapters/secondary/controller_REST/controller.go @@ -31,25 +31,48 @@ import ( type Info struct { } -type ControllerAdapter struct { +type ControllerAdapterImpl struct { ApiClient contrail.ApiClient } -func newControllerAdapter(apiClient contrail.ApiClient) *ControllerAdapter { - client := &ControllerAdapter{ApiClient: apiClient} +// TODO: this factory function is public only because we have a bunch of tests for +// ControllerAdapterImpl methods. We should instead test via the Facade - ControllerAdapter. +// I decided against rewriting all the tests for now, but it would be an improvement. +// Right now, ControllerAdapterImpl test the implementation, and not the behaviour. +func NewControllerAdapterImpl(apiClient contrail.ApiClient) *ControllerAdapterImpl { + client := &ControllerAdapterImpl{ApiClient: apiClient} return client } -func (c *ControllerAdapter) NewProject(domain, tenant string) (*types.Project, error) { +// TODO: this method is only used by tests - it can probably be removed from ControllerAdapterImpl +// entirely and moved to helpers. +func (c *ControllerAdapterImpl) NewProject(domain, tenant string) (*types.Project, error) { project := new(types.Project) project.SetFQName("domain", []string{domain, tenant}) if err := c.ApiClient.Create(project); err != nil { return nil, err } + + // Create security group as soon as project is created. This mimics contrail API server + // behaviuor. We can do it here, because NewProject method is used only in tests (see + // method comment). + if _, err := c.createSecurityGroup(domain, tenant); err != nil { + return nil, err + } + return project, nil } -func (c *ControllerAdapter) CreateNetworkWithSubnet(tenantName, networkName, subnetCIDR string) (*types.VirtualNetwork, error) { +func (c *ControllerAdapterImpl) createSecurityGroup(domain, tenant string) (*types.SecurityGroup, error) { + secgroup := new(types.SecurityGroup) + secgroup.SetFQName("project", []string{domain, tenant, "default"}) + if err := c.ApiClient.Create(secgroup); err != nil { + return nil, err + } + return secgroup, nil +} + +func (c *ControllerAdapterImpl) CreateNetworkWithSubnet(tenantName, networkName, subnetCIDR string) (*types.VirtualNetwork, error) { net, err := c.GetNetwork(tenantName, networkName) if err == nil { showDetails := true @@ -82,7 +105,7 @@ func (c *ControllerAdapter) CreateNetworkWithSubnet(tenantName, networkName, sub return net, nil } -func (c *ControllerAdapter) GetNetwork(tenantName, networkName string) (*types.VirtualNetwork, +func (c *ControllerAdapterImpl) GetNetwork(tenantName, networkName string) (*types.VirtualNetwork, error) { name := fmt.Sprintf("%s:%s:%s", common.DomainName, tenantName, networkName) net, err := types.VirtualNetworkByName(c.ApiClient, name) @@ -95,7 +118,7 @@ func (c *ControllerAdapter) GetNetwork(tenantName, networkName string) (*types.V // GetIpamSubnet returns IPAM subnet of specified virtual network with specified CIDR. // If virtual network has only one subnet, CIDR is ignored. -func (c *ControllerAdapter) GetIpamSubnet(net *types.VirtualNetwork, CIDR string) ( +func (c *ControllerAdapterImpl) GetIpamSubnet(net *types.VirtualNetwork, CIDR string) ( *types.IpamSubnetType, error) { if strings.HasPrefix(CIDR, "0.0.0.0") { @@ -150,7 +173,7 @@ func (c *ControllerAdapter) GetIpamSubnet(net *types.VirtualNetwork, CIDR string return nil, err } -func (c *ControllerAdapter) GetDefaultGatewayIp(subnet *types.IpamSubnetType) (string, error) { +func (c *ControllerAdapterImpl) GetDefaultGatewayIp(subnet *types.IpamSubnetType) (string, error) { gw := subnet.DefaultGateway if gw == "" { err := errors.New("Default GW is empty") @@ -160,7 +183,7 @@ func (c *ControllerAdapter) GetDefaultGatewayIp(subnet *types.IpamSubnetType) (s return gw, nil } -func (c *ControllerAdapter) GetOrCreateInstance(vif *types.VirtualMachineInterface, containerId string) ( +func (c *ControllerAdapterImpl) GetOrCreateInstance(vif *types.VirtualMachineInterface, containerId string) ( *types.VirtualMachine, error) { instance, err := c.GetInstance(containerId) if err == nil { @@ -199,12 +222,12 @@ func (c *ControllerAdapter) GetOrCreateInstance(vif *types.VirtualMachineInterfa return createdInstance, nil } -func (c *ControllerAdapter) GetInstance(containerId string) ( +func (c *ControllerAdapterImpl) GetInstance(containerId string) ( *types.VirtualMachine, error) { return types.VirtualMachineByName(c.ApiClient, containerId) } -func (c *ControllerAdapter) GetExistingInterface(net *types.VirtualNetwork, tenantName, +func (c *ControllerAdapterImpl) GetExistingInterface(net *types.VirtualNetwork, tenantName, containerId string) (*types.VirtualMachineInterface, error) { fqName := fmt.Sprintf("%s:%s:%s", common.DomainName, tenantName, containerId) @@ -220,7 +243,7 @@ func (c *ControllerAdapter) GetExistingInterface(net *types.VirtualNetwork, tena return nil, errors.New("Interface does not exist") } -func (c *ControllerAdapter) GetOrCreateInterface(net *types.VirtualNetwork, tenantName, +func (c *ControllerAdapterImpl) GetOrCreateInterface(net *types.VirtualNetwork, tenantName, containerId string) (*types.VirtualMachineInterface, error) { fqName := fmt.Sprintf("%s:%s:%s", common.DomainName, tenantName, containerId) @@ -259,7 +282,7 @@ func (c *ControllerAdapter) GetOrCreateInterface(net *types.VirtualNetwork, tena return createdIface, nil } -func (c *ControllerAdapter) assignDefaultSecurityGroup(iface *types.VirtualMachineInterface, tenantName string) error { +func (c *ControllerAdapterImpl) assignDefaultSecurityGroup(iface *types.VirtualMachineInterface, tenantName string) error { secGroupFqName := fmt.Sprintf("%s:%s:default", common.DomainName, tenantName) secGroup, err := types.SecurityGroupByName(c.ApiClient, secGroupFqName) if err != nil || secGroup == nil { @@ -269,7 +292,7 @@ func (c *ControllerAdapter) assignDefaultSecurityGroup(iface *types.VirtualMachi return iface.AddSecurityGroup(secGroup) } -func (c *ControllerAdapter) GetInterfaceMac(iface *types.VirtualMachineInterface) (string, error) { +func (c *ControllerAdapterImpl) GetInterfaceMac(iface *types.VirtualMachineInterface) (string, error) { macs := iface.GetVirtualMachineInterfaceMacAddresses() if len(macs.MacAddress) == 0 { err := errors.New("Empty MAC list") @@ -279,7 +302,7 @@ func (c *ControllerAdapter) GetInterfaceMac(iface *types.VirtualMachineInterface return macs.MacAddress[0], nil } -func (c *ControllerAdapter) GetOrCreateInstanceIp(net *types.VirtualNetwork, +func (c *ControllerAdapterImpl) GetOrCreateInstanceIp(net *types.VirtualNetwork, iface *types.VirtualMachineInterface, subnetUuid string) (*types.InstanceIp, error) { instIp, err := types.InstanceIpByName(c.ApiClient, iface.GetName()) if err == nil && instIp != nil { @@ -314,7 +337,7 @@ func (c *ControllerAdapter) GetOrCreateInstanceIp(net *types.VirtualNetwork, return allocatedIP, nil } -func (c *ControllerAdapter) DeleteElementRecursive(parent contrail.IObject) error { +func (c *ControllerAdapterImpl) DeleteElementRecursive(parent contrail.IObject) error { log.Debugln("Deleting", parent.GetType(), parent.GetUuid()) for err := c.ApiClient.Delete(parent); err != nil; err = c.ApiClient.Delete(parent) { // TODO: when fixing this method, consider using c.is404() method. @@ -359,6 +382,6 @@ func (c *ControllerAdapter) DeleteElementRecursive(parent contrail.IObject) erro return nil } -func (c *ControllerAdapter) is404(err error) bool { +func (c *ControllerAdapterImpl) is404(err error) bool { return strings.HasPrefix(err.Error(), "404") } diff --git a/adapters/secondary/controller_REST/controller_helpers_test.go b/adapters/secondary/controller_REST/controller_helpers_test.go index 6297110..6937aff 100644 --- a/adapters/secondary/controller_REST/controller_helpers_test.go +++ b/adapters/secondary/controller_REST/controller_helpers_test.go @@ -23,13 +23,15 @@ import ( "github.com/Juniper/contrail-go-api/config" "github.com/Juniper/contrail-go-api/types" "github.com/Juniper/contrail-windows-docker-driver/adapters/secondary/controller_rest" + "github.com/Juniper/contrail-windows-docker-driver/adapters/secondary/controller_rest/api" "github.com/Juniper/contrail-windows-docker-driver/common" . "github.com/onsi/gomega" log "github.com/sirupsen/logrus" ) -func NewTestClientAndProject(tenant string) (*controller_rest.ControllerAdapter, *types.Project) { - c := controller_rest.NewFakeControllerAdapter() +func NewTestClientAndProject(tenant string) (*controller_rest.ControllerAdapterImpl, *types.Project) { + fakeApiClient := api.NewFakeApiClient() + c := controller_rest.NewControllerAdapterImpl(fakeApiClient) project, err := c.NewProject(common.DomainName, tenant) Expect(err).ToNot(HaveOccurred()) @@ -58,13 +60,12 @@ func CreateTestNetwork(c contrail.ApiClient, netName string, return testNetwork } -func CreateTestSecurityGroup(c contrail.ApiClient, groupName string, - project *types.Project) *types.SecurityGroup { - group := new(types.SecurityGroup) - group.SetFQName("project", []string{common.DomainName, project.GetName(), groupName}) - err := c.Create(group) +func RemoveTestSecurityGroup(c contrail.ApiClient, groupName string, + project *types.Project) { + secGroupFqName := fmt.Sprintf("%s:%s:default", common.DomainName, tenantName) + secGroup, err := types.SecurityGroupByName(c, secGroupFqName) + err = c.Delete(secGroup) Expect(err).ToNot(HaveOccurred()) - return group } func AddSubnetWithDefaultGateway(c contrail.ApiClient, subnetPrefix, defaultGW string, @@ -144,7 +145,7 @@ func CreateTestInstanceIP(c contrail.ApiClient, tenantName string, return allocatedIP } -func ForceDeleteProject(c *controller_rest.ControllerAdapter, tenant string) { +func ForceDeleteProject(c *controller_rest.ControllerAdapterImpl, tenant string) { projToDelete, _ := c.ApiClient.FindByName("project", fmt.Sprintf("%s:%s", common.DomainName, tenant)) if projToDelete != nil { @@ -152,7 +153,7 @@ func ForceDeleteProject(c *controller_rest.ControllerAdapter, tenant string) { } } -func CleanupLingeringVM(c *controller_rest.ControllerAdapter, containerID string) { +func CleanupLingeringVM(c *controller_rest.ControllerAdapterImpl, containerID string) { instance, err := types.VirtualMachineByName(c.ApiClient, containerID) if err == nil { log.Debugln("Cleaning up lingering test vm", instance.GetUuid()) diff --git a/adapters/secondary/controller_REST/controller_test.go b/adapters/secondary/controller_REST/controller_test.go index a6f1b1b..8894247 100644 --- a/adapters/secondary/controller_REST/controller_test.go +++ b/adapters/secondary/controller_REST/controller_test.go @@ -73,9 +73,9 @@ var _ = BeforeSuite(func() { // } }) -var _ = Describe("ControllerAdapter", func() { +var _ = Describe("ControllerAdapterImpl", func() { - var client *ControllerAdapter + var client *ControllerAdapterImpl var project *types.Project BeforeEach(func() { @@ -355,9 +355,6 @@ var _ = Describe("ControllerAdapter", func() { }) Context("when vif doesn't exist in Contrail", func() { Context("when default security group exists", func() { - BeforeEach(func() { - CreateTestSecurityGroup(client.ApiClient, "default", project) - }) It("creates a new vif", func() { iface, err := client.GetOrCreateInterface(testNetwork, tenantName, containerID) Expect(err).ToNot(HaveOccurred()) @@ -378,6 +375,9 @@ var _ = Describe("ControllerAdapter", func() { }) }) Context("when default security group doesn't exist", func() { + BeforeEach(func() { + RemoveTestSecurityGroup(client.ApiClient, "default", project) + }) It("returns an error", func() { iface, err := client.GetOrCreateInterface(testNetwork, tenantName, containerID) Expect(err).To(HaveOccurred()) @@ -524,9 +524,6 @@ var _ = Describe("ControllerAdapter", func() { }) Context("when instance IP doesn't exist in Contrail", func() { It("creates new instance IP", func() { - if !useActualController { - Skip("test fails (pending) when using mocked client") - } instanceIP, err := client.GetOrCreateInstanceIp(testNetwork, testInterface, "") Expect(err).ToNot(HaveOccurred()) Expect(instanceIP).ToNot(BeNil()) diff --git a/adapters/secondary/controller_REST/factory.go b/adapters/secondary/controller_REST/factory.go index 3d2c8a5..b30364b 100644 --- a/adapters/secondary/controller_REST/factory.go +++ b/adapters/secondary/controller_REST/factory.go @@ -33,10 +33,12 @@ func NewControllerWithKeystoneAdapter(keys *auth.KeystoneParams, ip string, port apiClient := api.NewApiClient(ip, port, auth) - return newControllerAdapter(apiClient), nil + impl := NewControllerAdapterImpl(apiClient) + return newControllerAdapter(impl), nil } func NewFakeControllerAdapter() *ControllerAdapter { fakeApiClient := api.NewFakeApiClient() - return newControllerAdapter(fakeApiClient) + impl := NewControllerAdapterImpl(fakeApiClient) + return newControllerAdapter(impl) } diff --git a/adapters/secondary/controller_rest/api/fake_api_interceptors.go b/adapters/secondary/controller_rest/api/fake_api_interceptors.go new file mode 100644 index 0000000..1d18434 --- /dev/null +++ b/adapters/secondary/controller_rest/api/fake_api_interceptors.go @@ -0,0 +1,93 @@ +// +// Copyright (c) 2018 Juniper Networks, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package api + +import ( + contrail "github.com/Juniper/contrail-go-api" + "github.com/Juniper/contrail-go-api/types" + log "github.com/sirupsen/logrus" +) + +// vnInterceptor is responsible for adding a DefaultGateway to ipamSubnets created in +// tests. This is mimicking the behaviour of the actual API server that assings this +// address on its own. +type vnInterceptor struct{} + +func (ceptor *vnInterceptor) Get(obj contrail.IObject) { +} + +func (ceptor *vnInterceptor) Put(obj contrail.IObject) { + vn, ok := obj.(*types.VirtualNetwork) + if !ok { + log.Errorln("Invalid cast") + } + ipamReferences, err := vn.GetNetworkIpamRefs() + if err != nil { + log.Errorln(err) + return + } + for refIdx, _ := range ipamReferences { + ipamSubnets := ipamReferences[refIdx].Attr.(types.VnSubnetsType).IpamSubnets + for ipamIdx, _ := range ipamSubnets { + // It doesn't matter what IP we put here, because we don't care in the + // tests. What matters, is that it's not an empty string - for now. + ipamSubnets[ipamIdx].DefaultGateway = "1.2.3.4" + } + } +} + +// vmiInterceptor is responsible for assigning a MAC address to retreived virtual machine +// interface. It mimicks the behaviour of the actual API server. +type vmiInterceptor struct{} + +func (ceptor *vmiInterceptor) Get(obj contrail.IObject) { + vmi, ok := obj.(*types.VirtualMachineInterface) + if !ok { + log.Errorln("Invalid cast") + } + macs := vmi.GetVirtualMachineInterfaceMacAddresses() + if len(macs.MacAddress) == 0 { + // Add new MAC address - it doesn't matter what the actual value is, as we don't + // check it in the tests - for now. + var newMacs types.MacAddressesType + newMacs.AddMacAddress("DE:AD:BE:EF:FE:ED") + vmi.SetVirtualMachineInterfaceMacAddresses(&newMacs) + } +} + +func (ceptor *vmiInterceptor) Put(obj contrail.IObject) { +} + +// ipInterceptor is responsible for assigning an IP to newly created interface. This mimics +// the behaviour of the actual APi server. +type iipInterceptor struct{} + +func (ceptor *iipInterceptor) Get(obj contrail.IObject) { + ipobj, ok := obj.(*types.InstanceIp) + if !ok { + log.Errorln("Invalid cast") + } + ips := ipobj.GetInstanceIpAddress() + if len(ips) == 0 { + // We don't care about the specific value of IP, because we don't compare it in tests + // - for now. + ipobj.SetInstanceIpAddress("1.2.3.4") + } + +} + +func (ceptor *iipInterceptor) Put(obj contrail.IObject) { +} diff --git a/adapters/secondary/controller_rest/facade.go b/adapters/secondary/controller_rest/facade.go new file mode 100644 index 0000000..f8d9bd0 --- /dev/null +++ b/adapters/secondary/controller_rest/facade.go @@ -0,0 +1,119 @@ +// +// Copyright (c) 2018 Juniper Networks, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package controller_rest + +import ( + "net" + + contrail "github.com/Juniper/contrail-go-api" + "github.com/Juniper/contrail-go-api/types" + "github.com/Juniper/contrail-windows-docker-driver/core/ports" + log "github.com/sirupsen/logrus" +) + +// ControllerAdapter is a facade for complicated ControllerAdapterImpl. It exist, because +// there are lots of tests for ControllerAdapterImpl class, and it would be hard to modify it +// directly without throwing away many tests. +type ControllerAdapter struct { + controller *ControllerAdapterImpl +} + +func newControllerAdapter(controller *ControllerAdapterImpl) *ControllerAdapter { + return &ControllerAdapter{ + controller: controller, + } +} + +func (c *ControllerAdapter) NewProject(domain, tenant string) (*types.Project, error) { + return c.controller.NewProject(domain, tenant) +} + +func (c *ControllerAdapter) CreateNetworkWithSubnet(tenantName, networkName, subnetCIDR string) (*types.VirtualNetwork, error) { + return c.controller.CreateNetworkWithSubnet(tenantName, networkName, subnetCIDR) +} + +func (c *ControllerAdapter) GetNetwork(tenantName, networkName string) (*types.VirtualNetwork, error) { + return c.controller.GetNetwork(tenantName, networkName) +} + +func (c *ControllerAdapter) GetNetworkWithSubnet(tenantName, networkName, subnetCIDR string) (*types.VirtualNetwork, *types.IpamSubnetType, error) { + network, err := c.controller.GetNetwork(tenantName, networkName) + if err != nil { + return nil, nil, err + } + + log.Infoln("Got Contrail network", network.GetDisplayName()) + + ipamSubnet, err := c.controller.GetIpamSubnet(network, subnetCIDR) + if err != nil { + return nil, nil, err + } + + return network, ipamSubnet, nil +} + +func (c *ControllerAdapter) GetDefaultGatewayIp(ipamSubnet *types.IpamSubnetType) (string, error) { + return c.controller.GetDefaultGatewayIp(ipamSubnet) +} + +func (c *ControllerAdapter) CreateContainerInSubnet(tenantName, containerID string, + network *types.VirtualNetwork, ipamSubnet *types.IpamSubnetType) (*ports.ContrailContainer, error) { + + vif, err := c.controller.GetOrCreateInterface(network, tenantName, containerID) + if err != nil { + return nil, err + } + + vm, err := c.controller.GetOrCreateInstance(vif, containerID) + if err != nil { + return nil, err + } + + ipobj, err := c.controller.GetOrCreateInstanceIp(network, vif, ipamSubnet.SubnetUuid) + if err != nil { + return nil, err + } + ip := ipobj.GetInstanceIpAddress() + log.Debugln("Retrieved instance IP:", ip) + + mac, err := c.controller.GetInterfaceMac(vif) + log.Debugln("Retrieved MAC:", mac) + if err != nil { + return nil, err + } + + container := &ports.ContrailContainer{ + IP: net.ParseIP(ip), + PrefixLen: ipamSubnet.Subnet.IpPrefixLen, + Mac: mac, + VmUUID: vm.GetUuid(), + VmiUUID: vif.GetUuid(), + } + + return container, nil +} + +func (c *ControllerAdapter) GetInstance(containerId string) (*types.VirtualMachine, error) { + return c.controller.GetInstance(containerId) +} + +func (c *ControllerAdapter) GetExistingInterface(net *types.VirtualNetwork, tenantName, containerId string) (*types.VirtualMachineInterface, error) { + return c.controller.GetExistingInterface(net, tenantName, containerId) +} + +func (c *ControllerAdapter) DeleteElementRecursive(parent contrail.IObject) error { + return c.controller.DeleteElementRecursive(parent) +} diff --git a/adapters/secondary/local_networking/simulator/endpoint_repository.go b/adapters/secondary/local_networking/simulator/endpoint_repository.go index bc8928a..eccf30a 100644 --- a/adapters/secondary/local_networking/simulator/endpoint_repository.go +++ b/adapters/secondary/local_networking/simulator/endpoint_repository.go @@ -21,14 +21,27 @@ import ( "github.com/Microsoft/hcsshim" ) -type InMemEndpointRepository struct{} +type InMemEndpointRepository struct { + endpoints map[string]hcsshim.HNSEndpoint +} + +func NewInMemEndpointRepository() *InMemEndpointRepository { + return &InMemEndpointRepository{ + endpoints: make(map[string]hcsshim.HNSEndpoint), + } +} func (repo *InMemEndpointRepository) CreateEndpoint(configuration *hcsshim.HNSEndpoint) (string, error) { - return "", errors.New("Not implemented yet") + repo.endpoints[configuration.Name] = *configuration + return configuration.Id, nil } func (repo *InMemEndpointRepository) GetEndpointByName(name string) (*hcsshim.HNSEndpoint, error) { - return nil, errors.New("Not implemented yet") + if ep, exists := repo.endpoints[name]; exists { + return &ep, nil + } else { + return nil, errors.New("endpoint not found") + } } func (repo *InMemEndpointRepository) DeleteEndpoint(endpointID string) error { diff --git a/core/driver_core/driver_core.go b/core/driver_core/driver_core.go index 0236af1..35bdb8d 100644 --- a/core/driver_core/driver_core.go +++ b/core/driver_core/driver_core.go @@ -21,10 +21,15 @@ package driver_core import ( "errors" "fmt" + "strings" + "time" // TODO: this import should be removed when making Controller port smaller "github.com/Juniper/contrail-go-api/types" + // TODO: this import should be removed + "github.com/Microsoft/hcsshim" + "github.com/Juniper/contrail-windows-docker-driver/core/ports" log "github.com/sirupsen/logrus" ) @@ -37,17 +42,21 @@ type ContrailDriverCore struct { Agent ports.Agent LocalContrailNetworksRepo ports.LocalContrailNetworkRepository LocalContrailEndpointsRepo ports.LocalContrailEndpointRepository + // TODO: get rid of this sleep workaround asap + hnsEndpointWaitingTime time.Duration } func NewContrailDriverCore(vr ports.VRouter, c ports.Controller, a ports.Agent, nr ports.LocalContrailNetworkRepository, - er ports.LocalContrailEndpointRepository) (*ContrailDriverCore, error) { + er ports.LocalContrailEndpointRepository, + hnsEndpointWaitingTime time.Duration) (*ContrailDriverCore, error) { core := ContrailDriverCore{ vrouter: vr, Controller: c, Agent: a, LocalContrailNetworksRepo: nr, LocalContrailEndpointsRepo: er, + hnsEndpointWaitingTime: hnsEndpointWaitingTime, } if err := core.initializeAdapters(); err != nil { return nil, err @@ -59,27 +68,19 @@ func (core *ContrailDriverCore) initializeAdapters() error { return core.vrouter.Initialize() } -func (core *ContrailDriverCore) CreateNetwork(tenantName, networkName, ipPool string) error { - // Check if network is already created in Contrail. - log.Infoln(tenantName, networkName) - contrailNetwork, err := core.Controller.GetNetwork(tenantName, networkName) +func (core *ContrailDriverCore) CreateNetwork(tenantName, networkName, subnetCIDR string) error { + network, ipamSubnet, err := core.Controller.GetNetworkWithSubnet(tenantName, networkName, subnetCIDR) if err != nil { return err } - if contrailNetwork == nil { + if network == nil { return errors.New("Retrieved Contrail network is nil") } - log.Infoln("Got Contrail network", contrailNetwork.GetDisplayName()) - - contrailIpam, err := core.Controller.GetIpamSubnet(contrailNetwork, ipPool) - if err != nil { - return err - } - subnetCIDR := core.GetContrailSubnetCIDR(contrailIpam) + log.Debugln("Got Contrail network", network.GetDisplayName()) - contrailGateway := contrailIpam.DefaultGateway - if contrailGateway == "" { + gateway := ipamSubnet.DefaultGateway + if gateway == "" { // TODO: this fails in unit tests using contrail-go-api mock. So either: // * fix contrail-go-api mock to return *some* default GW // * or maybe we should keep going if default GW is empty, as a user may wish not @@ -87,12 +88,8 @@ func (core *ContrailDriverCore) CreateNetwork(tenantName, networkName, ipPool st log.Warn("Default GW is empty") } - // TODO: all the statements above should probably be refactored into a single method of - // Controller port. Something like - // subnetCIDR, gateway := GetSubnet(tenantName, networkName, ipPool) - _, err = core.LocalContrailNetworksRepo.CreateNetwork(tenantName, networkName, subnetCIDR, - contrailGateway) + gateway) return err } @@ -101,7 +98,91 @@ func (core *ContrailDriverCore) DeleteNetwork(tenantName, networkName, subnetCID return core.LocalContrailNetworksRepo.DeleteNetwork(tenantName, networkName, subnetCIDR) } +func (core *ContrailDriverCore) CreateEndpoint(tenantName, networkName, subnetCIDR, endpointID string) (*ports.ContrailContainer, error) { + + // WORKAROUND: We need to retreive Container ID here and use it instead of EndpointID as + // argument to GetOrCreateInstance(). EndpointID is equiv to interface, but in Contrail, + // we have a "VirtualMachine" in data model. A single VM can be connected to two or more + // overlay networks, but when we use EndpointID, this won't work. We need something like: + // containerID := req.Options["vmname"] + containerID := endpointID + + network, ipamSubnet, err := core.Controller.GetNetworkWithSubnet(tenantName, networkName, subnetCIDR) + if err != nil { + return nil, err + } + + log.Infoln(core.Controller.CreateContainerInSubnet(tenantName, containerID, network, ipamSubnet)) + + container, err := core.Controller.CreateContainerInSubnet(tenantName, containerID, network, ipamSubnet) + if err != nil { + return nil, err + } + + // contrail MACs are like 11:22:aa:bb:cc:dd + // HNS needs MACs like 11-22-AA-BB-CC-DD + formattedMac := strings.Replace(strings.ToUpper(container.Mac), ":", "-", -1) + + hnsNet, err := core.LocalContrailNetworksRepo.GetNetwork(tenantName, networkName, subnetCIDR) + if err != nil { + return nil, err + } + + gateway := ipamSubnet.DefaultGateway + if gateway == "" { + return nil, errors.New("Default GW is empty") + } + + // TODO: We should remove references to hcsshim here - probably by adding a new struct + // to core/ports/models.go + hnsEndpointConfig := &hcsshim.HNSEndpoint{ + VirtualNetworkName: hnsNet.Name, + Name: endpointID, + IPAddress: container.IP, + MacAddress: formattedMac, + GatewayAddress: gateway, + } + + hnsEndpointID, err := core.LocalContrailEndpointsRepo.CreateEndpoint(hnsEndpointConfig) + if err != nil { + return nil, err + } + + // TODO: this can be refactored into some nice mechanism. + ifName := core.generateFriendlyName(hnsEndpointID) + + go func() { + // WORKAROUND: Temporary workaround for HNS issue. + // Due to the bug in Microsoft HNS, Docker Driver has to wait for some time until endpoint + // is ready to handle ARP requests. Unfortunately it seems that HNS doesn't have api + // to verify if endpoint setup is done + time.Sleep(core.hnsEndpointWaitingTime) + err := core.Agent.AddPort(container.VmUUID, container.VmiUUID, ifName, + container.Mac, containerID, container.IP.String(), network.GetUuid()) + if err != nil { + log.Error(err.Error()) + } + }() + return container, nil +} + func (core *ContrailDriverCore) GetContrailSubnetCIDR(ipam *types.IpamSubnetType) string { // TODO: this should be probably moved to Controller when we make Controller port smaller. return fmt.Sprintf("%s/%v", ipam.Subnet.IpPrefix, ipam.Subnet.IpPrefixLen) } + +func (core *ContrailDriverCore) generateFriendlyName(hnsEndpointID string) string { + // Here's how the Forwarding Extension (kernel) can identify interfaces based on their + // friendly names. + // Windows Containers have NIC names like "NIC ID abcdef", where abcdef are the first 6 chars + // of their HNS endpoint ID. + // Hyper-V Containers have NIC names consisting of two uuids, probably representing utitlity + // VM's interface and endpoint's interface: + // "227301f6-bee9-4ae2-8a93-5e900cde3f47--910c5490-bff8-45e3-a2a0-0114ed9903e0" + // The second UUID (after the "--") is the HNS endpoints ID. + + // For now, we will always send the name in the Windows Containers format, because it probably + // has enough information to recognize it in kernel (6 first chars of UUID should be enough): + containerNicID := strings.Split(hnsEndpointID, "-")[0] + return fmt.Sprintf("Container NIC %s", containerNicID) +} diff --git a/core/driver_core/driver_core_test.go b/core/driver_core/driver_core_test.go index ec369d5..b2c4cf6 100644 --- a/core/driver_core/driver_core_test.go +++ b/core/driver_core/driver_core_test.go @@ -17,9 +17,11 @@ package driver_core_test import ( + "net" "net/http" "net/url" "testing" + "time" "github.com/Juniper/contrail-go-api/types" "github.com/Juniper/contrail-windows-docker-driver/adapters/secondary/controller_rest" @@ -34,12 +36,14 @@ import ( . "github.com/onsi/ginkgo/extensions/table" "github.com/onsi/ginkgo/reporters" . "github.com/onsi/gomega" + "github.com/onsi/gomega/ghttp" ) const ( - tenantName = "agatka" - networkName = "test_net" - subnetCIDR = "1.2.3.4/24" + tenantName = "agatka" + networkName = "test_net" + securityGroupName = "default" + subnetCIDR = "1.2.3.0/24" ) func TestCore(t *testing.T) { @@ -53,9 +57,10 @@ var _ = Describe("Core tests", func() { var testedCore *driver_core.ContrailDriverCore var controller ports.Controller var localNetRepo ports.LocalContrailNetworkRepository + var localEpRepo ports.LocalContrailEndpointRepository BeforeEach(func() { - testedCore, controller, localNetRepo = newSimulatedModulesUnderTest() + testedCore, controller, localNetRepo, localEpRepo = newSimulatedModulesUnderTest() }) Context("CreateNetwork", func() { @@ -103,6 +108,115 @@ var _ = Describe("Core tests", func() { }) Context("CreateEndpoint", func() { + var recvChan chan interface{} + var server *ghttp.Server + BeforeEach(func() { + recvChan = make(chan interface{}) + server = testServer(recvChan) + server.AppendHandlers( + ghttp.VerifyRequest("POST", "/port"), + ghttp.RespondWith(http.StatusOK, ""), + ) + }) + AfterEach(func() { + server.Close() + }) + + endpointID := "1234" + + Context("Controller network and local network exist", func() { + BeforeEach(func() { + _ = testProject(controller) + _ = testNetwork(controller) + err := testedCore.CreateNetwork(tenantName, networkName, subnetCIDR) + Expect(err).ToNot(HaveOccurred()) + + server.AppendHandlers( + ghttp.VerifyRequest("POST", "/port"), + ghttp.RespondWith(http.StatusOK, ""), + ) + }) + AfterEach(func() { + // Because right now port request is send asynchronously in a goroutine, we need to + // wait after each test case for any requests before moving onto the next test case. + // This is to ensure test isolation. Otherwise, the async request may "spill over" to + // the next test case which would be hard to debug. + By("notifies port listener") + Eventually(func() []*http.Request { + return server.ReceivedRequests() + }).Should(HaveLen(1)) + }) + It("returns container resource allocated in controller", func() { + container, err := testedCore.CreateEndpoint(tenantName, networkName, subnetCIDR, endpointID) + Expect(err).ToNot(HaveOccurred()) + + Expect(container.IP).To(MatchRegexp(`1.2.3.[0-9]+`)) + Expect(container.PrefixLen).To(Equal(24)) + Expect(container.Mac).To(MatchRegexp(`([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})`)) + Expect(container.VmUUID).ToNot(Equal("")) + Expect(container.VmiUUID).ToNot(Equal("")) + }) + It("configures HNS endpoint", func() { + _, err := testedCore.CreateEndpoint(tenantName, networkName, subnetCIDR, endpointID) + Expect(err).ToNot(HaveOccurred()) + + ep, err := localEpRepo.GetEndpointByName(endpointID) + Expect(err).ToNot(HaveOccurred()) + Expect(ep).ToNot(BeNil()) + Expect(ep.Name).To(Equal(endpointID)) + }) + }) + + assertErrors := func() { + container, err := testedCore.CreateEndpoint(tenantName, networkName, subnetCIDR, endpointID) + Expect(err).To(HaveOccurred()) + Expect(container).To(BeNil()) + } + assertDoesNotAllocate := func() { + container, err := testedCore.CreateEndpoint(tenantName, networkName, subnetCIDR, endpointID) + Expect(err).To(HaveOccurred()) + Expect(container).To(BeNil()) + } + assertDoesNotConfigure := func() { + _, err := testedCore.CreateEndpoint(tenantName, networkName, subnetCIDR, endpointID) + Expect(err).To(HaveOccurred()) + + ep, err := localEpRepo.GetEndpointByName(endpointID) + Expect(err).To(HaveOccurred()) + Expect(ep).To(BeNil()) + } + assertAfterEachDoesNotNotify := func() { + // Because right now port request is send asynchronously in a goroutine, we need to + // wait after each test case for any requests before moving onto the next test case. + // This is to ensure test isolation. Otherwise, the async request may "spill over" to + // the next test case which would be hard to debug. + By("does not notify port listener") + Consistently(func() []*http.Request { + return server.ReceivedRequests() + }).Should(HaveLen(0)) + } + Context("Contrail network exists, but local network does not exist", func() { + BeforeEach(func() { + _ = testProject(controller) + _ = testNetwork(controller) + }) + AfterEach(assertAfterEachDoesNotNotify) + It("errors", assertErrors) + It("doesn't allocate resources in controller", assertDoesNotAllocate) + It("doesn't configure HNS endpoint", assertDoesNotConfigure) + + }) + Context("Contrail network does not exist, but local network does", func() { + BeforeEach(func() { + someGateway := "1.2.3.1" + _, err := localNetRepo.CreateNetwork(tenantName, networkName, subnetCIDR, someGateway) + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(assertAfterEachDoesNotNotify) + It("errors", assertErrors) + It("doesn't allocate resources in controller", assertDoesNotAllocate) + It("doesn't configure HNS endpoint", assertDoesNotConfigure) + }) }) Context("DeleteEndpoint", func() { @@ -110,7 +224,8 @@ var _ = Describe("Core tests", func() { }) func newSimulatedModulesUnderTest() (c *driver_core.ContrailDriverCore, controller ports.Controller, - netRepo ports.LocalContrailNetworkRepository) { + netRepo ports.LocalContrailNetworkRepository, + epRepo ports.LocalContrailEndpointRepository) { ext := &hyperv_extension.HyperVExtensionSimulator{ Enabled: false, Running: true, @@ -120,14 +235,15 @@ func newSimulatedModulesUnderTest() (c *driver_core.ContrailDriverCore, controll controller = controller_rest.NewFakeControllerAdapter() netRepo = netSim.NewInMemContrailNetworksRepository() - epRepo := &netSim.InMemEndpointRepository{} + epRepo = netSim.NewInMemEndpointRepository() // TODO: Implement simulator for Agent. serverUrl, _ := url.Parse("http://127.0.0.1:9091") agent := agent.NewAgentRestAPI(http.DefaultClient, serverUrl) var err error - c, err = driver_core.NewContrailDriverCore(vrouter, controller, agent, netRepo, epRepo) + sleepTime := time.Second * 0 + c, err = driver_core.NewContrailDriverCore(vrouter, controller, agent, netRepo, epRepo, sleepTime) Expect(err).ToNot(HaveOccurred()) return @@ -144,3 +260,15 @@ func testNetwork(c ports.Controller) *types.VirtualNetwork { Expect(err).ToNot(HaveOccurred()) return network } + +func testServer(recv chan interface{}) *ghttp.Server { + // TODO: Refactor this test to use listener simulator, instead this test + // http server, when it's implemented. + server := ghttp.NewUnstartedServer() + listener, err := net.Listen("tcp", "127.0.0.1:9091") + Expect(err).ToNot(HaveOccurred()) + server.HTTPTestServer.Listener = listener + + server.Start() + return server +} diff --git a/core/ports/model.go b/core/ports/model.go new file mode 100644 index 0000000..517bfe0 --- /dev/null +++ b/core/ports/model.go @@ -0,0 +1,29 @@ +// +// Copyright (c) 2018 Juniper Networks, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Implemented according to +// https://github.com/docker/libnetwork/blob/master/docs/remote.md + +package ports + +import "net" + +type ContrailContainer struct { + IP net.IP + PrefixLen int + Mac string + VmUUID string + VmiUUID string +} diff --git a/core/ports/ports.go b/core/ports/ports.go index ee4b0bb..6ac112f 100644 --- a/core/ports/ports.go +++ b/core/ports/ports.go @@ -48,22 +48,17 @@ type LocalContrailEndpointRepository interface { // TODO: This interface can be simplified type Controller interface { + CreateNetworkWithSubnet(tenantName, networkName, subnetCIDR string) (*types.VirtualNetwork, error) + GetNetworkWithSubnet(tenantName, networkName, subnetCIDR string) (*types.VirtualNetwork, *types.IpamSubnetType, error) + + CreateContainerInSubnet(tenantName, containerID string, network *types.VirtualNetwork, subnet *types.IpamSubnetType) (*ContrailContainer, error) + + // This method is only used by tests; to remove? NewProject(domain, tenant string) (*types.Project, error) - CreateNetworkWithSubnet(tenantName, networkName, subnetCIDR string) (*types.VirtualNetwork, error) + // To remove when refactoring plugin.DeleteEndpoint GetNetwork(tenantName, networkName string) (*types.VirtualNetwork, error) - GetIpamSubnet(net *types.VirtualNetwork, CIDR string) (*types.IpamSubnetType, error) - GetDefaultGatewayIp(subnet *types.IpamSubnetType) (string, error) - - GetOrCreateInstance(vif *types.VirtualMachineInterface, containerId string) (*types.VirtualMachine, error) GetInstance(containerId string) (*types.VirtualMachine, error) - GetExistingInterface(net *types.VirtualNetwork, tenantName, containerId string) (*types.VirtualMachineInterface, error) - GetOrCreateInterface(net *types.VirtualNetwork, tenantName, containerId string) (*types.VirtualMachineInterface, error) - GetInterfaceMac(iface *types.VirtualMachineInterface) (string, error) - - GetOrCreateInstanceIp(net *types.VirtualNetwork, iface *types.VirtualMachineInterface, - subnetUuid string) (*types.InstanceIp, error) - DeleteElementRecursive(parent contrail.IObject) error } diff --git a/main.go b/main.go index a236ed4..d9323c7 100644 --- a/main.go +++ b/main.go @@ -154,7 +154,9 @@ func (ws *WinService) Execute(args []string, winChangeReqChan <-chan svc.ChangeR epRepo := &hns.HNSEndpointRepository{} - core, err := driver_core.NewContrailDriverCore(vrouter, controller, agent, netRepo, epRepo) + hnsWorkaroundSleepTime := time.Second * 5 + core, err := driver_core.NewContrailDriverCore(vrouter, controller, agent, netRepo, epRepo, + hnsWorkaroundSleepTime) if err != nil { log.Error(err) return