#479 Add sub commands to pungi-make-ostree script
Merged 7 years ago by lsedlar. Opened 7 years ago by qwan.

file removed
-81
@@ -1,81 +0,0 @@ 

- # -*- coding: utf-8 -*-

- 

- """

- This module contains functions required by pungi-make-ostree.

- It is expected to be runnable in Koji runroot.

- """

- 

- import argparse

- import os

- from kobo import shortcuts

- import errno

- 

- 

- def ensure_dir(path):

-     try:

-         os.makedirs(path)

-     except OSError as err:

-         if err.errno != errno.EEXIST:

-             raise

-     return path

- 

- 

- def make_log_file(log_dir, filename):

-     """Return path to log file with given name, if log_dir is set."""

-     if not log_dir:

-         return None

-     ensure_dir(log_dir)

-     return os.path.join(log_dir, '%s.log' % filename)

- 

- 

- def init_ostree_repo(repo, log_dir=None):

-     """If the ostree repo does not exist, initialize it."""

-     log_file = make_log_file(log_dir, 'init-ostree-repo')

-     if not os.path.isdir(repo) or not os.listdir(repo):

-         ensure_dir(repo)

-         shortcuts.run(['ostree', 'init', '--repo=%s' % repo, '--mode=archive-z2'],

-                       show_cmd=True, stdout=True, logfile=log_file)

- 

- 

- def make_ostree_repo(repo, config, version=None, log_dir=None):

-     log_file = make_log_file(log_dir, 'create-ostree-repo')

-     cmd = ['rpm-ostree', 'compose', 'tree', '--repo=%s' % repo,

-            '--write-commitid-to=%s' % make_log_file(log_dir, 'commitid')]

-     if version:

-         # Add versioning metadata

-         cmd.append('--add-metadata-string=version=%s' % version)

-     cmd.append(config)

- 

-     shortcuts.run(cmd, show_cmd=True, stdout=True, logfile=log_file)

- 

- 

- def update_ostree_summary(repo, log_dir=None):

-     log_file = make_log_file(log_dir, 'ostree-summary')

-     shortcuts.run(['ostree', 'summary', '-u', '--repo=%s' % repo],

-                   show_cmd=True, stdout=True, logfile=log_file)

- 

- 

- def run(opts):

-     init_ostree_repo(opts.ostree_repo, log_dir=opts.log_dir)

-     make_ostree_repo(opts.ostree_repo, opts.treefile, version=opts.version, log_dir=opts.log_dir)

-     if opts.update_summary:

-         update_ostree_summary(opts.ostree_repo, log_dir=opts.log_dir)

- 

- 

- def main(args=None):

-     parser = argparse.ArgumentParser()

-     parser.add_argument('--log-dir',

-                         help='where to log output')

- 

-     parser.add_argument('ostree_repo', metavar='OSTREE_REPO',

-                         help='where to put the ostree repo')

-     parser.add_argument('--treefile', required=True,

-                         help='treefile for rpm-ostree')

-     parser.add_argument('--version',

-                         help='version string to be added as versioning metadata')

-     parser.add_argument('--update-summary', action='store_true',

-                         help='update summary metadata')

- 

-     opts = parser.parse_args(args)

- 

-     run(opts)

@@ -0,0 +1,84 @@ 

+ # -*- coding: utf-8 -*-

+ 

+ 

+ # This program is free software; you can redistribute it and/or modify

+ # it under the terms of the GNU General Public License as published by

+ # the Free Software Foundation; version 2 of the License.

+ #

+ # This program is distributed in the hope that it will be useful,

+ # but WITHOUT ANY WARRANTY; without even the implied warranty of

+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+ # GNU Library General Public License for more details.

+ #

+ # You should have received a copy of the GNU General Public License

+ # along with this program; if not, see <https://gnu.org/licenses/>.

+ 

+ 

+ import argparse

+ 

+ from .tree import Tree

+ from .installer import Installer

+ 

+ 

+ def main(args=None):

+     parser = argparse.ArgumentParser()

+     subparser = parser.add_subparsers(help="Sub commands")

+ 

+     treep = subparser.add_parser("tree", help="Compose OSTree repository")

+     treep.set_defaults(_class=Tree, func='run')

+     treep.add_argument('--repo', metavar='PATH', required=True,

+                        help='where to put the OSTree repo (required)')

+     treep.add_argument('--treefile', metavar="FILE", required=True,

+                        help='treefile for rpm-ostree (required)')

+     treep.add_argument('--log-dir', metavar="DIR",

+                        help='where to log output')

+     treep.add_argument('--extra-config', metavar="FILE",

+                        help='JSON file contains extra configurations')

+     treep.add_argument('--version', metavar="VERSION",

+                        help='version string to be added as versioning metadata')

+     treep.add_argument('--update-summary', action='store_true',

+                        help='update summary metadata')

+ 

+     installerp = subparser.add_parser("installer", help="Create an OSTree installer image")

+     installerp.set_defaults(_class=Installer, func='run')

+     installerp.add_argument('-p', '--product', metavar='PRODUCT', required=True,

+                             help='product name (required)')

+     installerp.add_argument('-v', '--version', metavar='VERSION', required=True,

+                             help='version identifier (required)')

+     installerp.add_argument('-r', '--release', metavar='RELEASE', required=True,

+                             help='release information (required)')

+     installerp.add_argument('-s', '--source', metavar='REPOSITORY', required=True,

+                             action='append',

+                             help='source repository (required)')

+     installerp.add_argument('-o', '--output', metavar='DIR', required=True,

+                             help='path to image output directory (required)')

+     installerp.add_argument('--log-dir', metavar='DIR',

+                             help='path to log directory')

+     installerp.add_argument('--volid', metavar='VOLID',

+                             help='volume id')

+     installerp.add_argument('--variant', metavar='VARIANT',

+                             help='variant name')

+     installerp.add_argument('--rootfs-size', metavar='SIZE')

+     installerp.add_argument('--nomacboot', action='store_true', default=False)

+     installerp.add_argument('--noupgrade', action='store_true', default=False)

+     installerp.add_argument('--isfinal', action='store_true', default=False)

+ 

