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