Skip to content

Commit

Permalink
Move DeleteEndpoint to core.
Browse files Browse the repository at this point in the history
This is the last place that used docker network metadata.
Our CNM API is therefore pure.

This PR doesn't refactor DeleteEndpoint method yet.

* Moved tests, too.
* Accidentaly fixed DeleteElementRecursive to not use ugly
error message parsing using regex. Now, DeleteContainer
method will remove all resources allocated by us. This
fixed one of skipped controller client tests. Old function
is still used in some integration tests, so I moved it to
helpers.
* HNS endpoint repositories now find endpoints by names.
* facade.go and api.go files were moved to a different directory due to Windows case-insensitivity. Sorry.

Change-Id: I95c90a95190675d8d1fefa992998331606483418
Partial-Bug: #1780983
  • Loading branch information
Michal Kostrzewa committed Aug 1, 2018
1 parent fd61b2a commit 292cddc
Show file tree
Hide file tree
Showing 11 changed files with 284 additions and 171 deletions.
101 changes: 4 additions & 97 deletions adapters/primary/cnm/cnm.go
Expand Up @@ -26,13 +26,9 @@ import (
"os"
"time"

"context"

"github.com/Juniper/contrail-windows-docker-driver/common"
"github.com/Juniper/contrail-windows-docker-driver/core/driver_core"
winio "github.com/Microsoft/go-winio"
dockerTypes "github.com/docker/docker/api/types"
dockerClient "github.com/docker/docker/client"
"github.com/docker/go-connections/sockets"
"github.com/docker/go-plugins-helpers/network"
"github.com/docker/libnetwork/netlabel"
Expand Down Expand Up @@ -280,66 +276,15 @@ func (d *ServerCNM) DeleteEndpoint(req *network.DeleteEndpointRequest) error {
log.Debugln("=== DeleteEndpoint")
log.Debugln(req)

// TODO JW-187.
// We need something like:
// containerID := req.Options["vmname"]
containerID := req.EndpointID

// TODO: remove this function and call, so that we don't have to rely on network meta from
// docker. We want pure CNM plugin, not one that relies on docker running on local host.
meta, err := d.networkMetaFromDockerNetwork(req.NetworkID)
if err != nil {
return err
}

contrailNetwork, err := d.Core.Controller.GetNetwork(meta.tenant, meta.network)
if err != nil {
return err
}
log.Infoln("Retrieved Contrail network:", contrailNetwork.GetUuid())

contrailVif, err := d.Core.Controller.GetExistingInterface(contrailNetwork, meta.tenant,
containerID)
if err != nil {
log.Warn("When handling DeleteEndpoint, interface wasn't found")
} else {
go func() {
err := d.Core.Agent.DeletePort(contrailVif.GetUuid())
if err != nil {
log.Error(err.Error())
}
}()
}

contrailInstance, err := d.Core.Controller.GetInstance(containerID)
if err != nil {
log.Warn("When handling DeleteEndpoint, Contrail vm instance wasn't found")
} else {
err = d.Core.Controller.DeleteElementRecursive(contrailInstance)
if err != nil {
log.Warn("When handling DeleteEndpoint, failed to remove Contrail vm instance")
}
}

hnsEpName := req.EndpointID
epToDelete, err := d.Core.LocalContrailEndpointsRepo.GetEndpointByName(hnsEpName)
if err != nil {
return err
}
if epToDelete == nil {
log.Warn("When handling DeleteEndpoint, couldn't find HNS endpoint to delete")
return nil
}

return d.Core.LocalContrailEndpointsRepo.DeleteEndpoint(epToDelete.Id)
return d.Core.DeleteEndpoint(req.NetworkID, req.EndpointID)
}

func (d *ServerCNM) EndpointInfo(req *network.InfoRequest) (*network.InfoResponse, error) {
log.Debugln("=== EndpointInfo")
log.Debugln(req)

hnsEpName := req.EndpointID
hnsEp, err := d.Core.LocalContrailEndpointsRepo.GetEndpointByName(hnsEpName)
hnsEp, err := d.Core.LocalContrailEndpointsRepo.GetEndpoint(hnsEpName)
if err != nil {
return nil, err
}
Expand All @@ -366,7 +311,7 @@ func (d *ServerCNM) Join(req *network.JoinRequest) (*network.JoinResponse, error
fmt.Printf("%v: %v\n", k, v)
}

hnsEp, err := d.Core.LocalContrailEndpointsRepo.GetEndpointByName(req.EndpointID)
hnsEp, err := d.Core.LocalContrailEndpointsRepo.GetEndpoint(req.EndpointID)
if err != nil {
return nil, err
}
Expand All @@ -386,7 +331,7 @@ func (d *ServerCNM) Leave(req *network.LeaveRequest) error {
log.Debugln("=== Leave")
log.Debugln(req)

hnsEp, err := d.Core.LocalContrailEndpointsRepo.GetEndpointByName(req.EndpointID)
hnsEp, err := d.Core.LocalContrailEndpointsRepo.GetEndpoint(req.EndpointID)
if err != nil {
return err
}
Expand Down Expand Up @@ -475,41 +420,3 @@ func (d *ServerCNM) waitUntilPipeDialable() error {
time.Sleep(common.PipePollingRate)
}
}

func (d *ServerCNM) networkMetaFromDockerNetwork(dockerNetID string) (*NetworkMeta,
error) {
docker, err := dockerClient.NewEnvClient()
if err != nil {
return nil, err
}

inspectOptions := dockerTypes.NetworkInspectOptions{
Scope: "",
Verbose: false,
}
dockerNetwork, err := docker.NetworkInspect(context.Background(), dockerNetID, inspectOptions)
if err != nil {
return nil, err
}

var meta NetworkMeta
var exists bool

meta.tenant, exists = dockerNetwork.Options["tenant"]
if !exists {
return nil, errors.New("Retrieved network has no Contrail tenant specified")
}

meta.network, exists = dockerNetwork.Options["network"]
if !exists {
return nil, errors.New("Retrieved network has no Contrail network name specfied")
}

ipamCfg := dockerNetwork.IPAM.Config
if len(ipamCfg) == 0 {
return nil, errors.New("No configured subnets in docker network")
}
meta.subnetCIDR = ipamCfg[0].Subnet

return &meta, nil
}
94 changes: 53 additions & 41 deletions adapters/secondary/controller_REST/controller.go
Expand Up @@ -19,9 +19,8 @@ import (
"errors"
"fmt"
"net/http"
"regexp"
"strings"
"strconv"
"strings"

contrail "github.com/Juniper/contrail-go-api"
"github.com/Juniper/contrail-go-api/config"
Expand Down Expand Up @@ -339,45 +338,58 @@ func (c *ControllerAdapterImpl) GetOrCreateInstanceIp(net *types.VirtualNetwork,
return allocatedIP, nil
}

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) {
if c.isResourceNotFound(err) {
log.Errorln("Failed to delete Contrail resource", err.Error())
break
} else if strings.Contains(err.Error(), "409 Conflict") {
msg := err.Error()
// example error message when object has children:
// `409 Conflict: Delete when children still present:
// ['http://10.7.0.54:8082/virtual-network/23e300f4-ab1a-4d97-a1d9-9ed69b601e17']`

// This regex finds all strings like:
// `virtual-network/23e300f4-ab1a-4d97-a1d9-9ed69b601e17`
var re *regexp.Regexp
re, err = regexp.Compile(
"([a-z-]*\\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})")
if err != nil {
return err
}
found := re.FindAll([]byte(msg), -1)

for _, f := range found {
split := strings.Split(string(f), "/")
typename := split[0]
UUID := split[1]
var child contrail.IObject
child, err = c.ApiClient.FindByUuid(typename, UUID)
if err != nil {
return err
}
if child == nil {
return errors.New("Child object is nil")
}
err = c.DeleteElementRecursive(child)
if err != nil {
return err
}
}
func (c *ControllerAdapterImpl) DeleteContainer(containerID string) error {
log.Debugln("Starting delete procedure of container and related resources", containerID)
container, err := types.VirtualMachineByName(c.ApiClient, containerID)
if err != nil {
return err
}

if err := c.removeVMIBackRefsOf(container); err != nil {
return err
}

log.Debugln("Deleting virtual-machine", container.GetName(), container.GetUuid())
return c.ApiClient.DeleteByUuid("virtual-machine", container.GetUuid())
}

func (c *ControllerAdapterImpl) removeVMIBackRefsOf(container *types.VirtualMachine) error {
vmiRefs, err := container.GetVirtualMachineInterfaceBackRefs()
if err != nil {
return err
}
if len(vmiRefs) > 0 {
log.Debugln("Container has virtual-machine-interface refs")
}
for _, vmiRef := range vmiRefs {
vmi, err := c.ApiClient.FindByUuid("virtual-machine-interface", vmiRef.Uuid)
if err != nil {
return err
}
if err := c.removeInstanceIPBackRefsOf(vmi.(*types.VirtualMachineInterface)); err != nil {
return err
}
log.Debugln("Deleting virtual-machine-interface", vmi.GetUuid())
err = c.ApiClient.DeleteByUuid("virtual-machine-interface", vmi.GetUuid())
if err != nil {
return err
}
}
return nil
}

func (c *ControllerAdapterImpl) removeInstanceIPBackRefsOf(vmi *types.VirtualMachineInterface) error {
iipRefs, err := vmi.GetInstanceIpBackRefs()
if err != nil {
return err
}
if len(iipRefs) > 0 {
log.Debugln("virtual-machine-interaface has instance-ip refs")
}
for _, iipRef := range iipRefs {
log.Debugln("Deleting instance-ip", iipRef.Uuid)
if err := c.ApiClient.DeleteByUuid("instance-ip", iipRef.Uuid); err != nil {
return err
}
}
return nil
Expand Down
48 changes: 46 additions & 2 deletions adapters/secondary/controller_REST/controller_helpers_test.go
Expand Up @@ -17,7 +17,10 @@
package controller_rest_test

import (
"errors"
"fmt"
"regexp"
"strings"

contrail "github.com/Juniper/contrail-go-api"
"github.com/Juniper/contrail-go-api/config"
Expand Down Expand Up @@ -149,14 +152,55 @@ func ForceDeleteProject(c *controller_rest.ControllerAdapterImpl, tenant string)
projToDelete, _ := c.ApiClient.FindByName("project", fmt.Sprintf("%s:%s", common.DomainName,
tenant))
if projToDelete != nil {
c.DeleteElementRecursive(projToDelete)
ForceDeleteElementRecursive(c, projToDelete)
}
}

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())
c.DeleteElementRecursive(instance)
ForceDeleteElementRecursive(c, instance)
}
}

// This regex finds all strings like:
// `virtual-network/23e300f4-ab1a-4d97-a1d9-9ed69b601e17`
var resourceUriRegexp = regexp.MustCompile(
"([a-z-]*\\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})")

func ForceDeleteElementRecursive(c *controller_rest.ControllerAdapterImpl, 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.
if strings.Contains(err.Error(), "404 Resource") {
log.Errorln("Failed to delete Contrail resource", err.Error())
break
} else if strings.Contains(err.Error(), "409 Conflict") {
msg := err.Error()
// example error message when object has children:
// `409 Conflict: Delete when children still present:
// ['http://10.7.0.54:8082/virtual-network/23e300f4-ab1a-4d97-a1d9-9ed69b601e17']`
found := resourceUriRegexp.FindAll([]byte(msg), -1)

for _, f := range found {
split := strings.Split(string(f), "/")
typename := split[0]
UUID := split[1]
var child contrail.IObject
child, err = c.ApiClient.FindByUuid(typename, UUID)
if err != nil {
return err
}
if child == nil {
return errors.New("Child object is nil")
}
err = ForceDeleteElementRecursive(c, child)
if err != nil {
return err
}
}
}
}
return nil
}
6 changes: 1 addition & 5 deletions adapters/secondary/controller_REST/controller_test.go
Expand Up @@ -112,10 +112,6 @@ var _ = Describe("ControllerAdapterImpl", func() {
})

Specify("recursive deletion removes elements down the ref tree", func() {
if !useActualController {
Skip("test fails (pending) when using mocked client")
}

testNetwork := CreateTestNetworkWithSubnet(client.ApiClient, networkName, subnetCIDR,
project)
testInterface := CreateMockedInterface(client.ApiClient, testNetwork, tenantName,
Expand All @@ -124,7 +120,7 @@ var _ = Describe("ControllerAdapterImpl", func() {
testInstanceIP := CreateTestInstanceIP(client.ApiClient, tenantName, testInterface,
testNetwork)

err := client.DeleteElementRecursive(testInstance)
err := client.DeleteContainer(containerID)
Expect(err).ToNot(HaveOccurred())

_, err = client.ApiClient.FindByUuid(testNetwork.GetType(), testNetwork.GetUuid())
Expand Down
Expand Up @@ -19,7 +19,6 @@ import (
"errors"
"net"

contrail "github.com/Juniper/contrail-go-api"
"github.com/Juniper/contrail-go-api/types"
"github.com/Juniper/contrail-windows-docker-driver/core/model"
log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -126,6 +125,6 @@ func (c *ControllerAdapter) GetExistingInterface(net *types.VirtualNetwork, tena
return c.controller.GetExistingInterface(net, tenantName, containerId)
}

func (c *ControllerAdapter) DeleteElementRecursive(parent contrail.IObject) error {
return c.controller.DeleteElementRecursive(parent)
func (c *ControllerAdapter) DeleteContainer(containerID string) error {
return c.controller.DeleteContainer(containerID)
}
10 changes: 7 additions & 3 deletions adapters/secondary/local_networking/hns/endpoint_repository.go
Expand Up @@ -40,10 +40,14 @@ func (repo *HNSEndpointRepository) CreateEndpoint(name string, container *model.
return CreateHNSEndpoint(configuration)
}

func (repo *HNSEndpointRepository) GetEndpointByName(name string) (*hcsshim.HNSEndpoint, error) {
func (repo *HNSEndpointRepository) GetEndpoint(name string) (*hcsshim.HNSEndpoint, error) {
return GetHNSEndpointByName(name)
}

func (repo *HNSEndpointRepository) DeleteEndpoint(endpointID string) error {
return DeleteHNSEndpoint(endpointID)
func (repo *HNSEndpointRepository) DeleteEndpoint(name string) error {
ep, err := repo.GetEndpoint(name)
if err != nil {
return err
}
return DeleteHNSEndpoint(ep.Id)
}
Expand Up @@ -40,14 +40,19 @@ func (repo *InMemEndpointRepository) CreateEndpoint(name string, container *mode
return "123", nil
}

func (repo *InMemEndpointRepository) GetEndpointByName(name string) (*hcsshim.HNSEndpoint, error) {
func (repo *InMemEndpointRepository) GetEndpoint(name string) (*hcsshim.HNSEndpoint, error) {
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 {
return errors.New("Not implemented yet")
func (repo *InMemEndpointRepository) DeleteEndpoint(name string) error {
if _, exists := repo.endpoints[name]; exists {
delete(repo.endpoints, name)
return nil
} else {
return errors.New("endpoint not found, so couldn't delete it")
}
}

0 comments on commit 292cddc

Please sign in to comment.