+     installerp.add_argument('--installpkgs', metavar='PACKAGE', action='append',

+                             help='package glob to install before runtime-install.tmpl')

+     installerp.add_argument('--add-template', metavar='FILE', action='append',

+                             help='Additional template for runtime image')

+     installerp.add_argument('--add-template-var', metavar='ADD_TEMPLATE_VARS', action='append',

+                             help='Set variable for runtime image template')

+     installerp.add_argument('--add-arch-template', metavar='FILE', action='append',

+                             help='Additional template for architecture-specific image')

+     installerp.add_argument('--add-arch-template-var', metavar='ADD_ARCH_TEMPLATE_VARS', action='append',

+                             help='Set variable for architecture-specific image')

+ 

+     installerp.add_argument('--extra-config', metavar='FILE',

+                             help='JSON file contains extra configurations')

+ 

+     args = parser.parse_args(args)

+     _class = args._class()

+     _class.set_args(args)

+     func = getattr(_class, args.func)

+     func()

file added
+19
@@ -0,0 +1,19 @@ 

+ # -*- coding: utf-8 -*-

+ 

+ 

+ # This program is free software; you can redistribute it and/or modify

+ # it under the terms of the GNU General Public License as published by

+ # the Free Software Foundation; version 2 of the License.

+ #

+ # This program is distributed in the hope that it will be useful,

+ # but WITHOUT ANY WARRANTY; without even the implied warranty of

+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+ # GNU Library General Public License for more details.

+ #

+ # You should have received a copy of the GNU General Public License

+ # along with this program; if not, see <https://gnu.org/licenses/>.

+ 

+ 

+ class OSTree(object):

+     def set_args(self, args):

+         self.args = args

@@ -0,0 +1,80 @@ 

+ # -*- coding: utf-8 -*-

+ 

+ 

+ # This program is free software; you can redistribute it and/or modify

+ # it under the terms of the GNU General Public License as published by

+ # the Free Software Foundation; version 2 of the License.

+ #

+ # This program is distributed in the hope that it will be useful,

+ # but WITHOUT ANY WARRANTY; without even the implied warranty of

+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+ # GNU Library General Public License for more details.

+ #

+ # You should have received a copy of the GNU General Public License

+ # along with this program; if not, see <https://gnu.org/licenses/>.

+ 

+ 

+ import json

+ from kobo import shortcuts

+ 

+ from .base import OSTree

+ from ..wrappers import lorax

+ 

+ 

+ class Installer(OSTree):

+     def _merge_config(self, config):

+         if config.get("source_repo_from", None):

+             self.sources.extend([config.get("source_repo_from")])

+ 

+         self.installpkgs.extend(config.get('installpkgs', []))

+         self.add_template.extend(config.get("add_template", []))

+         self.add_template_var.extend(config.get("add_template_var"))

+         self.add_arch_template.extend(config.get("add_arch_template", []))

+         self.add_arch_template_var.extend(config.get("add_arch_template_var", []))

+ 

+     def run(self):

+         self.product = self.args.product

+         self.version = self.args.version

+         self.release = self.args.release

+         self.sources = self.args.source

+         self.output = self.args.output

+ 

+         self.logdir = self.args.log_dir

+         self.volid = self.args.volid

+         self.variant = self.args.variant

+         self.rootfs_size = self.args.rootfs_size

+         self.nomacboot = self.args.nomacboot

+         self.noupgrade = self.args.noupgrade

+         self.isfinal = self.args.isfinal

+ 

+         self.installpkgs = self.args.installpkgs or []

+         self.add_template = self.args.add_template or []

+         self.add_template_var = self.args.add_template_var or []

+         self.add_arch_template = self.args.add_arch_template or []

+         self.add_arch_template_var = self.args.add_arch_template_var or []

+ 

+         self.extra_config = self.args.extra_config

+         if self.extra_config:

+             self.extra_config = json.load(open(self.extra_config, 'r'))

+             self._merge_config(self.extra_config)

+ 

+         lorax_wrapper = lorax.LoraxWrapper()

+         cmd = lorax_wrapper.get_lorax_cmd(

+             self.product,

+             self.version,

+             self.release,

+             self.sources,

+             self.output,

+             variant=self.variant,

+             nomacboot=self.nomacboot,

+             volid=self.volid,

+             buildinstallpackages=self.installpkgs,

+             add_template=self.add_template,

+             add_template_var=self.add_template_var,

+             add_arch_template=self.add_arch_template,

+             add_arch_template_var=self.add_arch_template_var,

+             rootfs_size=self.rootfs_size,

+             is_final=self.isfinal,

+             log_dir=self.logdir

+         )

+         shortcuts.run(cmd)

file added
+101
@@ -0,0 +1,101 @@ 

+ # -*- coding: utf-8 -*-

+ 

+ 

+ # This program is free software; you can redistribute it and/or modify

+ # it under the terms of the GNU General Public License as published by

+ # the Free Software Foundation; version 2 of the License.

+ #

+ # This program is distributed in the hope that it will be useful,

+ # but WITHOUT ANY WARRANTY; without even the implied warranty of

+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+ # GNU Library General Public License for more details.

+ #

+ # You should have received a copy of the GNU General Public License

+ # along with this program; if not, see <https://gnu.org/licenses/>.

+ 

+ 

+ import os

+ import json

+ from kobo import shortcuts

+ 

+ from pungi.util import makedirs

+ from .base import OSTree

+ from .utils import (make_log_file, tweak_treeconf,

+                     get_ref_from_treefile, get_commitid_from_commitid_file)

+ 

+ 

+ class Tree(OSTree):

+     def _init_repo(self):

+         """If the ostree repo does not exist, initialize it."""

+         log_file = make_log_file(self.logdir, 'init-ostree-repo')

+         if not os.path.isdir(self.repo) or not os.listdir(self.repo):

+             makedirs(self.repo)

+             shortcuts.run(['ostree', 'init', '--repo=%s' % self.repo, '--mode=archive-z2'],

+                           show_cmd=True, stdout=True, logfile=log_file)

+ 

+     def _make_tree(self):

+         """Compose OSTree tree"""

+         log_file = make_log_file(self.logdir, 'create-ostree-repo')

