Repository: cekit/cekit

Issue:
Add support for rhpkg-stage
We currently have support only for `rhpkg` command. We need to support `rhpkg-stage` too. This will help us using the stage environment to test cekit. This should be done probably using a switch, maybe `--build-osbs-stage`?

Discussion:

Patch:
diff --git a/cekit/builders/docker.py b/cekit/builders/docker.py
index 12abd71..25fbd22 100644
--- a/cekit/builders/docker.py
+++ b/cekit/builders/docker.py
@@ -13,6 +13,7 @@ class DockerBuilder(Builder):
     """This class wraps docker build command to build and image"""
 
     def __init__(self, build_engine, target, params={}):
+        self._tags = params.get('tags')
         super(DockerBuilder, self).__init__(build_engine, target, params)
 
     def check_prerequisities(self):
@@ -20,20 +21,15 @@ class DockerBuilder(Builder):
             subprocess.check_output(['docker', 'info'], stderr=subprocess.STDOUT)
         except subprocess.CalledProcessError as ex:
             raise CekitError("Docker build engine needs docker installed and configured, error: %s"
-                                 % ex.output)
+                             % ex.output)
         except Exception as ex:
             raise CekitError("Docker build engine needs docker installed and configured!", ex)
 
-    def build(self, build_args):
+    def build(self):
         """After the source siles are generated, the container image can be built.
         We're using Docker to build the image currently.
-
-        This can be changed by specifying the tags in CLI using --build-tags option.
-
-        Args:
-          build_tags - a list of image tags
         """
-        tags = build_args.tags
+        tags = self._tags
         cmd = ["docker", "build"]
 
         # Custom tags for the container image
diff --git a/cekit/builders/osbs.py b/cekit/builders/osbs.py
index c517029..993c171 100644
--- a/cekit/builders/osbs.py
+++ b/cekit/builders/osbs.py
@@ -16,20 +16,28 @@ class OSBSBuilder(Builder):
     """Class representing OSBS builder."""
 
     def __init__(self, build_engine, target, params={}):
+        self._user = params.get('user')
+        self._nowait = params.get('nowait', False)
+        self._release = params.get('release', False)
+
+        self._stage = params.get('stage', False)
+        if params.get('stage'):
+            self._rhpkg = 'rhpkg-stage'
+        else:
+            self._rhpkg = 'rhpkg'
+
         super(OSBSBuilder, self).__init__(build_engine, target, params={})
-        self.user = params.get('user')
-        self.nowait = params.get('nowait')
 
     def check_prerequisities(self):
         try:
             subprocess.check_output(
-                ['rhpkg', 'help'], stderr=subprocess.STDOUT)
+                [self._rhpkg, 'help'], stderr=subprocess.STDOUT)
         except subprocess.CalledProcessError as ex:
             raise CekitError("OSBS build engine needs 'rhpkg' tools installed, error: %s"
-                                 % ex.output)
+                             % ex.output)
         except Exception as ex:
             raise CekitError(
-                "OSBS build engine needs 'rhpkg' tools installed!", ex)
+                "OSBS build engine needs '%s' tools installed!" % self._rhpkg, ex)
 
     def prepare(self, descriptor):
         """Prepares dist-git repository for OSBS build."""
@@ -42,8 +50,13 @@ class OSBSBuilder(Builder):
             raise CekitError(
                 "OSBS builder needs repostiory and branch provided!")
 
+        if self._stage:
+            osbs_dir = 'osbs-stage'
+        else:
+            osbs_dir = 'osbs'
+
         self.dist_git_dir = os.path.join(os.path.expanduser(tools.cfg['common']['work_dir']),
-                                         'osbs',
+                                         osbs_dir,
                                          repository)
         if not os.path.exists(os.path.dirname(self.dist_git_dir)):
             os.makedirs(os.path.dirname(self.dist_git_dir))
@@ -53,7 +66,7 @@ class OSBSBuilder(Builder):
                                 repository,
                                 branch)
 
-        self.dist_git.prepare(self.user)
+        self.dist_git.prepare(self._stage, self._user)
         self.dist_git.clean()
 
         self.artifacts = [a['name'] for a in descriptor.get('artifacts', [])]
@@ -80,9 +93,9 @@ class OSBSBuilder(Builder):
         logger.info("Updating lookaside cache...")
         if not self.artifacts:
             return
-        cmd = ["rhpkg"]
-        if self.user:
-            cmd += ['--user', self.user]
+        cmd = [self._rhpkg]
+        if self._user:
+            cmd += ['--user', self._user]
         cmd += ["new-sources"] + self.artifacts
 
         logger.debug("Executing '%s'" % cmd)
