/usr/share/pyshared/juju/charm/bundle.py is in juju-0.7 0.7-0ubuntu2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | import hashlib
import tempfile
import os
import stat
from zipfile import ZipFile, BadZipfile
from juju.charm.base import CharmBase, get_revision
from juju.charm.config import ConfigOptions
from juju.charm.metadata import MetaData
from juju.errors import CharmError
from juju.lib.filehash import compute_file_hash
class CharmBundle(CharmBase):
"""ZIP-archive that contains charm directory content."""
type = "bundle"
def __init__(self, path):
self.path = isinstance(path, file) and path.name or path
try:
zf = ZipFile(path, 'r')
except BadZipfile, exc:
raise CharmError(path, "must be a zip file (%s)" % exc)
if "metadata.yaml" not in zf.namelist():
raise CharmError(
path, "charm does not contain required file 'metadata.yaml'")
self.metadata = MetaData()
self.metadata.parse(zf.read("metadata.yaml"))
try:
revision_content = zf.read("revision")
except KeyError:
revision_content = None
self._revision = get_revision(
revision_content, self.metadata, self.path)
if self._revision is None:
raise CharmError(self.path, "has no revision")
self.config = ConfigOptions()
if "config.yaml" in zf.namelist():
self.config.parse(zf.read("config.yaml"))
def get_revision(self):
return self._revision
def compute_sha256(self):
"""Return the SHA256 digest for this charm bundle.
The digest is extracted out of the final bundle file itself.
"""
return compute_file_hash(hashlib.sha256, self.path)
def extract_to(self, directory_path):
"""Extract the bundle to directory path and return a
CharmDirectory handle"""
from .directory import CharmDirectory
zf = ZipFile(self.path, "r")
for info in zf.infolist():
mode = info.external_attr >> 16
if stat.S_ISLNK(mode):
source = zf.read(info.filename)
target = os.path.join(directory_path, info.filename)
# Support extracting over existing charm.
# TODO: a directory changed to a file needs install manifests
if os.path.exists(target):
os.remove(target)
os.symlink(source, target)
continue
# Preserve mode
extract_path = zf.extract(info, directory_path)
os.chmod(extract_path, mode)
return CharmDirectory(directory_path)
def as_bundle(self):
return self
def as_directory(self):
"""Returns the bundle as a CharmDirectory using a temporary
path"""
dn = tempfile.mkdtemp(prefix="tmp-charm-")
return self.extract_to(dn)
|