github.com/bigcommerce/nomad@v0.9.3-bc/drivers/docker/fingerprint.go (about)

     1  package docker
     2  
     3  import (
     4  	"context"
     5  	"runtime"
     6  	"sort"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/hashicorp/nomad/helper"
    11  	"github.com/hashicorp/nomad/plugins/drivers"
    12  	pstructs "github.com/hashicorp/nomad/plugins/shared/structs"
    13  )
    14  
    15  func (d *Driver) Fingerprint(ctx context.Context) (<-chan *drivers.Fingerprint, error) {
    16  	ch := make(chan *drivers.Fingerprint)
    17  	go d.handleFingerprint(ctx, ch)
    18  	return ch, nil
    19  }
    20  
    21  func (d *Driver) previouslyDetected() bool {
    22  	d.detectedLock.RLock()
    23  	defer d.detectedLock.RUnlock()
    24  
    25  	return d.detected
    26  }
    27  
    28  func (d *Driver) setDetected(detected bool) {
    29  	d.detectedLock.Lock()
    30  	defer d.detectedLock.Unlock()
    31  
    32  	d.detected = detected
    33  }
    34  
    35  // setFingerprintSuccess marks the driver as having fingerprinted successfully
    36  func (d *Driver) setFingerprintSuccess() {
    37  	d.fingerprintLock.Lock()
    38  	d.fingerprintSuccess = helper.BoolToPtr(true)
    39  	d.fingerprintLock.Unlock()
    40  }
    41  
    42  // setFingerprintFailure marks the driver as having failed fingerprinting
    43  func (d *Driver) setFingerprintFailure() {
    44  	d.fingerprintLock.Lock()
    45  	d.fingerprintSuccess = helper.BoolToPtr(false)
    46  	d.fingerprintLock.Unlock()
    47  }
    48  
    49  // fingerprintSuccessful returns true if the driver has
    50  // never fingerprinted or has successfully fingerprinted
    51  func (d *Driver) fingerprintSuccessful() bool {
    52  	d.fingerprintLock.Lock()
    53  	defer d.fingerprintLock.Unlock()
    54  	return d.fingerprintSuccess == nil || *d.fingerprintSuccess
    55  }
    56  
    57  func (d *Driver) handleFingerprint(ctx context.Context, ch chan *drivers.Fingerprint) {
    58  	defer close(ch)
    59  	ticker := time.NewTimer(0)
    60  	for {
    61  		select {
    62  		case <-ctx.Done():
    63  			return
    64  		case <-d.ctx.Done():
    65  			return
    66  		case <-ticker.C:
    67  			ticker.Reset(fingerprintPeriod)
    68  			ch <- d.buildFingerprint()
    69  		}
    70  	}
    71  }
    72  
    73  func (d *Driver) buildFingerprint() *drivers.Fingerprint {
    74  	fp := &drivers.Fingerprint{
    75  		Attributes:        map[string]*pstructs.Attribute{},
    76  		Health:            drivers.HealthStateHealthy,
    77  		HealthDescription: drivers.DriverHealthy,
    78  	}
    79  	client, _, err := d.dockerClients()
    80  	if err != nil {
    81  		if d.fingerprintSuccessful() {
    82  			d.logger.Info("failed to initialize client", "error", err)
    83  		}
    84  		d.setFingerprintFailure()
    85  		return &drivers.Fingerprint{
    86  			Health:            drivers.HealthStateUndetected,
    87  			HealthDescription: "Failed to initialize docker client",
    88  		}
    89  	}
    90  
    91  	env, err := client.Version()
    92  	if err != nil {
    93  		if d.fingerprintSuccessful() {
    94  			d.logger.Debug("could not connect to docker daemon", "endpoint", client.Endpoint(), "error", err)
    95  		}
    96  		d.setFingerprintFailure()
    97  
    98  		result := drivers.HealthStateUndetected
    99  		if d.previouslyDetected() {
   100  			result = drivers.HealthStateUnhealthy
   101  		}
   102  
   103  		return &drivers.Fingerprint{
   104  			Health:            result,
   105  			HealthDescription: "Failed to connect to docker daemon",
   106  		}
   107  	}
   108  
   109  	d.setDetected(true)
   110  	fp.Attributes["driver.docker"] = pstructs.NewBoolAttribute(true)
   111  	fp.Attributes["driver.docker.version"] = pstructs.NewStringAttribute(env.Get("Version"))
   112  	if d.config.AllowPrivileged {
   113  		fp.Attributes["driver.docker.privileged.enabled"] = pstructs.NewBoolAttribute(true)
   114  	}
   115  
   116  	if d.config.Volumes.Enabled {
   117  		fp.Attributes["driver.docker.volumes.enabled"] = pstructs.NewBoolAttribute(true)
   118  	}
   119  
   120  	if nets, err := client.ListNetworks(); err != nil {
   121  		d.logger.Warn("error discovering bridge IP", "error", err)
   122  	} else {
   123  		for _, n := range nets {
   124  			if n.Name != "bridge" {
   125  				continue
   126  			}
   127  
   128  			if len(n.IPAM.Config) == 0 {
   129  				d.logger.Warn("no IPAM config for bridge network")
   130  				break
   131  			}
   132  
   133  			if n.IPAM.Config[0].Gateway != "" {
   134  				fp.Attributes["driver.docker.bridge_ip"] = pstructs.NewStringAttribute(n.IPAM.Config[0].Gateway)
   135  			} else {
   136  				// Docker 17.09.0-ce dropped the Gateway IP from the bridge network
   137  				// See https://github.com/moby/moby/issues/32648
   138  				if d.fingerprintSuccess == nil {
   139  					d.logger.Debug("bridge_ip could not be discovered")
   140  				}
   141  			}
   142  			break
   143  		}
   144  	}
   145  
   146  	if dockerInfo, err := client.Info(); err != nil {
   147  		d.logger.Warn("failed to get Docker system info", "error", err)
   148  	} else {
   149  		runtimeNames := make([]string, 0, len(dockerInfo.Runtimes))
   150  		for name := range dockerInfo.Runtimes {
   151  			if d.config.GPURuntimeName == name {
   152  				// Nvidia runtime is detected by Docker.
   153  				// It makes possible to run GPU workloads using Docker driver on this host.
   154  				d.gpuRuntime = true
   155  			}
   156  			runtimeNames = append(runtimeNames, name)
   157  		}
   158  		sort.Strings(runtimeNames)
   159  
   160  		fp.Attributes["driver.docker.runtimes"] = pstructs.NewStringAttribute(
   161  			strings.Join(runtimeNames, ","))
   162  		fp.Attributes["driver.docker.os_type"] = pstructs.NewStringAttribute(dockerInfo.OSType)
   163  
   164  		if runtime.GOOS == "windows" && dockerInfo.OSType == "linux" {
   165  			if d.fingerprintSuccessful() {
   166  				d.logger.Warn("detected Linux docker containers on Windows; only Windows containers are supported")
   167  			}
   168  
   169  			d.setFingerprintFailure()
   170  			return &drivers.Fingerprint{
   171  				Health:            drivers.HealthStateUnhealthy,
   172  				HealthDescription: "Docker is configured with Linux containers; only Windows containers are supported",
   173  			}
   174  		}
   175  	}
   176  
   177  	d.setFingerprintSuccess()
   178  
   179  	return fp
   180  }