github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/daemon/links/links.go (about)

     1  package links
     2  
     3  import (
     4  	"fmt"
     5  	"path"
     6  	"strings"
     7  
     8  	"github.com/docker/go-connections/nat"
     9  )
    10  
    11  // Link struct holds informations about parent/child linked container
    12  type Link struct {
    13  	// Parent container IP address
    14  	ParentIP string
    15  	// Child container IP address
    16  	ChildIP string
    17  	// Link name
    18  	Name string
    19  	// Child environments variables
    20  	ChildEnvironment []string
    21  	// Child exposed ports
    22  	Ports []nat.Port
    23  }
    24  
    25  // NewLink initializes a new Link struct with the provided options.
    26  func NewLink(parentIP, childIP, name string, env []string, exposedPorts map[nat.Port]struct{}) *Link {
    27  	var (
    28  		i     int
    29  		ports = make([]nat.Port, len(exposedPorts))
    30  	)
    31  
    32  	for p := range exposedPorts {
    33  		ports[i] = p
    34  		i++
    35  	}
    36  
    37  	return &Link{
    38  		Name:             name,
    39  		ChildIP:          childIP,
    40  		ParentIP:         parentIP,
    41  		ChildEnvironment: env,
    42  		Ports:            ports,
    43  	}
    44  }
    45  
    46  // ToEnv creates a string's slice containing child container informations in
    47  // the form of environment variables which will be later exported on container
    48  // startup.
    49  func (l *Link) ToEnv() []string {
    50  	env := []string{}
    51  
    52  	_, n := path.Split(l.Name)
    53  	alias := strings.Replace(strings.ToUpper(n), "-", "_", -1)
    54  
    55  	if p := l.getDefaultPort(); p != nil {
    56  		env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port()))
    57  	}
    58  
    59  	//sort the ports so that we can bulk the continuous ports together
    60  	nat.Sort(l.Ports, func(ip, jp nat.Port) bool {
    61  		// If the two ports have the same number, tcp takes priority
    62  		// Sort in desc order
    63  		return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp")
    64  	})
    65  
    66  	for i := 0; i < len(l.Ports); {
    67  		p := l.Ports[i]
    68  		j := nextContiguous(l.Ports, p.Int(), i)
    69  		if j > i+1 {
    70  			env = append(env, fmt.Sprintf("%s_PORT_%s_%s_START=%s://%s:%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto(), l.ChildIP, p.Port()))
    71  			env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP))
    72  			env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto()))
    73  			env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT_START=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port()))
    74  
    75  			q := l.Ports[j]
    76  			env = append(env, fmt.Sprintf("%s_PORT_%s_%s_END=%s://%s:%s", alias, p.Port(), strings.ToUpper(q.Proto()), q.Proto(), l.ChildIP, q.Port()))
    77  			env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT_END=%s", alias, p.Port(), strings.ToUpper(q.Proto()), q.Port()))
    78  
    79  			i = j + 1
    80  			continue
    81  		} else {
    82  			i++
    83  		}
    84  	}
    85  	for _, p := range l.Ports {
    86  		env = append(env, fmt.Sprintf("%s_PORT_%s_%s=%s://%s:%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto(), l.ChildIP, p.Port()))
    87  		env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP))
    88  		env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port()))
    89  		env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto()))
    90  	}
    91  
    92  	// Load the linked container's name into the environment
    93  	env = append(env, fmt.Sprintf("%s_NAME=%s", alias, l.Name))
    94  
    95  	if l.ChildEnvironment != nil {
    96  		for _, v := range l.ChildEnvironment {
    97  			parts := strings.SplitN(v, "=", 2)
    98  			if len(parts) < 2 {
    99  				continue
   100  			}
   101  			// Ignore a few variables that are added during docker build (and not really relevant to linked containers)
   102  			if parts[0] == "HOME" || parts[0] == "PATH" {
   103  				continue
   104  			}
   105  			env = append(env, fmt.Sprintf("%s_ENV_%s=%s", alias, parts[0], parts[1]))
   106  		}
   107  	}
   108  	return env
   109  }
   110  
   111  func nextContiguous(ports []nat.Port, value int, index int) int {
   112  	if index+1 == len(ports) {
   113  		return index
   114  	}
   115  	for i := index + 1; i < len(ports); i++ {
   116  		if ports[i].Int() > value+1 {
   117  			return i - 1
   118  		}
   119  
   120  		value++
   121  	}
   122  	return len(ports) - 1
   123  }
   124  
   125  // Default port rules
   126  func (l *Link) getDefaultPort() *nat.Port {
   127  	var p nat.Port
   128  	i := len(l.Ports)
   129  
   130  	if i == 0 {
   131  		return nil
   132  	} else if i > 1 {
   133  		nat.Sort(l.Ports, func(ip, jp nat.Port) bool {
   134  			// If the two ports have the same number, tcp takes priority
   135  			// Sort in desc order
   136  			return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp")
   137  		})
   138  	}
   139  	p = l.Ports[0]
   140  	return &p
   141  }