github.com/Equinix-Metal/virtlet@v1.5.2-0.20210807010419-342346535dc5/pkg/metadata/sandboxes.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 "k8s.io/apimachinery/pkg/fields" 27 28 "github.com/Equinix-Metal/virtlet/pkg/metadata/types" 29 ) 30 31 var ( 32 sandboxKeyPrefix = []byte("sandboxes/") 33 sandboxDataBucket = []byte("data") 34 ) 35 36 func sandboxKey(sandboxID string) []byte { 37 return append(sandboxKeyPrefix, []byte(sandboxID)...) 38 } 39 40 type podSandboxMeta struct { 41 client *boltClient 42 id string 43 } 44 45 // GetID returns ID of the pod sandbox managed by this object 46 func (m podSandboxMeta) GetID() string { 47 return m.id 48 } 49 50 // Retrieve loads from DB and returns pod sandbox data bound to the object 51 func (m podSandboxMeta) Retrieve() (*types.PodSandboxInfo, error) { 52 if m.GetID() == "" { 53 return nil, errors.New("Pod sandbox ID cannot be empty") 54 } 55 var psi *types.PodSandboxInfo 56 err := m.client.db.View(func(tx *bolt.Tx) error { 57 bucket, err := getSandboxBucket(tx, m.GetID(), false, false) 58 if err != nil { 59 return err 60 } 61 return retrieveSandboxFromDB(bucket, &psi) 62 }) 63 if err == nil && psi != nil { 64 psi.PodID = m.GetID() 65 } 66 return psi, err 67 } 68 69 // Save allows to create/modify/delete pod sandbox instance bound to the object. 70 // Supplied handler gets current PodSandboxInfo 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 podSandboxMeta) Save(updater func(*types.PodSandboxInfo) (*types.PodSandboxInfo, error)) error { 74 if m.GetID() == "" { 75 return errors.New("Pod sandbox ID cannot be empty") 76 } 77 return m.client.db.Update(func(tx *bolt.Tx) error { 78 key := sandboxKey(m.GetID()) 79 var current *types.PodSandboxInfo 80 bucket, err := getSandboxBucket(tx, m.GetID(), true, false) 81 if err != nil { 82 return err 83 } 84 if err := retrieveSandboxFromDB(bucket, ¤t); err != nil { 85 return err 86 } 87 newData, err := updater(current) 88 if err != nil { 89 return err 90 } 91 92 if newData == nil { 93 return tx.DeleteBucket(key) 94 } 95 return saveSandboxToDB(bucket, newData) 96 }) 97 } 98 99 // PodSandbox returns interface instance which manages pod sandbox with given ID 100 func (b *boltClient) PodSandbox(podID string) PodSandboxMetadata { 101 return &podSandboxMeta{id: podID, client: b} 102 } 103 104 // ListPodSandboxes returns list of pod sandboxes that match given filter 105 func (b *boltClient) ListPodSandboxes(filter *types.PodSandboxFilter) ([]PodSandboxMetadata, error) { 106 var result []PodSandboxMetadata 107 err := b.db.View(func(tx *bolt.Tx) error { 108 c := tx.Cursor() 109 for k, _ := c.Seek(sandboxKeyPrefix); k != nil && bytes.HasPrefix(k, sandboxKeyPrefix); k, _ = c.Next() { 110 psm := podSandboxMeta{client: b, id: string(k[len(sandboxKeyPrefix):])} 111 fv, err := filterPodSandboxMeta(&psm, filter) 112 if err != nil { 113 return err 114 } 115 if fv { 116 result = append(result, psm) 117 } 118 } 119 return nil 120 }) 121 if err != nil { 122 return nil, err 123 } 124 return result, nil 125 } 126 127 func getSandboxBucket(tx *bolt.Tx, podID string, create, optional bool) (*bolt.Bucket, error) { 128 key := sandboxKey(podID) 129 if create { 130 bucket, err := tx.CreateBucketIfNotExists(key) 131 if err != nil { 132 return nil, err 133 } 134 return bucket, nil 135 } 136 bucket := tx.Bucket(key) 137 if bucket == nil && !optional { 138 return nil, fmt.Errorf("pod sandbox %q does not exist", podID) 139 } 140 return bucket, nil 141 } 142 143 func retrieveSandboxFromDB(bucket *bolt.Bucket, psi **types.PodSandboxInfo) error { 144 data := bucket.Get(sandboxDataBucket) 145 if data == nil { 146 return nil 147 } 148 return json.Unmarshal(data, psi) 149 } 150 151 func saveSandboxToDB(bucket *bolt.Bucket, psi *types.PodSandboxInfo) error { 152 data, err := json.Marshal(psi) 153 if err != nil { 154 return err 155 } 156 157 return bucket.Put(sandboxDataBucket, data) 158 } 159 160 func filterPodSandboxMeta(psm PodSandboxMetadata, filter *types.PodSandboxFilter) (bool, error) { 161 if filter == nil { 162 return true, nil 163 } 164 165 if filter.Id != "" && psm.GetID() != filter.Id { 166 return false, nil 167 } 168 169 psi, err := psm.Retrieve() 170 if err != nil { 171 return false, err 172 } 173 if psi == nil { 174 return false, fmt.Errorf("no data found for pod id %q", psm.GetID()) 175 } 176 177 if filter.State != nil && psi.State != *filter.State { 178 return false, nil 179 } 180 181 sel := fields.SelectorFromSet(filter.LabelSelector) 182 if !sel.Matches(fields.Set(psi.Config.Labels)) { 183 return false, nil 184 } 185 186 return true, nil 187 }