github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/metadata/containers.go (about) 1 /* 2 Copyright 2017 Mirantis 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package metadata 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "errors" 23 "fmt" 24 25 "github.com/boltdb/bolt" 26 27 "github.com/Mirantis/virtlet/pkg/metadata/types" 28 ) 29 30 var ( 31 containersBucket = []byte("containers") 32 containerKeyPrefix = []byte("containers/") 33 ) 34 35 func containerKey(containerID string) []byte { 36 return append(containerKeyPrefix, []byte(containerID)...) 37 } 38 39 type containerMeta struct { 40 client *boltClient 41 id string 42 } 43 44 // GetID returns ID of the container managed by this object 45 func (m containerMeta) GetID() string { 46 return m.id 47 } 48 49 // Retrieve loads from DB and returns container data bound to the object 50 func (m containerMeta) Retrieve() (*types.ContainerInfo, error) { 51 if m.GetID() == "" { 52 return nil, errors.New("Container ID cannot be empty") 53 } 54 var ci *types.ContainerInfo 55 err := m.client.db.View(func(tx *bolt.Tx) error { 56 bucket := tx.Bucket(containersBucket) 57 if bucket == nil { 58 return nil 59 } 60 data := bucket.Get([]byte(m.GetID())) 61 if data == nil { 62 return nil 63 } 64 return json.Unmarshal(data, &ci) 65 }) 66 return ci, err 67 } 68 69 // Save allows to create/modify/delete container data bound to the object. 70 // Supplied handler gets current ContainerInfo value (nil if doesn't exist) and returns new structure 71 // value to be saved or nil to delete. If error value is returned from the handler, the transaction is 72 // rolled back and returned error becomes the result of the function 73 func (m containerMeta) Save(updater func(*types.ContainerInfo) (*types.ContainerInfo, error)) error { 74 if m.GetID() == "" { 75 return errors.New("Container ID cannot be empty") 76 } 77 return m.client.db.Update(func(tx *bolt.Tx) error { 78 bucket, err := tx.CreateBucketIfNotExists(containersBucket) 79 if err != nil { 80 return err 81 } 82 var current *types.ContainerInfo 83 var oldPodID string 84 data := bucket.Get([]byte(m.GetID())) 85 if data != nil { 86 if err = json.Unmarshal(data, ¤t); err != nil { 87 return err 88 } 89 oldPodID = current.Config.PodSandboxID 90 } 91 newData, err := updater(current) 92 if err != nil { 93 return err 94 } 95 96 if current == nil && newData == nil { 97 return nil 98 } 99 100 if newData == nil { 101 if oldPodID != "" { 102 if err = removeContainerFromSandbox(tx, m.GetID(), oldPodID); err != nil { 103 return err 104 } 105 } 106 return bucket.Delete([]byte(m.GetID())) 107 } 108 newData.Id = m.GetID() 109 data, err = json.Marshal(newData) 110 if err != nil { 111 return err 112 } 113 114 if oldPodID != newData.Config.PodSandboxID { 115 if oldPodID != "" { 116 if err = removeContainerFromSandbox(tx, m.GetID(), oldPodID); err != nil { 117 return err 118 } 119 } 120 if newData.Config.PodSandboxID != "" { 121 if err = addContainerToSandbox(tx, m.GetID(), newData.Config.PodSandboxID); err != nil { 122 return err 123 } 124 } 125 } 126 return bucket.Put([]byte(m.GetID()), data) 127 }) 128 } 129 130 func addContainerToSandbox(tx *bolt.Tx, containerID, sandboxID string) error { 131 bucket, err := getSandboxBucket(tx, sandboxID, true, false) 132 if err != nil { 133 return err 134 } 135 return bucket.Put(containerKey(containerID), []byte{}) 136 } 137 138 func removeContainerFromSandbox(tx *bolt.Tx, containerID, sandboxID string) error { 139 bucket, err := getSandboxBucket(tx, sandboxID, false, true) 140 if err != nil { 141 return err 142 } 143 if bucket == nil { 144 return nil 145 } 146 return bucket.Delete(containerKey(containerID)) 147 } 148 149 // Container returns interface instance which manages container with given ID 150 func (b *boltClient) Container(containerID string) ContainerMetadata { 151 return &containerMeta{id: containerID, client: b} 152 } 153 154 // ListPodContainers returns a list of containers that belong to the pod with given ID value 155 func (b *boltClient) ListPodContainers(podID string) ([]ContainerMetadata, error) { 156 if podID == "" { 157 return nil, errors.New("Pod sandbox ID cannot be empty") 158 } 159 var result []ContainerMetadata 160 err := b.db.View(func(tx *bolt.Tx) error { 161 bucket, err := getSandboxBucket(tx, podID, false, false) 162 if err != nil { 163 return err 164 } 165 c := bucket.Cursor() 166 for k, _ := c.Seek(containerKeyPrefix); k != nil && bytes.HasPrefix(k, containerKeyPrefix); k, _ = c.Next() { 167 result = append(result, b.Container(string(k[len(containerKeyPrefix):]))) 168 } 169 return nil 170 }) 171 return result, err 172 } 173 174 // ImagesInUse returns a set of images in use by containers in the store. 175 // The keys of the returned map are image names and the values are always true. 176 func (b *boltClient) ImagesInUse() (map[string]bool, error) { 177 result := make(map[string]bool) 178 if err := b.db.View(func(tx *bolt.Tx) error { 179 c := tx.Cursor() 180 for k, _ := c.Seek(sandboxKeyPrefix); k != nil && bytes.HasPrefix(k, sandboxKeyPrefix); k, _ = c.Next() { 181 containers, err := b.ListPodContainers(string(k[len(sandboxKeyPrefix):])) 182 if err != nil { 183 return err 184 } 185 for _, containerMeta := range containers { 186 ci, err := containerMeta.Retrieve() 187 if err != nil { 188 return err 189 } 190 if ci == nil { 191 return fmt.Errorf("containerInfo of container %q not found in Virtlet metadata store", containerMeta.GetID()) 192 } 193 result[ci.Config.Image] = true 194 } 195 } 196 return nil 197 }); err != nil { 198 return nil, err 199 } 200 return result, nil 201 }