+         cmd = ['rpm-ostree', 'compose', 'tree', '--repo=%s' % self.repo,

+                '--write-commitid-to=%s' % self.commitid_file]

+         if self.version:

+             # Add versioning metadata

+             cmd.append('--add-metadata-string=version=%s' % self.version)

+         cmd.append(self.treefile)

+ 

+         shortcuts.run(cmd, show_cmd=True, stdout=True, logfile=log_file)

+ 

+     def _update_summary(self):

+         """Update summary metadata"""

+         log_file = make_log_file(self.logdir, 'ostree-summary')

+         shortcuts.run(['ostree', 'summary', '-u', '--repo=%s' % self.repo],

+                       show_cmd=True, stdout=True, logfile=log_file)

+ 

+     def _update_ref(self):

+         """

+         Update the ref.

+ 

+         '--write-commitid-to' is specified when compose the tree, so we need

+         to update the ref by ourselves. ref is retrieved from treefile and

+         commitid is retrieved from the committid file.

+         """

+         tag_ref = True

+         if self.extra_config:

+             tag_ref = self.extra_config.get('tag_ref', True)

+         if not tag_ref:

+             return

+         ref = get_ref_from_treefile(self.treefile)

+         commitid = get_commitid_from_commitid_file(self.commitid_file)

+         if ref and commitid:

+             # Let's write the tag out ourselves

+             heads_dir = os.path.join(self.repo, 'refs', 'heads')

+             if not os.path.exists(heads_dir):

+                 raise RuntimeError('Refs/heads did not exist in ostree repo')

+ 

+             ref_path = os.path.join(heads_dir, ref)

+             makedirs(os.path.dirname(ref_path))

+             with open(ref_path, 'w') as f:

+                 f.write(commitid + '\n')

+ 

+     def run(self):

+         self.repo = self.args.repo

+         self.treefile = self.args.treefile

+         self.version = self.args.version

+         self.logdir = self.args.log_dir

+         self.update_summary = self.args.update_summary

+         self.extra_config = self.args.extra_config

+         if self.extra_config:

+             self.extra_config = json.load(open(self.extra_config, 'r'))

+             source_repo_from = self.extra_config.get('source_repo_from', None)

+             extra_source_repos = self.extra_config.get('extra_source_repos', None)

+             keep_original_sources = self.extra_config.get('keep_original_sources', False)

+             repos = extra_source_repos + [{'name': 'source_repo_from', 'baseurl': source_repo_from}]

+             tweak_treeconf(self.treefile, source_repos=repos, keep_original_sources=keep_original_sources)

+ 

+         self.commitid_file = make_log_file(self.logdir, 'commitid')

+ 

+         self._init_repo()

+         self._make_tree()

+         self._update_ref()

+         if self.update_summary:

+             self._update_summary()

@@ -0,0 +1,93 @@ 

+ # -*- coding: utf-8 -*-

+ 

+ 

+ # This program is free software; you can redistribute it and/or modify

+ # it under the terms of the GNU General Public License as published by

+ # the Free Software Foundation; version 2 of the License.

+ #

+ # This program is distributed in the hope that it will be useful,

+ # but WITHOUT ANY WARRANTY; without even the implied warranty of

+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+ # GNU Library General Public License for more details.

+ #

+ # You should have received a copy of the GNU General Public License

+ # along with this program; if not, see <https://gnu.org/licenses/>.

+ 

+ 

+ import datetime

+ import json

+ import os

+ 

+ from pungi.util import makedirs

+ 

+ 

+ def make_log_file(log_dir, filename):

+     """Return path to log file with given name, if log_dir is set."""

+     if not log_dir:

+         return None

+     makedirs(log_dir)

+     return os.path.join(log_dir, '%s.log' % filename)

+ 

+ 

+ def get_ref_from_treefile(treefile):

+     """Return ref name by parsing the tree config file"""

+     ref = None

+     if os.path.isfile(treefile):

+         with open(treefile, 'r') as f:

+             try:

+                 parsed = json.loads(f.read())

+                 ref = parsed['ref']

+             except Exception:

+                 pass

+     return ref

+ 

+ 

+ def get_commitid_from_commitid_file(commitid_file):

+     """Return commit id which is read from the commitid file"""

+     commitid = None

+     if os.path.isfile(commitid_file):

+         with open(commitid_file, 'r') as f:

+             commitid = f.read().replace('\n', '')

+     return commitid

+ 

+ 

+ def tweak_treeconf(treeconf, source_repos=None, keep_original_sources=False):

+     """

+     Update tree config file by adding new repos, and remove existing repos

+     from the tree config file if 'keep_original_sources' is not enabled.

+     """

+     # add this timestamp to repo name to get unique repo filename and repo name

+     # should be safe enough

+     time = datetime.datetime.now().strftime("%Y%m%d%H%M%S")

+ 

+     treeconf_dir = os.path.dirname(treeconf)

+     with open(treeconf, 'r') as f:

+         treeconf_content = json.load(f)

+ 

+     # backup the old tree config

+     os.rename(treeconf, '%s.%s.bak' % (treeconf, time))

+ 

+     repos = []

+     if source_repos:

+         for repo in source_repos:

+             name = "%s-%s" % (repo['name'], time)

+             with open("%s/%s.repo" % (treeconf_dir, name), 'w') as f:

+                 f.write("[%s]\n" % name)

+                 f.write("name=%s\n" % name)

+                 f.write("baseurl=%s\n" % repo['baseurl'])

+                 exclude = repo.get('exclude', None)

+                 if exclude:

+                     f.write("exclude=%s\n" % exclude)

+                 gpgcheck = '1' if repo.get('gpgcheck', False) else '0'

+                 f.write("gpgcheck=%s\n" % gpgcheck)

+             repos.append(name)

+ 

+     original_repos = treeconf_content.get('repos', [])

+     if keep_original_sources:

+         treeconf_content['repos'] = original_repos + repos

+     else:

+         treeconf_content['repos'] = repos

+ 

+     # update tree config to add new repos

+     with open(treeconf, 'w') as f:

+         json.dump(treeconf_content, f, indent=4)

file modified
+37 -80
@@ -1,12 +1,13 @@ 

  # -*- coding: utf-8 -*-

  

