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 }