Source code for labtest.provider.aws.state

# -*- coding: utf-8 -*-
import os
import json
from fabric.api import env, run
from fabric.contrib.files import exists
from fabric.context_managers import settings
from labtest import filesystem
from labtest.provider.base_state import BaseState


[docs]class NotInCacheException(LookupError): pass
[docs]class S3State(BaseState): """ Implements retrieval of state information from S3 and optionally a local cache """ default_config = { 'cache': True, 'cache_path': '/testing/state/', } def _instantiate_cache(self): """ Create the directory for the cache if it doesn't exist """ if not exists(self.cache_path): filesystem.create_dir(self.cache_path) def _get_from_cache(self, key): """ Looks in the local cache for the key and returns it. Raises exception if not found Args: key: The key to get Raises: NotInCacheException: If the key is not in the cache. This is to differentiate from a successful retrieval of a key from cache with a ``None`` value. Returns: Value of the found key """ if self.cache: self._instantiate_cache() path = os.path.join(self.cache_path, key.lstrip('/')) if exists(path) and not filesystem.is_dir(path): return unicode(filesystem.get_file_contents(path).getvalue()) raise NotInCacheException() def _is_in_s3(self, key): """ Is the key in S3? Args: key: The key Returns: boolean """ key = key.strip('/') cmd = [ 'aws s3api list-objects-v2', '--bucket', self.bucket, '--prefix', key, '--query "Contents[*].[Key][]"', '--output json' ] result = run(' '.join(cmd), quiet=env.quiet) return key in json.loads(result) def _get_local_file_path(self, key): """ Given a key, return the full path in which to store the file If ``cache`` is ``True`` it will be the cache path, otherwise it will be a tmp file Args: key: the key to base the path on Returns: The full path """ if self.cache: path = os.path.join(self.cache_path, key.lstrip('/')) basepath = os.path.dirname(path) filesystem.create_dir(basepath) return path else: return run('mktemp', quiet=env.quiet)
[docs] def get(self, key): """ Get the value of ``key`` from ``bucket`` navigating the hierarchy of the key path Args: key: The key, as a ``/`` delimited path. Returns: The value or ``None`` """ for k in self._calc_search_paths(key): try: return self._get_from_cache(k) except NotInCacheException: if self._is_in_s3(k): break path = self._get_local_file_path(k) # This is the path actually found with settings(warn_only=True): run('aws s3api get-object --bucket {bucket} --key {key} {cache_path}'.format( bucket=self.bucket, key=k.lstrip('/'), cache_path=path), quiet=env.quiet) return unicode(filesystem.get_file_contents(path).getvalue()) return None