- import datetime

+ import copy

  import json

  import os

  from kobo.threads import ThreadPool, WorkerThread

  

  from .base import ConfigGuardedPhase

  from .. import util

+ from ..ostree.utils import get_ref_from_treefile, get_commitid_from_commitid_file

  from ..paths import translate_path

  from ..wrappers import kojiwrapper, scm

  
@@ -53,7 +54,6 @@ 

  

          self._clone_repo(repodir, config['config_url'], config.get('config_branch', 'master'))

  

-         treeconf = os.path.join(repodir, config['treefile'])

          source_repos = [{'name': '%s-%s' % (compose.compose_id, config['source_repo_from']),

                           'baseurl': source_repo}]

  
@@ -72,30 +72,41 @@ 

  

              source_repos = source_repos + extra_source_repos

  

-         keep_original_sources = config.get('keep_original_sources', False)

-         self._tweak_treeconf(treeconf, source_repos=source_repos,

-                              keep_original_sources=keep_original_sources)

+         # copy the original config and update before save to a json file

+         new_config = copy.copy(config)

+ 

+         # repos in configuration can have repo url set to variant UID,

+         # update it to have the actual url that we just translated.

+         new_config.update({'source_repo_from': source_repo})

+         if extra_source_repos:

+             new_config.update({'extra_source_repos': extra_source_repos})

+ 

+         # remove unnecessary (for 'pungi-make-ostree tree' script ) elements

+         # from config, it doesn't hurt to have them, however remove them can

+         # reduce confusion

+         for k in ['ostree_repo', 'treefile', 'config_url', 'config_branch',

+                   'failable', 'version', 'update_summary']:

+             new_config.pop(k, None)

+ 

+         extra_config_file = None

+         if new_config:

+             # write a json file to save the configuration, so 'pungi-make-ostree tree'

+             # can take use of it

+             extra_config_file = os.path.join(workdir, 'extra_config.json')

+             with open(extra_config_file, 'w') as f:

+                 json.dump(new_config, f, indent=4)

  

          # Ensure target directory exists, otherwise Koji task will fail to

          # mount it.

          util.makedirs(config['ostree_repo'])

  

-         self._run_ostree_cmd(compose, variant, arch, config, repodir)

-         ref, commitid = self._get_commit_info(config, repodir)

-         if config.get('tag_ref', True) and ref and commitid:

-             # Let's write the tag out ourselves

-             heads_dir = os.path.join(config['ostree_repo'], 'refs', 'heads')

-             if not os.path.exists(heads_dir):

-                 raise RuntimeError('Refs/heads did not exist in ostree repo')

- 

-             ref_path = os.path.join(heads_dir, ref)

-             if not os.path.exists(os.path.dirname(ref_path)):

-                 os.makedirs(os.path.dirname(ref_path))

- 

-             with open(ref_path, 'w') as f:

-                 f.write(commitid + '\n')

+         self._run_ostree_cmd(compose, variant, arch, config, repodir,

+                              extra_config_file=extra_config_file)

  

          if compose.notifier:

+             ref = get_ref_from_treefile(os.path.join(repodir, config['treefile']))

+             # 'pungi-make-ostree tree' writes commitid to commitid.log in logdir

+             commitid = get_commitid_from_commitid_file(os.path.join(self.logdir, 'commitid.log'))

              compose.notifier.send('ostree',

                                    variant=variant.uid,

                                    arch=arch,
@@ -104,26 +115,12 @@ 

  

          self.pool.log_info('[DONE ] %s' % msg)

  

-     def _get_commit_info(self, config, config_repo):

-         ref = None

-         commitid = None

-         with open(os.path.join(config_repo, config['treefile']), 'r') as f:

-             try:

-                 parsed = json.loads(f.read())

-                 ref = parsed['ref']

-             except ValueError:

-                 return None, None

-         if os.path.exists(os.path.join(self.logdir, 'commitid')):

-             with open(os.path.join(self.logdir, 'commitid'), 'r') as f:

-                 commitid = f.read().replace('\n', '')

-         else:

-             return None, None

-         return ref, commitid

- 

-     def _run_ostree_cmd(self, compose, variant, arch, config, config_repo):

+     def _run_ostree_cmd(self, compose, variant, arch, config, config_repo, extra_config_file=None):

          cmd = [

              'pungi-make-ostree',

-             '--log-dir=%s' % os.path.join(self.logdir),

+             'tree',

+             '--repo=%s' % config['ostree_repo'],

+             '--log-dir=%s' % self.logdir,

              '--treefile=%s' % os.path.join(config_repo, config['treefile']),

          ]

  
@@ -131,12 +128,12 @@ 

          if version:

              cmd.append('--version=%s' % version)

  

+         if extra_config_file:

+             cmd.append('--extra-config=%s' % extra_config_file)

+ 

          if config.get('update_summary', False):

              cmd.append('--update-summary')

  

-         # positional argument: ostree_repo

-         cmd.append(config['ostree_repo'])

- 

          runroot_channel = compose.conf.get("runroot_channel")

          runroot_tag = compose.conf["runroot_tag"]

  
@@ -157,43 +154,3 @@ 

      def _clone_repo(self, repodir, url, branch):

          scm.get_dir_from_scm({'scm': 'git', 'repo': url, 'branch': branch, 'dir': '.'},

                               repodir, logger=self.pool._logger)

- 

-     def _tweak_treeconf(self, treeconf, source_repos, keep_original_sources=False):

-         """

-         Update tree config file by adding new repos and remove existing repos

-         from the tree config file if 'keep_original_sources' is not enabled.

-         """

-         # add this timestamp to repo name to get unique repo filename and repo name

-         # should be safe enough

-         time = datetime.datetime.now().strftime("%Y%m%d%H%M%S")

- 

-         treeconf_dir = os.path.dirname(treeconf)

-         with open(treeconf, 'r') as f:

-             treeconf_content = json.load(f)

- 

-         # backup the old tree config

-         os.rename(treeconf, '%s.%s.bak' % (treeconf, time))

- 

-         repos = []

-         for repo in source_repos:

-             name = "%s-%s" % (repo['name'], time)

-             with open("%s/%s.repo" % (treeconf_dir, name), 'w') as f:

-                 f.write("[%s]\n" % name)

-                 f.write("name=%s\n" % name)

-                 f.write("baseurl=%s\n" % repo['baseurl'])

-                 exclude = repo.get('exclude', None)

-                 if exclude:

-                     f.write("exclude=%s\n" % exclude)

-                 gpgcheck = '1' if repo.get('gpgcheck', False) else '0'

-                 f.write("gpgcheck=%s\n" % gpgcheck)

-             repos.append(name)

- 

-         original_repos = treeconf_content.get('repos', [])

-         if keep_original_sources:

-             treeconf_content['repos'] = original_repos + repos

-         else:

-             treeconf_content['repos'] = repos

- 

-         # update tree config to add new repos

-         with open(treeconf, 'w') as f:

-             json.dump(treeconf_content, f, indent=4)

file modified
+22 -72
@@ -110,10 +110,12 @@ 

          self.assertEqual(koji.get_runroot_cmd.call_args_list,

                           [mock.call('rrt', 'x86_64',

                                      ['pungi-make-ostree',

+                                      'tree',

+                                      '--repo=%s' % self.repo,

                                       '--log-dir=%s/logs/x86_64/Everything/ostree-1' % self.topdir,

                                       '--treefile=%s/fedora-atomic-docker-host.json' % (

                                           self.topdir + '/work/ostree-1/config_repo'),

-                                      self.repo],

+                                      '--extra-config=%s/extra_config.json' % (self.topdir + '/work/ostree-1')],

                                      channel=None, mounts=[self.topdir, self.repo],

                                      packages=['pungi', 'ostree', 'rpm-ostree'],

                                      task_id=True, use_shell=True, new_chroot=True)])
