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