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, &current); 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  }