#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import print_function import argparse import json import os import sys import six here = sys.path[0] if here != '/usr/bin': # Git checkout sys.path[0] = os.path.dirname(here) import pungi.checks import pungi.compose import pungi.paths import pungi.phases import pungi.wrappers.scm import pungi.util from pungi.wrappers.variants import VariantsXmlParser, VariantsValidationError from pungi_utils import config_utils class ValidationCompose(pungi.compose.Compose): def __init__(self, conf, has_old, topdir): self.topdir = topdir self.conf = conf self._logger = None self.just_phases = [] self.skip_phases = [] self.has_old_composes = has_old self.paths = pungi.paths.Paths(self) self.variants = {} self.all_variants = {} @property def old_composes(self): return '/dummy' if self.has_old_composes else None @property def compose_id(self): return 'Dummy-1.0-20160811.t.0' @property def compose_type(self): return 'test' @property def compose_date(self): return '20160811' @property def compose_respin(self): return '0' def read_variants(compose, config): with pungi.util.temp_dir() as tmp_dir: scm_dict = compose.conf["variants_file"] if isinstance(scm_dict, six.string_types) and scm_dict[0] != '/': config_dir = os.path.dirname(config) scm_dict = os.path.join(config_dir, scm_dict) files = pungi.wrappers.scm.get_file_from_scm(scm_dict, tmp_dir) tree_arches = compose.conf.get("tree_arches") tree_variants = compose.conf.get("tree_variants") with open(os.path.join(tmp_dir, files[0]), "r") as file_obj: parser = VariantsXmlParser(file_obj, tree_arches, tree_variants) compose.variants = parser.parse() for variant in compose.variants.values(): compose.all_variants[variant.uid] = variant for child in variant.get_variants(): compose.all_variants[child.uid] = child def run(config, topdir, has_old, offline, defined_variables): # Load default values for undefined variables. This is useful for # validating templates that are supposed to be filled in later with # pungi-config-dump. try: defaults_file = os.path.join( os.path.dirname(config), ".pungi-config-validate.json" ) with open(defaults_file) as f: defined_variables.update(json.load(f)) except IOError: pass # Load actual configuration conf = pungi.util.load_config(config, defined_variables) # Remove the dummy variables used for defaults. for key in defined_variables: del conf[key] errors, warnings = pungi.checks.validate(conf, offline=offline) if errors or warnings: for error in errors + warnings: print(error) sys.exit(1) errors = [] compose = ValidationCompose(conf, has_old, topdir) try: read_variants(compose, config) except VariantsValidationError as exc: errors.extend(str(exc).splitlines()) except RuntimeError as exc: print("WARNING: Failed to load variants: %s" % exc) pkgset_phase = pungi.phases.PkgsetPhase(compose) buildinstall_phase = pungi.phases.BuildinstallPhase(compose) phases = [ pungi.phases.InitPhase(compose), buildinstall_phase, pkgset_phase, pungi.phases.GatherPhase(compose, pkgset_phase), pungi.phases.ExtraFilesPhase(compose, pkgset_phase), pungi.phases.CreaterepoPhase(compose), pungi.phases.OstreeInstallerPhase(compose, buildinstall_phase), pungi.phases.OSTreePhase(compose), pungi.phases.ProductimgPhase(compose, pkgset_phase), pungi.phases.CreateisoPhase(compose, buildinstall_phase), pungi.phases.ExtraIsosPhase(compose), pungi.phases.LiveImagesPhase(compose), pungi.phases.LiveMediaPhase(compose), pungi.phases.ImageBuildPhase(compose), pungi.phases.ImageChecksumPhase(compose), pungi.phases.TestPhase(compose), ] for phase in phases: if phase.skip(): continue try: phase.validate() except ValueError as ex: for i in str(ex).splitlines(): errors.append("%s: %s" % (phase.name.upper(), i)) return errors class DumpSchemaAction(argparse.Action): def __call__(self, parser, ns, values, option_string=None): json.dump(pungi.checks.make_schema(), sys.stdout, sort_keys=True, indent=4) print('') sys.exit(0) def main(args=None): parser = argparse.ArgumentParser() parser.add_argument('--dump-schema', nargs=0, action=DumpSchemaAction, help='print JSON Schema of configuration and exit') parser.add_argument('config', metavar='CONFIG', help='configuration file to validate') parser.add_argument('--old-composes', action='store_true', help='indicate if pungi-koji will be run with --old-composes option') parser.add_argument( "--offline", action="store_true", help="Do not validate git references in URLs", ) parser.add_argument( "-e", "--define", action="append", default=[], metavar="VAR=VALUE", type=config_utils.validate_definition, help=( "Define a variable on command line and inject it into the config file. " "Can be used multiple times." ), ) opts = parser.parse_args(args) defines = config_utils.extract_defines(opts.define) with pungi.util.temp_dir() as topdir: errors = run(opts.config, topdir, opts.old_composes, opts.offline, defines) for msg in errors: print(msg) return bool(errors) if __name__ == '__main__': if main(): sys.exit(1)