@@ -121,17 +123,7 @@ 

                           [mock.call(koji.get_runroot_cmd.return_value,

                                      log_file=self.topdir + '/logs/x86_64/Everything/ostree-1/runroot.log')])

  

-         repo_files = []

-         for fp in os.listdir(os.path.join(self.topdir, 'work/ostree-1/config_repo')):

-             if fp.endswith('.repo'):

-                 repo_files.append(fp)

- 

-                 if fp not in ['fedora-rawhide.repo', 'fedora-24.repo', 'fedora-23.repo']:

-                     with open(os.path.join(self.topdir, 'work/ostree-1/config_repo', fp)) as f:

-                         self.assertIn('baseurl=http://example.com/Everything/$basearch/os', f.read())

-         # test a new repo file created

-         self.assertEqual(len(repo_files), 4)

- 

+         self.assertTrue(os.path.isfile(os.path.join(self.topdir, 'work/ostree-1/extra_config.json')))

          self.assertTrue(os.path.isdir(self.repo))

  

      @mock.patch('pungi.wrappers.scm.get_dir_from_scm')
@@ -181,7 +173,7 @@ 

          koji = KojiWrapper.return_value

          koji.run_runroot_cmd.side_effect = self._mock_runroot(

              0,

-             {'commitid': 'fca3465861a',

+             {'commitid.log': 'fca3465861a',

               'create-ostree-repo.log':

                  ['Doing work', 'fedora-atomic/25/x86_64 -> fca3465861a']})

          t = ostree.OSTreeThread(self.pool)
@@ -214,7 +206,7 @@ 

                           [mock.call('ostree',

                                      variant='Everything',

                                      arch='x86_64',

-                                     ref=None,

+                                     ref='fedora-atomic/25/x86_64',

                                      commitid=None)])

  

      @mock.patch('pungi.wrappers.scm.get_dir_from_scm')
@@ -254,10 +246,13 @@ 

          self.assertEqual(koji.get_runroot_cmd.call_args_list,

                           [mock.call('rrt', 'x86_64',

                                      ['pungi-make-ostree',

+                                      'tree',

+                                      '--repo=%s' % self.repo,

                                       '--log-dir=%s/logs/x86_64/Everything/ostree-1' % self.topdir,

                                       '--treefile=%s/fedora-atomic-docker-host.json' % (

                                           self.topdir + '/work/ostree-1/config_repo'),

-                                      '--update-summary', self.repo],

+                                      '--extra-config=%s/work/ostree-1/extra_config.json' % self.topdir,

+                                      '--update-summary'],

                                      channel=None, mounts=[self.topdir, self.repo],

                                      packages=['pungi', 'ostree', 'rpm-ostree'],

                                      task_id=True, use_shell=True, new_chroot=True)])
@@ -286,10 +281,13 @@ 

          self.assertEqual(koji.get_runroot_cmd.call_args_list,

                           [mock.call('rrt', 'x86_64',

                                      ['pungi-make-ostree',

+                                      'tree',

+                                      '--repo=%s' % self.repo,

                                       '--log-dir=%s/logs/x86_64/Everything/ostree-1' % self.topdir,

                                       '--treefile=%s/fedora-atomic-docker-host.json' % (

                                           self.topdir + '/work/ostree-1/config_repo'),

-                                      '--version=24', self.repo],

+                                      '--version=24',

+                                      '--extra-config=%s/work/ostree-1/extra_config.json' % self.topdir],

                                      channel=None, mounts=[self.topdir, self.repo],

                                      packages=['pungi', 'ostree', 'rpm-ostree'],

                                      task_id=True, use_shell=True, new_chroot=True)])
@@ -299,7 +297,7 @@ 

  

      @mock.patch('pungi.wrappers.scm.get_dir_from_scm')

      @mock.patch('pungi.wrappers.kojiwrapper.KojiWrapper')

-     def test_run_with_extra_source_repos(self, KojiWrapper, get_dir_from_scm):

+     def test_write_extra_config_file(self, KojiWrapper, get_dir_from_scm):

          get_dir_from_scm.side_effect = self._dummy_config_repo

  

          koji = KojiWrapper.return_value
@@ -319,55 +317,6 @@ 

                      'exclude': 'systemd-container'

                  }

              ],

-             'config_url': 'https://git.fedorahosted.org/git/fedora-atomic.git',

-             'config_branch': 'f24',

-             'treefile': 'fedora-atomic-docker-host.json',

