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