@@ -95,15 +108,15 @@ class OSBSBuilder(Builder):
 
         logger.info("Update finished.")
 
-    def build(self, build_args):
-        cmd = ["rhpkg"]
-        if self.user:
-            cmd += ['--user', self.user]
+    def build(self):
+        cmd = [self._rhpkg]
+        if self._user:
+            cmd += ['--user', self._user]
         cmd.append("container-build")
-        if self.nowait:
+        if self._nowait:
             cmd += ['--nowait']
 
-        if not build_args.build_osbs_release:
+        if not self._release:
             cmd.append("--scratch")
 
         with Chdir(self.dist_git_dir):
@@ -117,7 +130,7 @@ class OSBSBuilder(Builder):
                 logger.info("No changes made to the code, committing skipped")
 
             if decision("Do you want to build the image in OSBS?"):
-                build_type = "release" if build_args.build_osbs_release else "scratch"
+                build_type = "release" if self._release else "scratch"
                 logger.info("Executing %s container build in OSBS..." % build_type)
 
                 logger.debug("Executing '%s'." % ' '.join(cmd))
@@ -163,7 +176,7 @@ class DistGit(object):
 
         return False
 
-    def prepare(self, user=None):
+    def prepare(self, stage, user=None):
         if os.path.exists(self.output):
             with Chdir(self.output):
                 logger.info("Pulling latest changes in repo %s..." % self.repo)
@@ -177,11 +190,15 @@ class DistGit(object):
             logger.info("Cloning %s git repository (%s branch)..." %
                         (self.repo, self.branch))
 
-            cmd = ['rhpkg']
+            if stage:
+                cmd = ['rhpkg-stage']
+            else:
+                cmd = ['rhpkg']
+
             if user:
                 cmd += ['--user', user]
             cmd += ["-q", "clone", "-b", self.branch, self.repo, self.output]
-
+            logger.debug("Cloning: '%s'" % ' '.join(cmd))
             subprocess.check_output(cmd)
             logger.debug("Repository %s cloned" % self.repo)
 
diff --git a/cekit/cli.py b/cekit/cli.py
index 8b828f4..ce64beb 100644
--- a/cekit/cli.py
+++ b/cekit/cli.py
@@ -90,6 +90,11 @@ class Cekit(object):
                                  action='store_true',
                                  help='run rhpkg container build with --nowait option')
 
+        build_group.add_argument('--build-osbs-stage',
+                                 dest='build_osbs_stage',
+                                 action='store_true',
+                                 help='use rhpkg-stage instead of rhpkg')
+
         build_group.add_argument('--build-tech-preview',
                                  action='store_true',
                                  help='perform tech preview build')
@@ -172,12 +177,17 @@ class Cekit(object):
 
             if 'build' in self.args.commands:
                 params = {'user': self.args.build_osbs_user,
-                          'nowait': self.args.build_osbs_nowait}
+                          'nowait': self.args.build_osbs_nowait,
+                          'stage': self.args.build_osbs_stage,
+                          'release': self.args.build_osbs_release,
+                          'tags': self.args.tags,
+                          }
+
                 builder = Builder(self.args.build_engine,
                                   self.args.target,
                                   params)
                 builder.prepare(generator.image)
-                builder.build(self.args)
+                builder.build()
 
             if 'test' in self.args.commands:
 
