class Run(object): debug = False dryrun = False params = None answers_data = {GLOBAL_CONF: {}} tmpdir = None answers_file = None provider = DEFAULT_PROVIDER installed = False plugins = [] update = False app_path = None target_path = None app_id = None app = None answers_output = None kwargs = None def __init__(self, answers, APP, dryrun = False, debug = False, stop = False, **kwargs): self.debug = debug self.dryrun = dryrun self.stop = stop self.kwargs = kwargs if "answers_output" in kwargs: self.answers_output = kwargs["answers_output"] if os.environ and "IMAGE" in os.environ: self.app_path = APP APP = os.environ["IMAGE"] del os.environ["IMAGE"] if APP and os.path.exists(APP): self.app_path = APP else: self.app_path = os.getcwd() install = Install(answers, APP, dryrun = dryrun, target_path = self.app_path) install.install() self.params = Params(target_path=self.app_path) if "ask" in kwargs: self.params.ask = kwargs["ask"] workdir = None if "workdir" in kwargs: workdir = kwargs["workdir"] self.utils = Utils(self.params, workdir) if not "workdir" in kwargs: kwargs["workdir"] = self.utils.workdir self.answers_file = answers self.plugin = Plugin() self.plugin.load_plugins() def _dispatchGraph(self): if not "graph" in self.params.mainfile_data: raise Exception("Graph not specified in %s" % MAIN_FILE) for component, graph_item in self.params.mainfile_data["graph"].iteritems(): if self.utils.isExternal(graph_item): component_run = Run(self.answers_file, self.utils.getExternalAppDir(component), self.dryrun, self.debug, **self.kwargs) ret = component_run.run() if self.answers_output: self.params.loadAnswers(ret) else: self._processComponent(component, graph_item) def _applyTemplate(self, data, component): template = Template(data) config = self.params.getValues(component) logger.debug("Config: %s ", config) output = None while not output: try: logger.debug(config) output = template.substitute(config) except KeyError as ex: name = ex.args[0] logger.debug("Artifact contains unknown parameter %s, asking for it", name) config[name] = self.params.askFor(name, {"description": "Missing parameter '%s', provide the value or fix your %s" % (name, MAIN_FILE)}) if not len(config[name]): raise Exception("Artifact contains unknown parameter %s" % name) self.params.loadAnswers({component: {name: config[name]}}) return output def _processArtifacts(self, component, provider): artifacts = self.utils.getArtifacts(component) artifact_provider_list = [] if not provider in artifacts: raise Exception("Data for provider \"%s\" are not part of this app" % self.params.provider) dst_dir = os.path.join(self.utils.workdir, component) data = None for artifact in artifacts[provider]: if "inherit" in artifact: logger.debug("Inheriting from %s", artifact["inherit"]) for item in artifact["inherit"]: inherited_artifacts, _ = self._processArtifacts(component, item) artifact_provider_list += inherited_artifacts continue artifact_path = self.utils.sanitizePath(artifact) with open(os.path.join(self.app_path, artifact_path), "r") as fp: data = fp.read() logger.debug("Templating artifact %s/%s", self.app_path, artifact_path) data = self._applyTemplate(data, component) artifact_dst = os.path.join(dst_dir, artifact_path) if not os.path.isdir(os.path.dirname(artifact_dst)): os.makedirs(os.path.dirname(artifact_dst)) with open(artifact_dst, "w") as fp: logger.debug("Writing artifact to %s", artifact_dst) fp.write(data) artifact_provider_list.append(artifact_path) return artifact_provider_list, dst_dir def _processComponent(self, component, graph_item): logger.debug("Processing component %s", component) artifact_list, dst_dir = self._processArtifacts(component, self.params.provider) provider_class = self.plugin.getProvider(self.params.provider) provider = provider_class(self.params.getValues(component), artifact_list, dst_dir, self.dryrun) if provider: logger.info("Using provider %s for component %s", self.params.provider, component) else: raise Exception("Something is broken - couldn't get the provider") try: provider.init() provider.deploy() except ProviderFailedException as ex: logger.error(ex) sys.exit(1) def run(self): self.params.loadMainfile(os.path.join(self.params.target_path, MAIN_FILE)) self.params.loadAnswers(self.answers_file) self.utils.checkAllArtifacts() config = self.params.get() if "provider" in config: self.provider = config["provider"] self._dispatchGraph() #Think about this a bit more probably - it's (re)written for all components... if self.answers_output: self.params.writeAnswers(self.answers_output) return self.params.answers_data return None
class Create(object): name = None app_id = None dryrun = False schema = None def __init__(self, name, schema = None, dryrun = False): self.name = name self.app_id = self._nameToId(name) self.dryrun = dryrun self.schema_path = schema if not self.schema_path: self.schema_path = SCHEMA_URL self.params = Params() self.params.app = self.app_id def _loadSchema(self): if not os.path.isfile(self.schema_path): response = urllib2.urlopen(self.schema_path) with open(os.path.basename(self.schema_path), "w") as fp: fp.write(response.read()) self.schema_path = os.path.basename(self.schema_path) with open(self.schema_path, "r") as fp: self.schema = json.load(fp) def create(self): self._loadSchema() if self.schema and "elements" in self.schema: self._writeFromSchema(self.schema["elements"]) else: print("Corrupted schema, couldn't create app") def build(self, tag): if not tag: tag = self.app_id cmd = ["docker", "build", "-t", tag, "."] if self.dryrun: print("Build: %s" % " ".join(cmd)) else: subprocess.call(cmd) def _writeFromSchema(self, elements): for element in elements: value = element["value"] if not element["contents"] and not value: continue if element["name"] == "application": value = self.app_id print("Writing %s" % element["name"]) if element["type"] == "directory": if value: os.mkdir(value) os.chdir(value) self._writeFromSchema(element["contents"]) os.chdir("..") else: logger.debug("No value for directory %s", element["name"]) elif element["type"] == "file": with open(value, "w") as fp: logger.debug(element) if element["contents"]: if isinstance(element["contents"], str) or isinstance(element["contents"], unicode): fp.write(element["contents"]) elif isinstance(element["contents"], collections.Mapping): fp.write(anymarkup.serialize(self._generateContents(element["contents"]), format='yaml')) # elif element["contentType"] == "application/json": # if element["name"] == "Atomicfile": # element["contents"] = self._updateAtomicfile(element["contents"]) # fp.write(json.dumps(element["contents"])) def _pickOne(self, what, info, options): options_text = "" for i, option in enumerate(options): options_text += "%s. %s\n" % (i+1, option) required = False if "required" in info: required = info["required"] value = raw_input("%s (%s)\n Options:\n%s\nYour choice (default: 1): " % (what, info["description"], options_text)) if len(value) == 0: value = 1 elif int(value) == 0 and not required: return None return options[int(value)-1] def _getName(self, element, content, path = None): name = None if not "name" in content: name = element elif not content["name"]: name = self._generateValue(path) if not name: name = self.params.askFor(element, content) elif type(content["name"]) is list: name = self._pickOne(element, content, content["name"]) else: name = content["name"] logger.debug(name) return name def _generateContents(self, contents, path="root"): result = {} for element, content in contents.iteritems(): local_path = "%s.%s" % (path, element) name = self._getName(element, content, local_path) print("Filling %s" % name) if not content["required"]: skip = self.params.askFor("Element %s not required, do you want to skip it?" % name, {"description": "Type y or n", "default": "Y"}) if isTrue(skip): continue #logger.debug("Key: %s, value %s", element, content["value"]) if content["type"] == "object": result[name] = self._generateContents(content["value"], local_path) elif content["type"] == "list": tmp_results = [] while True: value = self.params.askFor(content["value"].keys()[0], content["value"][content["value"].keys()[0]]) if len(value) == 0: break tmp_results.append(value) result[name] = tmp_results else: if not content["value"]: logger.debug(local_path) value = self._generateValue(local_path) if not value: value = self.params.askFor(element, content) logger.debug(value) else: value = content["value"] result[name] = value return result def _generateValue(self, element): if element == "root.id": return self.app_id elif element == "root.metadata.name": return self.name elif element == "root.graph.component": return self.app_id return None def _nameToId(self, name): return name.strip().lower().replace(" ", "-") def _updateAtomicfile(self, contents): print(contents) if "name" in contents: contents["name"] = self.name if "id" in contents: contents["id"] = self.app_id if "graph" in contents: component = {"repository": "", "name": self.app_id} contents["graph"].append(component) return contents