|
14 | 14 | package network |
15 | 15 |
|
16 | 16 | import ( |
| 17 | + "strings" |
17 | 18 | "time" |
18 | 19 |
|
19 | 20 | "github.com/docker/docker/api/types" |
| 21 | + "github.com/sirupsen/logrus" |
20 | 22 | "golang.org/x/net/context" |
21 | 23 | ) |
22 | 24 |
|
23 | | -// LocalEndpointsStopper groups the Docker NetworkInspect, ContainerStop, and NetworkRemove functions. |
| 25 | +// LocalEndpointsStopper groups the Docker NetworkInspect, ContainerStop, ContainerRemove, and NetworkRemove functions. |
24 | 26 | // |
25 | | -// These functions can be used together to remove a network once unwanted containers in the network are stopped. |
| 27 | +// These functions can be used together to remove a network once unwanted containers are stopped. |
26 | 28 | type LocalEndpointsStopper interface { |
| 29 | + networkInspector |
| 30 | + containerStopper |
| 31 | + containerRemover |
| 32 | + networkRemover |
| 33 | +} |
| 34 | + |
| 35 | +type networkInspector interface { |
27 | 36 | NetworkInspect(ctx context.Context, networkID string, options types.NetworkInspectOptions) (types.NetworkResource, error) |
| 37 | +} |
| 38 | + |
| 39 | +type containerStopper interface { |
28 | 40 | ContainerStop(ctx context.Context, containerID string, timeout *time.Duration) error |
| 41 | +} |
| 42 | + |
| 43 | +type containerRemover interface { |
| 44 | + ContainerRemove(ctx context.Context, containerID string, options types.ContainerRemoveOptions) error |
| 45 | +} |
| 46 | + |
| 47 | +type networkRemover interface { |
29 | 48 | NetworkRemove(ctx context.Context, networkID string) error |
30 | 49 | } |
31 | 50 |
|
32 | | -// Teardown stops the Local Endpoints container and removes the Local network created by Setup. |
| 51 | +// Teardown removes both the Local Endpoints container and the Local network created by Setup. |
33 | 52 | // If there are other containers running in the network besides the endpoints container, this function does nothing. |
34 | 53 | // |
35 | 54 | // If there is any unexpected errors, we exit the program with a fatal log. |
36 | | -func Teardown(dockerClient *LocalEndpointsStopper) { |
| 55 | +func Teardown(dockerClient LocalEndpointsStopper) { |
| 56 | + if hasRunningTasksInNetwork(dockerClient) { |
| 57 | + return |
| 58 | + } |
| 59 | + logrus.Infof("The network %s has no more running tasks, stopping the endpoints containers...", EcsLocalNetworkName) |
| 60 | + |
| 61 | + stopEndpointsContainer(dockerClient) |
| 62 | + removeEndpointsContainer(dockerClient) |
| 63 | + removeLocalNetwork(dockerClient) |
| 64 | +} |
| 65 | + |
| 66 | +// hasRunningTasksInNetwork returns true if there are other containers besides the |
| 67 | +// endpoints container running in the local network, false otherwise. |
| 68 | +func hasRunningTasksInNetwork(d networkInspector) bool { |
| 69 | + ctx, cancel := context.WithTimeout(context.Background(), dockerTimeout) |
| 70 | + defer cancel() |
| 71 | + |
| 72 | + resp, err := d.NetworkInspect(ctx, EcsLocalNetworkName, types.NetworkInspectOptions{}) |
| 73 | + if err != nil { |
| 74 | + logrus.Fatalf("Failed to inspect network %s due to %v", EcsLocalNetworkName, err) |
| 75 | + } |
| 76 | + |
| 77 | + if len(resp.Containers) > 1 { |
| 78 | + // Has other containers running in the network |
| 79 | + return true |
| 80 | + } |
| 81 | + |
| 82 | + for _, container := range resp.Containers { |
| 83 | + if container.Name != localEndpointsContainerName { |
| 84 | + // The only container running in the network is a task without the endpoints container. |
| 85 | + // This scenario should not happen unless the user themselves stopped the endpoints container. |
| 86 | + logrus.Warnf("The %s container is running in the %s network without the %s container, please stop it first", |
| 87 | + container.Name, EcsLocalNetworkName, localEndpointsContainerName) |
| 88 | + return true |
| 89 | + } |
| 90 | + } |
| 91 | + |
| 92 | + return false |
| 93 | +} |
| 94 | + |
| 95 | +func stopEndpointsContainer(d containerStopper) { |
| 96 | + ctx, cancel := context.WithTimeout(context.Background(), dockerTimeout) |
| 97 | + defer cancel() |
| 98 | + |
| 99 | + err := d.ContainerStop(ctx, localEndpointsContainerName, nil) |
| 100 | + if err != nil { |
| 101 | + if strings.Contains(strings.ToLower(err.Error()), "no such container") { |
| 102 | + // The container was removed, do nothing. |
| 103 | + return |
| 104 | + } |
| 105 | + logrus.Fatalf("Failed to stop %s container due to %v", localEndpointsContainerName, err) |
| 106 | + } |
| 107 | + logrus.Infof("Stopped the %s container successfully, removing it...", localEndpointsContainerName) |
| 108 | +} |
| 109 | + |
| 110 | +// removeEndpointsContainer removes the endpoints container. |
| 111 | +// |
| 112 | +// If we do not remove the container, then the user will receive a "network not found" error on using "local up". |
| 113 | +// Here is a sample scenario: |
| 114 | +// 1) User runs "local up" and creates a new local network with an endpoints container. |
| 115 | +// 2) User runs "local down" and stops the endpoints container but does not remove it, however the network is removed. |
| 116 | +// 3) User runs "local up" again and creates a new local network but re-starts the old endpoints container. |
| 117 | +// The old endpoints container tries to connect to the network created in step 1) and fails. |
| 118 | +func removeEndpointsContainer(d containerRemover) { |
| 119 | + ctx, cancel := context.WithTimeout(context.Background(), dockerTimeout) |
| 120 | + defer cancel() |
| 121 | + |
| 122 | + err := d.ContainerRemove(ctx, localEndpointsContainerName, types.ContainerRemoveOptions{}) |
| 123 | + if err != nil { |
| 124 | + if strings.Contains(strings.ToLower(err.Error()), "no such container") { |
| 125 | + // The container was removed, do nothing. |
| 126 | + return |
| 127 | + } |
| 128 | + logrus.Fatalf("Failed to remove %s container due to %v", localEndpointsContainerName, err) |
| 129 | + } |
| 130 | + logrus.Infof("Removed the %s container successfully, removing the %s network...", |
| 131 | + localEndpointsContainerName, EcsLocalNetworkName) |
| 132 | +} |
| 133 | + |
| 134 | +func removeLocalNetwork(d networkRemover) { |
| 135 | + ctx, cancel := context.WithTimeout(context.Background(), dockerTimeout) |
| 136 | + defer cancel() |
37 | 137 |
|
| 138 | + err := d.NetworkRemove(ctx, EcsLocalNetworkName) |
| 139 | + if err != nil { |
| 140 | + if strings.Contains(strings.ToLower(err.Error()), "no such network") { |
| 141 | + // The network was removed, do nothing. |
| 142 | + return |
| 143 | + } |
| 144 | + logrus.Fatalf("Failed to remove %s network due to %v", EcsLocalNetworkName, err) |
| 145 | + } |
| 146 | + logrus.Infof("Removed the %s network successfully", EcsLocalNetworkName) |
38 | 147 | } |
0 commit comments