gitlab.com/postgres-ai/database-lab/v3@v3.0.3/pkg/util/networks/networks.go (about)

     1  /*
     2  2021 © Postgres.ai
     3  */
     4  
     5  // Package networks describes custom network elements.
     6  package networks
     7  
     8  import (
     9  	"context"
    10  	"fmt"
    11  
    12  	"github.com/docker/docker/api/types"
    13  	"github.com/docker/docker/api/types/network"
    14  	"github.com/docker/docker/client"
    15  
    16  	"gitlab.com/postgres-ai/database-lab/v3/pkg/log"
    17  )
    18  
    19  const (
    20  	// DLEApp contains name of the Database Lab Engine network.
    21  	DLEApp = "DLE"
    22  
    23  	// InternalType contains name of the internal network type.
    24  	InternalType = "internal"
    25  
    26  	// networkPrefix defines a distinctive prefix for internal DLE networks.
    27  	networkPrefix = "dle_network_"
    28  )
    29  
    30  // Setup creates a new internal Docker network and connects container to it.
    31  func Setup(ctx context.Context, dockerCLI *client.Client, instanceID, containerName string) (string, error) {
    32  	networkName := getNetworkName(instanceID)
    33  
    34  	log.Dbg("Discovering internal network:", networkName)
    35  
    36  	networkResource, err := dockerCLI.NetworkInspect(ctx, networkName, types.NetworkInspectOptions{})
    37  	if err == nil {
    38  		if !hasContainerConnected(networkResource, containerName) {
    39  			if err := dockerCLI.NetworkConnect(ctx, networkResource.ID, containerName, &network.EndpointSettings{}); err != nil {
    40  				return "", err
    41  			}
    42  
    43  			log.Dbg(fmt.Sprintf("Container %s has been connected to %s", containerName, networkName))
    44  		}
    45  
    46  		return networkResource.ID, nil
    47  	}
    48  
    49  	log.Dbg("Internal network not found:", err.Error())
    50  	log.Dbg("Creating a new internal network:", networkName)
    51  
    52  	internalNetwork, err := dockerCLI.NetworkCreate(ctx, networkName, types.NetworkCreate{
    53  		Labels: map[string]string{
    54  			"instance": instanceID,
    55  			"app":      DLEApp,
    56  			"type":     InternalType,
    57  		},
    58  		Attachable:     true,
    59  		Internal:       true,
    60  		CheckDuplicate: true,
    61  	})
    62  	if err != nil {
    63  		return "", err
    64  	}
    65  
    66  	log.Dbg("A new internal network has been created:", internalNetwork.ID)
    67  
    68  	if err := dockerCLI.NetworkConnect(ctx, internalNetwork.ID, containerName, &network.EndpointSettings{}); err != nil {
    69  		return "", err
    70  	}
    71  
    72  	return internalNetwork.ID, nil
    73  }
    74  
    75  // Stop disconnect all containers from the network and removes it.
    76  func Stop(dockerCLI *client.Client, internalNetworkID, containerName string) {
    77  	log.Dbg("Disconnecting DLE container from the internal network:", containerName)
    78  
    79  	if err := dockerCLI.NetworkDisconnect(context.Background(), internalNetworkID, containerName, true); err != nil {
    80  		log.Errf(err.Error())
    81  		return
    82  	}
    83  
    84  	log.Dbg("DLE container has been disconnected from the internal network:", containerName)
    85  
    86  	networkInspect, err := dockerCLI.NetworkInspect(context.Background(), internalNetworkID, types.NetworkInspectOptions{})
    87  	if err != nil {
    88  		log.Errf(err.Error())
    89  		return
    90  	}
    91  
    92  	if len(networkInspect.Containers) == 0 {
    93  		log.Dbg("No containers connected to the internal network. Removing network:", internalNetworkID)
    94  
    95  		if err := dockerCLI.NetworkRemove(context.Background(), internalNetworkID); err != nil {
    96  			log.Errf(err.Error())
    97  			return
    98  		}
    99  
   100  		log.Dbg("The internal network has been removed:", internalNetworkID)
   101  	}
   102  }
   103  
   104  // Connect connects a container to an internal Docker network.
   105  func Connect(ctx context.Context, dockerCLI *client.Client, instanceID, containerID string) error {
   106  	networkName := getNetworkName(instanceID)
   107  
   108  	log.Dbg("Discovering internal network:", networkName)
   109  
   110  	networkResource, err := dockerCLI.NetworkInspect(ctx, networkName, types.NetworkInspectOptions{})
   111  	if err != nil {
   112  		return fmt.Errorf("internal network not found: %w", err)
   113  	}
   114  
   115  	if !hasContainerConnected(networkResource, containerID) {
   116  		if err := dockerCLI.NetworkConnect(ctx, networkResource.ID, containerID, &network.EndpointSettings{}); err != nil {
   117  			return err
   118  		}
   119  
   120  		log.Dbg(fmt.Sprintf("Container %s has been connected to %s", instanceID, networkName))
   121  	}
   122  
   123  	return nil
   124  }
   125  
   126  // Reconnect connects a container to internal Docker network.
   127  func Reconnect(ctx context.Context, dockerCLI *client.Client, instanceID, containerID string) error {
   128  	log.Dbg(fmt.Sprintf("Reconnect container %s to internal network", containerID))
   129  
   130  	networkName := getNetworkName(instanceID)
   131  
   132  	log.Dbg("Discovering internal network:", networkName)
   133  
   134  	networkResource, err := dockerCLI.NetworkInspect(ctx, networkName, types.NetworkInspectOptions{})
   135  	if err != nil {
   136  		return fmt.Errorf("internal network not found: %w", err)
   137  	}
   138  
   139  	log.Dbg(fmt.Sprintf("Disconnecting container %s from internal network", containerID))
   140  
   141  	if err := dockerCLI.NetworkDisconnect(context.Background(), networkName, containerID, true); err != nil {
   142  		return err
   143  	}
   144  
   145  	log.Dbg(fmt.Sprintf("Container %s has been disconnected from internal network", containerID))
   146  
   147  	if err := dockerCLI.NetworkConnect(ctx, networkResource.ID, containerID, &network.EndpointSettings{}); err != nil {
   148  		return err
   149  	}
   150  
   151  	log.Dbg(fmt.Sprintf("Container %s has been connected to %s", instanceID, networkName))
   152  
   153  	return nil
   154  }
   155  
   156  func hasContainerConnected(networkResource types.NetworkResource, containerID string) bool {
   157  	for _, container := range networkResource.Containers {
   158  		if container.Name == containerID {
   159  			return true
   160  		}
   161  	}
   162  
   163  	return false
   164  }
   165  
   166  func getNetworkName(instanceID string) string {
   167  	return networkPrefix + instanceID
   168  }