-             'ostree_repo': self.repo

-         }

- 

-         t = ostree.OSTreeThread(self.pool)

- 

-         t.process((self.compose, self.compose.variants['Everything'], 'x86_64', cfg), 1)

- 

-         repo_files = []

-         for fp in os.listdir(os.path.join(self.topdir, 'work/ostree-1/config_repo')):

-             if fp.endswith('.repo'):

-                 repo_files.append(fp)

- 

-                 if fp not in ['fedora-rawhide.repo', 'fedora-24.repo', 'fedora-23.repo']:

-                     if fp.startswith('repo_a'):

-                         with open(os.path.join(self.topdir, 'work/ostree-1/config_repo', fp)) as f:

-                             # ignore timestamp in repo name while checking

-                             content = f.read()

-                             self.assertIn('[repo_a', content)

-                             self.assertIn('name=repo_a', content)

-                             self.assertIn('baseurl=http://url/to/repo/a', content)

-                             self.assertIn('exclude=systemd-container', content)

-                             self.assertIn('gpgcheck=0', content)

-                     elif fp.startswith('Server'):

-                         with open(os.path.join(self.topdir, 'work/ostree-1/config_repo', fp)) as f:

-                             content = f.read()

-                             self.assertIn('[Server', content)

-                             self.assertIn('baseurl=http://example.com/Server/$basearch/os', content)

-                             self.assertIn('exclude=systemd-container', content)

-                             self.assertIn('gpgcheck=0', content)

-                     else:

-                         # this is the Everything repo (source_repo_from)

-                         with open(os.path.join(self.topdir, 'work/ostree-1/config_repo', fp)) as f:

-                             self.assertIn('baseurl=http://example.com/Everything/$basearch/os', f.read())

-         # test new repos files created

-         self.assertEqual(len(repo_files), 3 + 1 + len(cfg['extra_source_repos']))

- 

-     @mock.patch('pungi.wrappers.scm.get_dir_from_scm')

-     @mock.patch('pungi.wrappers.kojiwrapper.KojiWrapper')

-     def test_run_with_keep_original_source_repos(self, KojiWrapper, get_dir_from_scm):

-         get_dir_from_scm.side_effect = self._dummy_config_repo

- 

-         koji = KojiWrapper.return_value

-         koji.run_runroot_cmd.side_effect = self._mock_runroot(0)

- 

-         cfg = {

-             'source_repo_from': 'Everything',

              'keep_original_sources': True,

              'config_url': 'https://git.fedorahosted.org/git/fedora-atomic.git',

              'config_branch': 'f24',
@@ -379,12 +328,13 @@ 

  

          t.process((self.compose, self.compose.variants['Everything'], 'x86_64', cfg), 1)

  

-         treeconf_content = json.load(open(os.path.join(self.topdir,

-                                                        'work/ostree-1/config_repo',

-                                                        cfg['treefile'])))

- 

-         # added 1 repo (Everything), have 4 (3 + 1) repos now

-         self.assertEqual(len(treeconf_content['repos']), 4)

+         extra_config_file = os.path.join(self.topdir, 'work/ostree-1/extra_config.json')

+         self.assertTrue(os.path.isfile(extra_config_file))

+         extra_config = json.load(open(extra_config_file, 'r'))

+         self.assertTrue(extra_config.get('keep_original_sources', False))

+         self.assertEqual(extra_config.get('source_repo_from', None), 'http://example.com/Everything/$basearch/os')

+         self.assertEqual(len(extra_config.get('extra_source_repos', [])), len(cfg['extra_source_repos']))

+         self.assertEqual(extra_config.get('extra_source_repos').pop()['baseurl'], 'http://example.com/Server/$basearch/os')

  

  if __name__ == '__main__':

      unittest.main()

file modified
+245 -6
@@ -6,7 +6,9 @@ 

  import mock

  

  import os

+ import json

  import sys

+ import datetime

  

  sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))

  sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'bin'))
@@ -15,16 +17,28 @@ 

  from pungi import ostree

  

  

- class OstreeScriptTest(helpers.PungiTestCase):

+ class OstreeTreeScriptTest(helpers.PungiTestCase):

+ 

+     def _make_dummy_config_dir(self, path):

+         helpers.touch(os.path.join(path, 'fedora-atomic-docker-host.json'),

+                       json.dumps({'ref': 'fedora-atomic/25/x86_64',

+                                   'repos': ['fedora-rawhide', 'fedora-24', 'fedora-23']}))

+         helpers.touch(os.path.join(path, 'fedora-rawhide.repo'),

+                       '[fedora-rawhide]\nmirrorlist=mirror-mirror-on-the-wall')

+         helpers.touch(os.path.join(path, 'fedora-24.repo'),

+                       '[fedora-24]\nmetalink=who-is-the-fairest-of-them-all')

+         helpers.touch(os.path.join(path, 'fedora-23.repo'),

+                       '[fedora-23]\nbaseurl=why-not-zoidberg?')

  

      @mock.patch('kobo.shortcuts.run')

      def test_full_run(self, run):

          repo = os.path.join(self.topdir, 'atomic')

  

          ostree.main([

+             'tree',

+             '--repo=%s' % repo,

              '--log-dir=%s' % os.path.join(self.topdir, 'logs', 'Atomic'),

              '--treefile=%s/fedora-atomic-docker-host.json' % self.topdir,

-             repo,

          ])

  

          self.maxDiff = None
@@ -44,9 +58,10 @@ 

          os.mkdir(repo)

  

          ostree.main([

+             'tree',

+             '--repo=%s' % repo,

              '--log-dir=%s' % os.path.join(self.topdir, 'logs', 'Atomic'),

              '--treefile=%s/fedora-atomic-docker-host.json' % self.topdir,

-             repo,

          ])

  

          self.maxDiff = None
@@ -66,9 +81,10 @@ 

          helpers.touch(os.path.join(repo, 'initialized'))

  

          ostree.main([

+             'tree',

+             '--repo=%s' % repo,

              '--log-dir=%s' % os.path.join(self.topdir, 'logs', 'Atomic'),

              '--treefile=%s/fedora-atomic-docker-host.json' % self.topdir,

-             repo,

          ])

  

          self.maxDiff = None
