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 }