def __init__(self, path): """Set initial values and parse configuration files from the charm.""" self.path = path revision_content = None self.metadata = MetaData(os.path.join(path, "metadata.yaml")) revision_path = os.path.join(self.path, "revision") if os.path.exists(revision_path): with open(revision_path) as f: revision_content = f.read() self._revision = get_revision(revision_content, self.metadata, self.path) if self._revision is None: self.set_revision(0) elif revision_content is None: self.set_revision(self._revision) self.config = ConfigOptions() self.config.load(os.path.join(path, "config.yaml")) self._temp_bundle = None self._temp_bundle_file = None
class CharmDirectory(CharmBase): """ Directory that holds charm content. :param path: path to a charm directory. The directory must contain the following files:: - ``metadata.yaml`` """ type = "dir" def __init__(self, path): """Set initial values and parse configuration files from the charm.""" self.path = path revision_content = None self.metadata = MetaData(os.path.join(path, "metadata.yaml")) revision_path = os.path.join(self.path, "revision") if os.path.exists(revision_path): with open(revision_path) as f: revision_content = f.read() self._revision = get_revision(revision_content, self.metadata, self.path) if self._revision is None: self.set_revision(0) elif revision_content is None: self.set_revision(self._revision) self.config = ConfigOptions() self.config.load(os.path.join(path, "config.yaml")) self._temp_bundle = None self._temp_bundle_file = None def get_revision(self): """Get charm revision from directory.""" return self._revision def set_revision(self, revision): """Set charm revision of a directory representation.""" self._revision = revision with open(os.path.join(self.path, "revision"), "w") as f: f.write(str(revision) + "\n") def make_archive(self, path): """ Create an archive of the directory and write it to ``path``. :param path: path to archive. - build/* - This is used for packing the charm itself and any similar tasks. - */.* - Hidden files are all ignored for now. This will most likely be changed into a specific ignore list (.bzr, etc) """ zf = zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED) for dirpath, dirnames, filenames in os.walk(self.path): relative_path = dirpath[len(self.path) + 1 :] if relative_path and not self._ignore(relative_path): zf.write(dirpath, relative_path) for name in filenames: archive_name = os.path.join(relative_path, name) if not self._ignore(archive_name): real_path = os.path.join(dirpath, name) self._check_type(real_path) if os.path.islink(real_path): self._check_link(real_path) self._write_symlink(zf, os.readlink(real_path), archive_name) else: zf.write(real_path, archive_name) zf.close() def _check_type(self, path): """Check the path.""" s = os.stat(path) if stat.S_ISDIR(s.st_mode) or stat.S_ISREG(s.st_mode): return path raise InvalidCharmFile(self.metadata.name, path, "Invalid file type for a charm") def _check_link(self, path): """Check the path.""" link_path = os.readlink(path) if link_path[0] == "/": raise InvalidCharmFile(self.metadata.name, path, "Absolute links are invalid") path_dir = os.path.dirname(path) link_path = os.path.join(path_dir, link_path) if not link_path.startswith(os.path.abspath(self.path)): raise InvalidCharmFile(self.metadata.name, path, "Only internal symlinks are allowed") def _write_symlink(self, zf, link_target, link_path): """Package symlinks with appropriate zipfile metadata.""" info = zipfile.ZipInfo() info.filename = link_path info.create_system = 3 info.external_attr = (stat.S_IFLNK | 0755) << 16 zf.writestr(info, link_target) def _ignore(self, path): if path == "build" or path.startswith("build/"): return True if path.startswith("."): return True def as_bundle(self): from tribus.common.charms.bundle import CharmBundle if self._temp_bundle is None: prefix = "%s-%d.charm." % (self.metadata.name, self.get_revision()) temp_file = tempfile.NamedTemporaryFile(prefix=prefix) self.make_archive(temp_file.name) self._temp_bundle = CharmBundle(temp_file.name) self._temp_bundle_file = temp_file return self._temp_bundle def as_directory(self): return self def compute_sha256(self): """Compute sha256, based on the bundle.""" return self.as_bundle().compute_sha256()