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