@@ -84,10 +100,11 @@ 

          repo = os.path.join(self.topdir, 'atomic')

  

          ostree.main([

+             'tree',

+             '--repo=%s' % repo,

              '--log-dir=%s' % os.path.join(self.topdir, 'logs', 'Atomic'),

              '--treefile=%s/fedora-atomic-docker-host.json' % self.topdir,

              '--update-summary',

-             repo,

          ])

  

          self.maxDiff = None
@@ -107,10 +124,11 @@ 

          repo = os.path.join(self.topdir, 'atomic')

  

          ostree.main([

+             'tree',

+             '--repo=%s' % repo,

              '--log-dir=%s' % os.path.join(self.topdir, 'logs', 'Atomic'),

              '--treefile=%s/fedora-atomic-docker-host.json' % self.topdir,

              '--version=24',

-             repo,

          ])

  

          self.maxDiff = None
@@ -124,5 +142,226 @@ 

                          self.topdir + '/fedora-atomic-docker-host.json'],

                         logfile=self.topdir + '/logs/Atomic/create-ostree-repo.log', show_cmd=True, stdout=True)])

  

+     @mock.patch('pungi.ostree.utils.datetime')

+     @mock.patch('kobo.shortcuts.run')

+     def test_extra_config_with_extra_repos(self, run, time):

+         time.datetime.now.return_value = datetime.datetime(2016, 1, 1, 1, 1)

+         timestamp = time.datetime.now().strftime("%Y%m%d%H%M%S")

+ 

+         configdir = os.path.join(self.topdir, 'config')

+         self._make_dummy_config_dir(configdir)

+         treefile = os.path.join(configdir, 'fedora-atomic-docker-host.json')

+ 

+         repo = os.path.join(self.topdir, 'atomic')

+ 

+         extra_config_file = os.path.join(self.topdir, 'extra_config.json')

+         extra_config = {

+             "source_repo_from": "http://www.example.com/Server.repo",

+             "extra_source_repos": [

+                 {

+                     "name": "optional",

+                     "baseurl": "http://example.com/repo/x86_64/optional",

+                     "exclude": "systemd-container",

+                     "gpgcheck": False

+                 },

+                 {

+                     "name": "extra",

+                     "baseurl": "http://example.com/repo/x86_64/extra",

+                 }

+             ]

+         }

+         helpers.touch(extra_config_file, json.dumps(extra_config))

+ 

+         ostree.main([

+             'tree',

+             '--repo=%s' % repo,

+             '--log-dir=%s' % os.path.join(self.topdir, 'logs', 'Atomic'),

+             '--treefile=%s' % treefile,

+             '--extra-config=%s' % extra_config_file,

+         ])

+ 

+         source_repo_from_name = "source_repo_from-%s" % timestamp

+         source_repo_from_repo = os.path.join(configdir, "%s.repo" % source_repo_from_name)

+         self.assertTrue(os.path.isfile(source_repo_from_repo))

+         with open(source_repo_from_repo, 'r') as f:

+             content = f.read()

+             self.assertIn("[%s]" % source_repo_from_name, content)

+             self.assertIn("name=%s" % source_repo_from_name, content)

+             self.assertIn("baseurl=http://www.example.com/Server.repo", content)

+             self.assertIn("gpgcheck=0", content)

+ 

+         optional_repo_name = "optional-%s" % timestamp

+         optional_repo = os.path.join(configdir, "%s.repo" % optional_repo_name)

+         self.assertTrue(os.path.isfile(optional_repo))

+         with open(optional_repo, 'r') as f:

+             content = f.read()

+             self.assertIn("[%s]" % optional_repo_name, content)

+             self.assertIn("name=%s" % optional_repo_name, content)

+             self.assertIn("baseurl=http://example.com/repo/x86_64/optional", content)

+             self.assertIn("gpgcheck=0", content)

+ 

+         extra_repo_name = "extra-%s" % timestamp

+         extra_repo = os.path.join(configdir, "%s.repo" % extra_repo_name)

+         self.assertTrue(os.path.isfile(extra_repo))

+         with open(extra_repo, 'r') as f:

+             content = f.read()

+             self.assertIn("[%s]" % extra_repo_name, content)

+             self.assertIn("name=%s" % extra_repo_name, content)

+             self.assertIn("baseurl=http://example.com/repo/x86_64/extra", content)

+             self.assertIn("gpgcheck=0", content)

+ 

+         treeconf = json.load(open(treefile, 'r'))

+         repos = treeconf['repos']

+         self.assertEqual(len(repos), 3)

+         for name in [source_repo_from_name, optional_repo_name, extra_repo_name]:

+             self.assertIn(name, repos)

+ 

+     @mock.patch('pungi.ostree.utils.datetime')

+     @mock.patch('kobo.shortcuts.run')

+     def test_extra_config_with_keep_original_sources(self, run, time):

+         time.datetime.now.return_value = datetime.datetime(2016, 1, 1, 1, 1)

+         timestamp = time.datetime.now().strftime("%Y%m%d%H%M%S")

+ 

+         configdir = os.path.join(self.topdir, 'config')

+         self._make_dummy_config_dir(configdir)

+         treefile = os.path.join(configdir, 'fedora-atomic-docker-host.json')

+ 

+         repo = os.path.join(self.topdir, 'atomic')

+ 

+         extra_config_file = os.path.join(self.topdir, 'extra_config.json')

+         extra_config = {

+             "source_repo_from": "http://www.example.com/Server.repo",

+             "extra_source_repos": [

+                 {

+                     "name": "optional",

+                     "baseurl": "http://example.com/repo/x86_64/optional",

+                     "exclude": "systemd-container",

+                     "gpgcheck": False

+                 },

+                 {

+                     "name": "extra",

+                     "baseurl": "http://example.com/repo/x86_64/extra",

+                 }

+             ],

+             "keep_original_sources": True

+         }

+         helpers.touch(extra_config_file, json.dumps(extra_config))

+ 

+         ostree.main([

+             'tree',

+             '--repo=%s' % repo,

+             '--log-dir=%s' % os.path.join(self.topdir, 'logs', 'Atomic'),

+             '--treefile=%s' % treefile,

+             '--extra-config=%s' % extra_config_file,

+         ])

