github.com/moby/docker@v26.1.3+incompatible/libnetwork/sandbox_store.go (about) 1 package libnetwork 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "sync" 8 9 "github.com/containerd/log" 10 "github.com/docker/docker/libnetwork/datastore" 11 "github.com/docker/docker/libnetwork/osl" 12 "github.com/docker/docker/libnetwork/scope" 13 ) 14 15 const ( 16 sandboxPrefix = "sandbox" 17 ) 18 19 type epState struct { 20 Eid string 21 Nid string 22 } 23 24 type sbState struct { 25 ID string 26 Cid string 27 c *Controller 28 dbIndex uint64 29 dbExists bool 30 Eps []epState 31 EpPriority map[string]int 32 // external servers have to be persisted so that on restart of a live-restore 33 // enabled daemon we get the external servers for the running containers. 34 // 35 // It is persisted as "ExtDNS2" for historical reasons. ExtDNS2 was used to 36 // handle migration between docker < 1.14 and >= 1.14. Before version 1.14 we 37 // used ExtDNS but with a []string. As it's unlikely that installations still 38 // have state from before 1.14, we've dropped the migration code. 39 ExtDNS []extDNSEntry `json:"ExtDNS2"` 40 } 41 42 func (sbs *sbState) Key() []string { 43 return []string{sandboxPrefix, sbs.ID} 44 } 45 46 func (sbs *sbState) KeyPrefix() []string { 47 return []string{sandboxPrefix} 48 } 49 50 func (sbs *sbState) Value() []byte { 51 b, err := json.Marshal(sbs) 52 if err != nil { 53 return nil 54 } 55 return b 56 } 57 58 func (sbs *sbState) SetValue(value []byte) error { 59 return json.Unmarshal(value, sbs) 60 } 61 62 func (sbs *sbState) Index() uint64 { 63 sb, err := sbs.c.SandboxByID(sbs.ID) 64 if err != nil { 65 return sbs.dbIndex 66 } 67 68 maxIndex := sb.dbIndex 69 if sbs.dbIndex > maxIndex { 70 maxIndex = sbs.dbIndex 71 } 72 73 return maxIndex 74 } 75 76 func (sbs *sbState) SetIndex(index uint64) { 77 sbs.dbIndex = index 78 sbs.dbExists = true 79 80 sb, err := sbs.c.SandboxByID(sbs.ID) 81 if err != nil { 82 return 83 } 84 85 sb.dbIndex = index 86 sb.dbExists = true 87 } 88 89 func (sbs *sbState) Exists() bool { 90 if sbs.dbExists { 91 return sbs.dbExists 92 } 93 94 sb, err := sbs.c.SandboxByID(sbs.ID) 95 if err != nil { 96 return false 97 } 98 99 return sb.dbExists 100 } 101 102 func (sbs *sbState) Skip() bool { 103 return false 104 } 105 106 func (sbs *sbState) New() datastore.KVObject { 107 return &sbState{c: sbs.c} 108 } 109 110 func (sbs *sbState) CopyTo(o datastore.KVObject) error { 111 dstSbs := o.(*sbState) 112 dstSbs.c = sbs.c 113 dstSbs.ID = sbs.ID 114 dstSbs.Cid = sbs.Cid 115 dstSbs.dbIndex = sbs.dbIndex 116 dstSbs.dbExists = sbs.dbExists 117 dstSbs.EpPriority = sbs.EpPriority 118 119 dstSbs.Eps = append(dstSbs.Eps, sbs.Eps...) 120 dstSbs.ExtDNS = append(dstSbs.ExtDNS, sbs.ExtDNS...) 121 122 return nil 123 } 124 125 func (sb *Sandbox) storeUpdate() error { 126 sbs := &sbState{ 127 c: sb.controller, 128 ID: sb.id, 129 Cid: sb.containerID, 130 EpPriority: sb.epPriority, 131 ExtDNS: sb.extDNS, 132 } 133 134 retry: 135 sbs.Eps = nil 136 for _, ep := range sb.Endpoints() { 137 // If the endpoint is not persisted then do not add it to 138 // the sandbox checkpoint 139 if ep.Skip() { 140 continue 141 } 142 143 sbs.Eps = append(sbs.Eps, epState{ 144 Nid: ep.getNetwork().ID(), 145 Eid: ep.ID(), 146 }) 147 } 148 149 err := sb.controller.updateToStore(sbs) 150 if err == datastore.ErrKeyModified { 151 // When we get ErrKeyModified it is sufficient to just 152 // go back and retry. No need to get the object from 153 // the store because we always regenerate the store 154 // state from in memory sandbox state 155 goto retry 156 } 157 158 return err 159 } 160 161 func (sb *Sandbox) storeDelete() error { 162 cs := sb.controller.getStore() 163 if cs == nil { 164 return fmt.Errorf("datastore is not initialized") 165 } 166 167 return cs.DeleteObject(&sbState{ 168 c: sb.controller, 169 ID: sb.id, 170 Cid: sb.containerID, 171 dbExists: sb.dbExists, 172 }) 173 } 174 175 func (c *Controller) sandboxCleanup(activeSandboxes map[string]interface{}) error { 176 store := c.getStore() 177 if store == nil { 178 return fmt.Errorf("could not find local scope store") 179 } 180 181 sandboxStates, err := store.List(&sbState{c: c}) 182 if err != nil { 183 if err == datastore.ErrKeyNotFound { 184 // It's normal for no sandboxes to be found. Just bail out. 185 return nil 186 } 187 return fmt.Errorf("failed to get sandboxes: %v", err) 188 } 189 190 for _, s := range sandboxStates { 191 sbs := s.(*sbState) 192 sb := &Sandbox{ 193 id: sbs.ID, 194 controller: sbs.c, 195 containerID: sbs.Cid, 196 extDNS: sbs.ExtDNS, 197 endpoints: []*Endpoint{}, 198 populatedEndpoints: map[string]struct{}{}, 199 dbIndex: sbs.dbIndex, 200 isStub: true, 201 dbExists: true, 202 } 203 204 msg := " for cleanup" 205 create := true 206 isRestore := false 207 if val, ok := activeSandboxes[sb.ID()]; ok { 208 msg = "" 209 sb.isStub = false 210 isRestore = true 211 opts := val.([]SandboxOption) 212 sb.processOptions(opts...) 213 sb.restoreHostsPath() 214 sb.restoreResolvConfPath() 215 create = !sb.config.useDefaultSandBox 216 } 217 sb.osSbox, err = osl.NewSandbox(sb.Key(), create, isRestore) 218 if err != nil { 219 log.G(context.TODO()).Errorf("failed to create osl sandbox while trying to restore sandbox %.7s%s: %v", sb.ID(), msg, err) 220 continue 221 } 222 223 c.mu.Lock() 224 c.sandboxes[sb.id] = sb 225 c.mu.Unlock() 226 227 for _, eps := range sbs.Eps { 228 n, err := c.getNetworkFromStore(eps.Nid) 229 var ep *Endpoint 230 if err != nil { 231 log.G(context.TODO()).Errorf("getNetworkFromStore for nid %s failed while trying to build sandbox for cleanup: %v", eps.Nid, err) 232 ep = &Endpoint{ 233 id: eps.Eid, 234 network: &Network{ 235 id: eps.Nid, 236 ctrlr: c, 237 drvOnce: &sync.Once{}, 238 persist: true, 239 }, 240 sandboxID: sbs.ID, 241 } 242 } else { 243 ep, err = n.getEndpointFromStore(eps.Eid) 244 if err != nil { 245 log.G(context.TODO()).Errorf("getEndpointFromStore for eid %s failed while trying to build sandbox for cleanup: %v", eps.Eid, err) 246 ep = &Endpoint{ 247 id: eps.Eid, 248 network: n, 249 sandboxID: sbs.ID, 250 } 251 } 252 } 253 if _, ok := activeSandboxes[sb.ID()]; ok && err != nil { 254 log.G(context.TODO()).Errorf("failed to restore endpoint %s in %s for container %s due to %v", eps.Eid, eps.Nid, sb.ContainerID(), err) 255 continue 256 } 257 sb.addEndpoint(ep) 258 } 259 260 if _, ok := activeSandboxes[sb.ID()]; !ok { 261 log.G(context.TODO()).Infof("Removing stale sandbox %s (%s)", sb.id, sb.containerID) 262 if err := sb.delete(true); err != nil { 263 log.G(context.TODO()).Errorf("Failed to delete sandbox %s while trying to cleanup: %v", sb.id, err) 264 } 265 continue 266 } 267 268 // reconstruct osl sandbox field 269 if !sb.config.useDefaultSandBox { 270 if err := sb.restoreOslSandbox(); err != nil { 271 log.G(context.TODO()).Errorf("failed to populate fields for osl sandbox %s: %v", sb.ID(), err) 272 continue 273 } 274 } else { 275 // FIXME(thaJeztah): osSbox (and thus defOsSbox) is always nil on non-Linux: move this code to Linux-only files. 276 c.defOsSboxOnce.Do(func() { 277 c.defOsSbox = sb.osSbox 278 }) 279 } 280 281 for _, ep := range sb.endpoints { 282 if !c.isAgent() { 283 n := ep.getNetwork() 284 if !c.isSwarmNode() || n.Scope() != scope.Swarm || !n.driverIsMultihost() { 285 n.updateSvcRecord(ep, true) 286 } 287 } 288 } 289 } 290 291 return nil 292 }