diff --git a/docs/build.rst b/docs/build.rst
index 809bf4d..9503ece 100644
--- a/docs/build.rst
+++ b/docs/build.rst
@@ -19,6 +19,7 @@ You can execute an container image build by running:
 
 * ``--tag`` -- an image tag used to build image (can be specified multiple times)
 * ``--build-engine`` -- a builder engine to use ``osbs`` or ``docker`` [#f1]_
+* ``--build-osbs-stage`` -- use ``rhpkg-stage`` tool instead of ``rhpkg``
 * ``--build-osbs-release`` [#f2]_ -- perform a OSBS release build
 * ``--build-osbs-user`` -- alternative user passed to `rhpkg --user`
 * ``--build-osbs-nowait`` -- run `rhpkg container-build` with `--nowait` option specified


Test patch:
diff --git a/tests/test_builder.py b/tests/test_builder.py
new file mode 100644
index 0000000..c341b06
--- /dev/null
+++ b/tests/test_builder.py
@@ -0,0 +1,118 @@
+import subprocess
+
+from cekit.builder import Builder
+
+
+def test_osbs_builder_defaults(mocker):
+    mocker.patch.object(subprocess, 'check_output')
+
+    builder = Builder('osbs', 'tmp', {})
+
+    assert builder._release is False
+    assert builder._rhpkg == 'rhpkg'
+    assert builder._nowait is False
+
+
+def test_osbs_builder_use_rhpkg_staget(mocker):
+    mocker.patch.object(subprocess, 'check_output')
+
+    params = {'stage': True}
+    builder = Builder('osbs', 'tmp', params)
+
+    assert builder._rhpkg == 'rhpkg-stage'
+
+
+def test_osbs_builder_nowait(mocker):
+    mocker.patch.object(subprocess, 'check_output')
+
+    params = {'nowait': True}
+    builder = Builder('osbs', 'tmp', params)
+
+    assert builder._nowait is True
+
+
+def test_osbs_builder_user(mocker):
+    mocker.patch.object(subprocess, 'check_output')
+
+    params = {'user': 'UserFoo'}
+    builder = Builder('osbs', 'tmp', params)
+
+    assert builder._user == 'UserFoo'
+
+
+class DistGitMock(object):
+    def add(self):
+        pass
+
+    def stage_modified(self):
+        pass
+
+
+def create_osbs_build_object(mocker, builder_type, params):
+    mocker.patch.object(subprocess, 'check_output')
+    mocker.patch('cekit.builders.osbs.decision')
+
+    builder = Builder(builder_type, 'tmp', params)
+    builder.dist_git_dir = '/tmp'
+    builder.dist_git = DistGitMock()
+    builder.artifacts = []
+    return builder
+
+
+def test_osbs_builder_run_rhpkg_stage(mocker):
+    mocker.patch.object(subprocess, 'check_output')
+
+    params = {'stage': True}
+
+    check_call = mocker.patch.object(subprocess, 'check_call')
+    builder = create_osbs_build_object(mocker, 'osbs', params)
+    builder.build()
+
+    check_call.assert_called_once_with(['rhpkg-stage', 'container-build', '--scratch'])
+
+
+def test_osbs_builder_run_rhpkg(mocker):
+    mocker.patch.object(subprocess, 'check_output')
+
+    check_call = mocker.patch.object(subprocess, 'check_call')
+    builder = create_osbs_build_object(mocker, 'osbs', {})
+    builder.build()
+
+    check_call.assert_called_once_with(['rhpkg', 'container-build', '--scratch'])
+
+
+def test_osbs_builder_run_rhpkg_nowait(mocker):
+    mocker.patch.object(subprocess, 'check_output')
+    params = {'nowait': True}
+
+    check_call = mocker.patch.object(subprocess, 'check_call')
+    builder = create_osbs_build_object(mocker, 'osbs', params)
+    builder.build()
+
+    check_call.assert_called_once_with(['rhpkg', 'container-build', '--nowait', '--scratch'])
+
+
+def test_osbs_builder_run_rhpkg_user(mocker):
+    params = {'user': 'Foo'}
+
+    check_call = mocker.patch.object(subprocess, 'check_call')
+    builder = create_osbs_build_object(mocker, 'osbs', params)
+    builder.build()
+
+    check_call.assert_called_once_with(['rhpkg', '--user', 'Foo', 'container-build', '--scratch'])
+
+
+def test_docker_builder_defaults():
+    params = {'tags': ['foo', 'bar']}
+    builder = Builder('docker', 'tmp', params)
+
+    assert builder._tags == ['foo', 'bar']
+
+
+def test_docker_builder_run(mocker):
+    params = {'tags': ['foo', 'bar']}
+    check_call = mocker.patch.object(subprocess, 'check_call')
+    builder = create_osbs_build_object(mocker, 'docker', params)
+    builder.build()
+
+    check_call.assert_called_once_with(['docker', 'build', '-t', 'foo', '-t', 'bar', 'tmp/image'])
diff --git a/tests/test_unit_args.py b/tests/test_unit_args.py
index daf0cd1..e41516a 100644
--- a/tests/test_unit_args.py
+++ b/tests/test_unit_args.py
@@ -38,6 +38,18 @@ def test_args_build_engine(mocker, engine):
     assert Cekit().parse().args.build_engine == engine
 
 
+def test_args_osbs_stage(mocker):
+    mocker.patch.object(sys, 'argv', ['cekit', 'build', '--build-osbs-stage'])
+
+    assert Cekit().parse().args.build_osbs_stage is True
+
+
+def test_args_osbs_stage_false(mocker):
+    mocker.patch.object(sys, 'argv', ['cekit', 'build'])
+
+    assert Cekit().parse().args.build_osbs_stage is False
+
+
 def test_args_invalid_build_engine(mocker):
     mocker.patch.object(sys, 'argv', ['cekit', 'build', '--build-engine', 'rkt'])