+ 

+         source_repo_from_name = "source_repo_from-%s" % timestamp

+         optional_repo_name = "optional-%s" % timestamp

+         extra_repo_name = "extra-%s" % timestamp

+ 

+         treeconf = json.load(open(treefile, 'r'))

+         repos = treeconf['repos']

+         self.assertEqual(len(repos), 6)

+         for name in ['fedora-rawhide', 'fedora-24', 'fedora-23',

+                      source_repo_from_name, optional_repo_name, extra_repo_name]:

+             self.assertIn(name, repos)

+ 

+ 

+ class OstreeInstallerScriptTest(helpers.PungiTestCase):

+     def setUp(self):

+         super(OstreeInstallerScriptTest, self).setUp()

+         self.product = "dummyproduct"

+         self.version = "1.0"

+         self.release = "20160101.t.0"

+         self.output = os.path.join(self.topdir, 'output')

+         self.logdir = os.path.join(self.topdir, 'logs')

+         self.volid = '%s-%s' % (self.product, self.version)

+         self.variant = 'dummy'

+         self.rootfs_size = None

+ 

+     @mock.patch('kobo.shortcuts.run')

+     def test_run_with_args(self, run):

+         args = ['installer',

+                 '--product=%s' % self.product,

+                 '--version=%s' % self.version,

+                 '--release=%s' % self.release,

+                 '--output=%s' % self.output,

+                 '--variant=%s' % self.variant,

+                 '--rootfs-size=%s' % self.rootfs_size,

+                 '--nomacboot',

+                 '--isfinal']

+         args.append('--source=%s' % 'http://www.example.com/dummy/repo')

+         args.append('--installpkgs=dummy-foo')

+         args.append('--installpkgs=dummy-bar')

+         args.append('--add-template=/path/to/lorax.tmpl')

+         args.append('--add-template-var=ostree_osname=dummy')

+         args.append('--add-arch-template=/path/to/lorax-embed.tmpl')

+         args.append('--add-arch-template-var=ostree_repo=http://www.example.com/ostree')

+         ostree.main(args)

+         self.maxDiff = None

+         self.assertItemsEqual(run.mock_calls,

+                               [mock.call(['lorax',

+                                           '--product=dummyproduct',

+                                           '--version=1.0',

+                                           '--release=20160101.t.0',

+                                           '--source=http://www.example.com/dummy/repo',

+                                           '--variant=dummy',

+                                           '--nomacboot',

+                                           '--isfinal',

+                                           '--installpkgs=dummy-foo',

+                                           '--installpkgs=dummy-bar',

+                                           '--add-template=/path/to/lorax.tmpl',

+                                           '--add-arch-template=/path/to/lorax-embed.tmpl',

+                                           '--add-template-var=ostree_osname=dummy',

+                                           '--add-arch-template-var=ostree_repo=http://www.example.com/ostree',

+                                           '--rootfs-size=None',

+                                           self.output])])

+ 

+     @mock.patch('kobo.shortcuts.run')

+     def test_run_with_extra_config_file(self, run):

+         extra_config_file = os.path.join(self.topdir, 'extra_config.json')

+         helpers.touch(extra_config_file,

+                       json.dumps({'source_repo_from': 'http://www.example.com/another/repo',

+                                   'installpkgs': ['dummy-foo', 'dummy-bar'],

+                                   'add_template': ['/path/to/lorax.tmpl'],

+                                   'add_template_var': ['ostree_osname=dummy-atomic',

+                                                        'ostree_ref=dummy/x86_64/docker'],

+                                   'add_arch_template': ['/path/to/lorax-embed.tmpl'],

+                                   'add_arch_template_var': ['ostree_osname=dummy-atomic',

+                                                             'ostree_repo=http://www.example.com/ostree']}))

+         args = ['installer',

+                 '--product=%s' % self.product,

+                 '--version=%s' % self.version,

+                 '--release=%s' % self.release,

+                 '--output=%s' % self.output,

+                 '--variant=%s' % self.variant,

+                 '--rootfs-size=%s' % self.rootfs_size,

+                 '--nomacboot',

+                 '--isfinal']

+         args.append('--source=%s' % 'http://www.example.com/dummy/repo')

+         args.append('--extra-config=%s' % extra_config_file)

+         ostree.main(args)

+         self.maxDiff = None

+         self.assertItemsEqual(run.mock_calls,

+                               [mock.call(['lorax',

+                                           '--product=dummyproduct',

+                                           '--version=1.0',

+                                           '--release=20160101.t.0',

+                                           '--source=http://www.example.com/dummy/repo',

+                                           '--source=http://www.example.com/another/repo',

+                                           '--variant=dummy',

+                                           '--nomacboot',

+                                           '--isfinal',

+                                           '--installpkgs=dummy-foo',

+                                           '--installpkgs=dummy-bar',

+                                           '--add-template=/path/to/lorax.tmpl',

+                                           '--add-arch-template=/path/to/lorax-embed.tmpl',

+                                           '--add-template-var=ostree_osname=dummy-atomic',

+                                           '--add-template-var=ostree_ref=dummy/x86_64/docker',

+                                           '--add-arch-template-var=ostree_osname=dummy-atomic',

+                                           '--add-arch-template-var=ostree_repo=http://www.example.com/ostree',

+                                           '--rootfs-size=None',

+                                           self.output])])

+ 

  if __name__ == '__main__':

      unittest.main()

Update pungi-make-ostree script to support sub commands:
tree : compose OSTree repository
installer: build OSTree installer image

This is pretty much pungi.util.makedirs. Maybe merge those two functions? (Although that might make it more complicated to split the ostree commands into a subpackage.) What do you think?

The code changes look reasonable to me.

I have started a test compose in stage. Let's see how it goes.

rebased

7 years ago

rebased

7 years ago

updated to use pungi.util.makedirs instead of pungi.ostree.utils.ensure_dir.

The test compose failed for unrelated reasons. Guess who forgot a kerberos ticket… I'll post a comment when I get some usable results.

Pull-Request has been merged by lsedlar

7 years ago